diff --git a/akka-actor-tests/src/test/scala/akka/pattern/BackoffOnRestartSupervisorSpec.scala b/akka-actor-tests/src/test/scala/akka/pattern/BackoffOnRestartSupervisorSpec.scala index 3396ca34ce..7429bd057a 100644 --- a/akka-actor-tests/src/test/scala/akka/pattern/BackoffOnRestartSupervisorSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/pattern/BackoffOnRestartSupervisorSpec.scala @@ -1,13 +1,13 @@ /** - * Copyright (C) 2015-2016 Lightbend Inc. - */ + * Copyright (C) 2015-2016 Lightbend Inc. + */ package akka.pattern -import java.util.concurrent.{TimeUnit, CountDownLatch} +import java.util.concurrent.{ TimeUnit, CountDownLatch } import akka.pattern.TestActor.NormalException -import akka.testkit.{ImplicitSender, AkkaSpec, TestProbe, filterException} +import akka.testkit.{ ImplicitSender, AkkaSpec, TestProbe, filterException } import scala.concurrent.duration._ import akka.actor._ import scala.language.postfixOps @@ -28,11 +28,11 @@ class TestActor(probe: ActorRef) extends Actor { probe ! "STARTED" def receive = { - case "DIE" ⇒ context.stop(self) - case "THROW" ⇒ throw new TestActor.NormalException + case "DIE" ⇒ context.stop(self) + case "THROW" ⇒ throw new TestActor.NormalException case "THROW_STOPPING_EXCEPTION" ⇒ throw new TestActor.StoppingException - case ("TO_PARENT", msg) ⇒ context.parent ! msg - case other ⇒ probe ! other + case ("TO_PARENT", msg) ⇒ context.parent ! msg + case other ⇒ probe ! other } } diff --git a/akka-bench-jmh/src/main/scala/akka/http/HttpBenchmark.scala b/akka-bench-jmh/src/main/scala/akka/http/HttpBenchmark.scala deleted file mode 100644 index 706c9c49ed..0000000000 --- a/akka-bench-jmh/src/main/scala/akka/http/HttpBenchmark.scala +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright (C) 2015-2016 Lightbend Inc. - */ - -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) - } -} diff --git a/akka-bench-jmh/src/main/scala/akka/http/HttpBlueprintBenchmark.scala b/akka-bench-jmh/src/main/scala/akka/http/HttpBlueprintBenchmark.scala deleted file mode 100644 index d9925b8a82..0000000000 --- a/akka-bench-jmh/src/main/scala/akka/http/HttpBlueprintBenchmark.scala +++ /dev/null @@ -1,128 +0,0 @@ -/** - * Copyright (C) 2015-2016 Lightbend Inc. - */ - -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() - } - -} - diff --git a/akka-bench-jmh/src/main/scala/akka/http/HttpMessageMatchingBenchmark.scala b/akka-bench-jmh/src/main/scala/akka/http/HttpMessageMatchingBenchmark.scala deleted file mode 100644 index a1a6e94619..0000000000 --- a/akka-bench-jmh/src/main/scala/akka/http/HttpMessageMatchingBenchmark.scala +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (C) 2015-2016 Lightbend Inc. - */ - -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 - } - } - -} \ No newline at end of file diff --git a/akka-bench-jmh/src/main/scala/akka/http/HttpRequestParsingBenchmark.scala b/akka-bench-jmh/src/main/scala/akka/http/HttpRequestParsingBenchmark.scala deleted file mode 100644 index 4233a5115b..0000000000 --- a/akka-bench-jmh/src/main/scala/akka/http/HttpRequestParsingBenchmark.scala +++ /dev/null @@ -1,102 +0,0 @@ -/** - * Copyright (C) 2015-2016 Lightbend Inc. - */ -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) - } -} diff --git a/akka-bench-jmh/src/main/scala/akka/http/HttpResponseRenderingBenchmark.scala b/akka-bench-jmh/src/main/scala/akka/http/HttpResponseRenderingBenchmark.scala deleted file mode 100644 index 8e5aa5c10a..0000000000 --- a/akka-bench-jmh/src/main/scala/akka/http/HttpResponseRenderingBenchmark.scala +++ /dev/null @@ -1,250 +0,0 @@ -/** - * Copyright (C) 2015-2016 Lightbend Inc. - */ - -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 - -} - diff --git a/akka-docs/rst/java/code/docs/http/javadsl/HttpClientExampleDocTest.java b/akka-docs/rst/java/code/docs/http/javadsl/HttpClientExampleDocTest.java deleted file mode 100644 index 5377fa75e8..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/HttpClientExampleDocTest.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -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 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 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 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 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> connectionFlow = - Http.get(system).outgoingConnection(toHost("akka.io", 80)); - final CompletionStage responseFuture = - Source.single(HttpRequest.create("/")) - .via(connectionFlow) - .runWith(Sink.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, - Pair, Integer>, - HostConnectionPool> poolClientFlow = - Http.get(system).cachedHostConnectionPool(toHost("akka.io", 80), materializer); - - // construct a pool client flow with context type `Integer` - - final CompletionStage, Integer>> responseFuture = - Source - .single(Pair.create(HttpRequest.create("/"), 42)) - .via(poolClientFlow) - .runWith(Sink., 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 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 fetch(String url) { - return http.singleRequest(HttpRequest.create(url), materializer); - } - } - //#single-request-in-actor-example - -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/HttpsExamplesDocTest.java b/akka-docs/rst/java/code/docs/http/javadsl/HttpsExamplesDocTest.java deleted file mode 100644 index 8883594b1d..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/HttpsExamplesDocTest.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -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 - } - -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/ModelDocTest.java b/akka-docs/rst/java/code/docs/http/javadsl/ModelDocTest.java deleted file mode 100644 index 96321a899c..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/ModelDocTest.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -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 getCredentialsOfRequest(HttpRequest request) { - Optional 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 -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/WebSocketClientExampleTest.java b/akka-docs/rst/java/code/docs/http/javadsl/WebSocketClientExampleTest.java deleted file mode 100644 index 0fd90fddc4..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/WebSocketClientExampleTest.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright (C) 2016 Lightbend Inc. - */ -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> printSink = - Sink.foreach((message) -> - System.out.println("Got message: " + message.asTextMessage().getStrictText()) - ); - - // send this as a message over the WebSocket - final Source helloSource = - Source.single(TextMessage.create("hello world")); - - // the CompletionStage is the materialized value of Sink.foreach - // and it is completed when the stream completes - final Flow> flow = - Flow.fromSinkAndSourceMat(printSink, helloSource, Keep.left()); - - final Pair, CompletionStage> pair = - http.singleWebSocketRequest( - WebSocketRequest.create("ws://echo.websocket.org"), - flow, - materializer - ); - - // The first value in the pair is a CompletionStage that - // completes when the WebSocket request has connected successfully (or failed) - final CompletionStage 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 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 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>> flow = - Flow.fromSinkAndSourceMat( - Sink.foreach(System.out::println), - Source.maybe(), - Keep.right()); - - final Pair, CompletableFuture>> 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>> source = - Source.from(Arrays.asList(TextMessage.create("one"), TextMessage.create("two"))) - .concatMat(Source.maybe(), Keep.right()); - - final Flow>> flow = - Flow.fromSinkAndSourceMat( - Sink.foreach(System.out::println), - source, - Keep.right()); - - final Pair, CompletableFuture>> 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 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> printSink = - Sink.foreach((message) -> - System.out.println("Got message: " + message.asTextMessage().getStrictText()) - ); - - // send this as a message over the WebSocket - Source helloSource = - Source.single(TextMessage.create("hello world")); - - - Flow> webSocketFlow = - http.webSocketClientFlow(WebSocketRequest.create("ws://echo.websocket.org")); - - - Pair, CompletionStage> pair = - helloSource.viaMat(webSocketFlow, Keep.right()) - .toMat(printSink, Keep.both()) - .run(materializer); - - - // The first value in the pair is a CompletionStage that - // completes when the WebSocket request has connected successfully (or failed) - CompletionStage upgradeCompletion = pair.first(); - - // the second value is the completion of the sink from above - // in other words, it completes when the WebSocket disconnects - CompletionStage closed = pair.second(); - - CompletionStage 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 - } - - - - } diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/FormFieldRequestValsExampleTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/FormFieldRequestValsExampleTest.java deleted file mode 100644 index 546579d1ad..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/FormFieldRequestValsExampleTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -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 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; - } - } - - -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/HeaderRequestValsExampleTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/HeaderRequestValsExampleTest.java deleted file mode 100644 index 873184445c..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/HeaderRequestValsExampleTest.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -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 - } -} \ No newline at end of file diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/HighLevelServerBindFailureExample.java b/akka-docs/rst/java/code/docs/http/javadsl/server/HighLevelServerBindFailureExample.java deleted file mode 100644 index e58bcd9535..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/HighLevelServerBindFailureExample.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -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 handler = route.flow(system, materializer); - final CompletionStage 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 diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/HighLevelServerExample.java b/akka-docs/rst/java/code/docs/http/javadsl/server/HighLevelServerExample.java deleted file mode 100644 index 3537964e61..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/HighLevelServerExample.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -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 routeFlow = app.createRoute().flow(system, materializer); - final CompletionStage 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, "Hello world!")) - ), - path("ping", () -> - // return a simple `text/plain` response - complete("PONG!") - ), - path("hello", () -> - // uses the route defined above - helloRoute - ) - )); - } -} -//#high-level-server-example diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/HttpBasicAuthenticatorExample.java b/akka-docs/rst/java/code/docs/http/javadsl/server/HttpBasicAuthenticatorExample.java deleted file mode 100644 index 4d0c8eebe9..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/HttpBasicAuthenticatorExample.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ -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 authenticate(Optional 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 - } - - -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/HttpServerExampleDocTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/HttpServerExampleDocTest.java deleted file mode 100644 index 1d6e7c9078..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/HttpServerExampleDocTest.java +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -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> serverSource = - Http.get(system).bind(ConnectHttp.toHost("localhost", 8080), materializer); - - CompletionStage 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> serverSource = - Http.get(system).bind(ConnectHttp.toHost("localhost", 80), materializer); - - CompletionStage 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> serverSource = - Http.get(system).bind(ConnectHttp.toHost("localhost", 8080), materializer); - - Flow 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 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> serverSource = - Http.get(system).bind(ConnectHttp.toHost("localhost", 8080), materializer); - - Flow 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 httpEcho = - Flow.of(HttpRequest.class) - .via(failureDetection) - .map(request -> { - Source bytes = request.entity().getDataBytes(); - HttpEntity.Chunked entity = HttpEntities.create(ContentTypes.TEXT_PLAIN_UTF8, bytes); - - return HttpResponse.create() - .withEntity(entity); - }); - - CompletionStage 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> serverSource = - Http.get(system).bind(ConnectHttp.toHost("localhost", 8080), materializer); - - //#request-handler - final Function requestHandler = - new Function() { - private final HttpResponse NOT_FOUND = - HttpResponse.create() - .withStatus(404) - .withEntity("Unknown resource!"); - - - @Override - public HttpResponse apply(HttpRequest request) throws Exception { - Uri uri = request.getUri(); - if (request.method() == HttpMethods.GET) { - if (uri.path().equals("/")) { - return - HttpResponse.create() - .withEntity(ContentTypes.TEXT_HTML_UTF8, - "Hello world!"); - } 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 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 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 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 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 - } - - -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/HttpsServerExampleTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/HttpsServerExampleTest.java deleted file mode 100644 index 9499b86cbe..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/HttpsServerExampleTest.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -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); - //# - } -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/JsonStreamingExamplesTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/JsonStreamingExamplesTest.java deleted file mode 100644 index c43d6241e5..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/JsonStreamingExamplesTest.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (C) 2016 Lightbend Inc. - */ - -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 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 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 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 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 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 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 -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/OAuth2AuthenticatorExample.java b/akka-docs/rst/java/code/docs/http/javadsl/server/OAuth2AuthenticatorExample.java deleted file mode 100644 index 5f2b7da862..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/OAuth2AuthenticatorExample.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ -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 authenticate(Optional 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 - } - - -} \ No newline at end of file diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/PathDirectiveExampleTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/PathDirectiveExampleTest.java deleted file mode 100644 index 26461988e2..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/PathDirectiveExampleTest.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -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/" - path(PathMatchers.segment("admin") - .slash("user") - .slash(PathMatchers.integerSegment()), userId -> { - return complete("Hello user " + userId); - } - ); - - // matches "/admin/user/", 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/" 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 - } -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/WebSocketCoreExample.java b/akka-docs/rst/java/code/docs/http/javadsl/server/WebSocketCoreExample.java deleted file mode 100644 index 89c985a866..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/WebSocketCoreExample.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -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 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 handler = request -> handleRequest(request); - CompletionStage 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 greeter() { - return - Flow.create() - .collect(new JavaPartialFunction() { - @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 diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/WebSocketRoutingExample.java b/akka-docs/rst/java/code/docs/http/javadsl/server/WebSocketRoutingExample.java deleted file mode 100644 index c40facb357..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/WebSocketRoutingExample.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -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 greeter() { - return - Flow.create() - .collect(new JavaPartialFunction() { - @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())); - } - } -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java deleted file mode 100644 index 0b93560be3..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java +++ /dev/null @@ -1,893 +0,0 @@ -/* - * Copyright (C) 2016-2016 Lightbend Inc. - */ -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 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 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 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> neverAuth = - creds -> Optional.empty(); - final Function, Optional> 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, 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> neverAuth = - creds -> Optional.empty(); - - final Route originalRoute = pathPrefix("auth", () -> - path("never", () -> - authenticateBasic("my-realm", neverAuth, obj -> complete("Welcome to the bat-cave!")) - ) - ); - - final Function, 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 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() - .match(Rejected.class, rejected -> { - final Iterable 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>() - .match(Rejected.class, rejected -> CompletableFuture.supplyAsync(() -> { - final Iterable 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 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 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 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 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 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 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 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 - } - -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/CodingDirectivesExamplesTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/directives/CodingDirectivesExamplesTest.java deleted file mode 100644 index 3ba307ca1f..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/CodingDirectivesExamplesTest.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (C) 2016-2016 Lightbend Inc. - */ -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 - } - -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/CookieDirectivesExamplesTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/directives/CookieDirectivesExamplesTest.java deleted file mode 100644 index cf38a1f12e..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/CookieDirectivesExamplesTest.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2016-2016 Lightbend Inc. - */ -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 - } - -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/CustomDirectivesExamplesTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/directives/CustomDirectivesExamplesTest.java deleted file mode 100644 index e2459752d4..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/CustomDirectivesExamplesTest.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2015-2016 Lightbend Inc. - */ -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 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> authenticate, MyRole requiredRole, Supplier inner) { - return headerValueByName("X-My-User-Id", (userId) -> { - return headerValueByName("X-My-User-Secret", (secret) -> { - Set 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> 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); - - } - -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/CustomHttpMethodExamplesTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/directives/CustomHttpMethodExamplesTest.java deleted file mode 100644 index c98e4ef675..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/CustomHttpMethodExamplesTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2015-2016 Lightbend Inc. - */ -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 handler = routes.flow(system, materializer); - final Http http = Http.get(system); - final CompletionStage 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 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() - ); - } -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/DebuggingDirectivesExamplesTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/directives/DebuggingDirectivesExamplesTest.java deleted file mode 100644 index 4011d3b0c5..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/DebuggingDirectivesExamplesTest.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (C) 2015-2016 Lightbend Inc. - */ - -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 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> 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, Optional> 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 showSuccessAsInfo = (response) -> - LogEntry.create(String.format("Response code '%d'", response.status().intValue()), - InfoLevel()); - - Function, 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> requestMethodAsInfo = - (request, response) -> { - Long requestTime = System.nanoTime(); - return printResponseTime(request, response, requestTime); - }; - - // handle rejections to optionally generate a log entry - BiFunction, Optional> 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 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 - } - } -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/ExecutionDirectivesExamplesTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/directives/ExecutionDirectivesExamplesTest.java deleted file mode 100644 index b8e1809732..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/ExecutionDirectivesExamplesTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2016-2016 Lightbend Inc. - */ -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 - } -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/FileAndResourceDirectivesExamplesTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/directives/FileAndResourceDirectivesExamplesTest.java deleted file mode 100644 index b9b0da0ebc..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/FileAndResourceDirectivesExamplesTest.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2016-2016 Lightbend Inc. - */ -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 - } -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/FileUploadDirectivesExamplesTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/directives/FileUploadDirectivesExamplesTest.java deleted file mode 100644 index 2af6f8362e..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/FileUploadDirectivesExamplesTest.java +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Copyright (C) 2016-2016 Lightbend Inc. - */ -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 infoFileRoute = - (info, file) -> { - // do something with the file and file metadata ... - file.delete(); - return complete(StatusCodes.OK); - }; - - - final Route route = uploadedFile("csv", infoFileRoute); - - Map 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) => Route to process the file contents - BiFunction, Route> processUploadedFile = - (metadata, byteSource) -> { - CompletionStage 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 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) => Route to process the file contents - BiFunction, Route> processUploadedFile = - (metadata, byteSource) -> { - CompletionStage 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 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"); - //# - } -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/FormFieldDirectivesExamplesTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/directives/FormFieldDirectivesExamplesTest.java deleted file mode 100644 index bb8b685150..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/FormFieldDirectivesExamplesTest.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2016-2016 Lightbend Inc. - */ -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, 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>, 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>, 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 - } -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/FutureDirectivesExamplesTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/directives/FutureDirectivesExamplesTest.java deleted file mode 100644 index 23a4d58fdf..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/FutureDirectivesExamplesTest.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2016-2016 Lightbend Inc. - */ - -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() - .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() - .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 - } - -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/HeaderDirectivesExamplesTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/directives/HeaderDirectivesExamplesTest.java deleted file mode 100644 index e274a0ef9f..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/HeaderDirectivesExamplesTest.java +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright (C) 2015-2016 Lightbend Inc. - */ -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> 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 extractHostPort = - new JavaPartialFunction() { - @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> 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 extractHostPort = - new JavaPartialFunction() { - @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 - } -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/HostDirectivesExamplesTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/directives/HostDirectivesExamplesTest.java deleted file mode 100644 index 03515d748c..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/HostDirectivesExamplesTest.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2015-2016 Lightbend Inc. - */ - -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 - } - -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/MethodDirectivesExamplesTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/directives/MethodDirectivesExamplesTest.java deleted file mode 100644 index 39cf1bbbe7..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/MethodDirectivesExamplesTest.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2015-2016 Lightbend Inc. - */ - -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 - } -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/MiscDirectivesExamplesTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/directives/MiscDirectivesExamplesTest.java deleted file mode 100644 index df44b9114b..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/MiscDirectivesExamplesTest.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2016-2016 Lightbend Inc. - */ -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 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 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 - } - -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/ParameterDirectivesExamplesTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/directives/ParameterDirectivesExamplesTest.java deleted file mode 100644 index 203a4a595b..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/ParameterDirectivesExamplesTest.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2016-2016 Lightbend Inc. - */ -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 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 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 - } - -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/PathDirectivesExamplesTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/directives/PathDirectivesExamplesTest.java deleted file mode 100644 index b8187f6ef0..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/PathDirectivesExamplesTest.java +++ /dev/null @@ -1,322 +0,0 @@ -/* - * Copyright (C) 2015-2016 Lightbend Inc. - */ - -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 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 " + - "this URI."); - - 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 " + - "this URI."); - - 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 - } - -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/RangeDirectivesExamplesTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/directives/RangeDirectivesExamplesTest.java deleted file mode 100644 index 5dfe1ed0cb..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/RangeDirectivesExamplesTest.java +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Copyright (C) 2016-2016 Lightbend Inc. - */ -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> completionStage = - response.entity(Unmarshaller.entityToMultipartByteRanges()).getParts() - .runFold(new ArrayList<>(), (acc, n) -> { - acc.add(n); - return acc; - }, materializer); - try { - final List 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 - } - //# - } -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/RespondWithDirectivesExamplesTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/directives/RespondWithDirectivesExamplesTest.java deleted file mode 100644 index 64dfe8e37a..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/RespondWithDirectivesExamplesTest.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -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 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 - } -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/RouteDirectivesExamplesTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/directives/RouteDirectivesExamplesTest.java deleted file mode 100644 index 7771245b79..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/RouteDirectivesExamplesTest.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2016-2016 Lightbend Inc. - */ -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 this URI."); - //#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 - } -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/SchemeDirectivesExamplesTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/directives/SchemeDirectivesExamplesTest.java deleted file mode 100644 index 8b59bdaae7..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/SchemeDirectivesExamplesTest.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2015-2016 Lightbend Inc. - */ - -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 - } - -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/SecurityDirectivesExamplesTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/directives/SecurityDirectivesExamplesTest.java deleted file mode 100644 index 68d7386bbe..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/SecurityDirectivesExamplesTest.java +++ /dev/null @@ -1,364 +0,0 @@ -/* - * Copyright (C) 2016-2016 Lightbend Inc. - */ -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> 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, String> myUserPassAuthenticator = - new JavaPartialFunction, String>() { - @Override - public String apply(Optional 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, CompletionStage> myUserPassAuthenticator = - new JavaPartialFunction,CompletionStage>() { - @Override - public CompletionStage apply(Optional 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, CompletionStage>> 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 auth = credentials -> true; - - final Function, CompletionStage>> 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> 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 admins = new HashSet<>(); - admins.add("Peter"); - final Function 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> 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 admins = new HashSet<>(); - admins.add("Peter"); - final Set synchronizedAdmins = Collections.synchronizedSet(admins); - - final Function> 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 - } -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/TimeoutDirectivesExamplesTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/directives/TimeoutDirectivesExamplesTest.java deleted file mode 100644 index b5aeb28d8f..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/TimeoutDirectivesExamplesTest.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (C) 2016-2016 Lightbend Inc. - */ - -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 shutdown(CompletionStage binding) { - return binding.thenAccept(b -> { - System.out.println(String.format("Unbinding from %s", b.localAddress())); - - final CompletionStage unbound = b.unbind(); - try { - unbound.toCompletableFuture().get(3, TimeUnit.SECONDS); // block... - } catch (TimeoutException | InterruptedException | ExecutionException e) { - throw new RuntimeException(e); - } - }); - } - - private Optional runRoute(ActorSystem system, ActorMaterializer materializer, Route route, String routePath) { - final Tuple3 inetaddrHostAndPort = TestUtils.temporaryServerHostnameAndPort("127.0.0.1"); - Tuple2 hostAndPort = new Tuple2<>( - inetaddrHostAndPort._2(), - (Integer) inetaddrHostAndPort._3() - ); - - final Flow routeFlow = route.flow(system, materializer); - final CompletionStage binding = http.bindAndHandle(routeFlow, ConnectHttp.toHost(hostAndPort._1(), hostAndPort._2()), materializer); - - final CompletionStage responseCompletionStage = http.singleRequest(HttpRequest.create("http://" + hostAndPort._1() + ":" + hostAndPort._2() + "/" + routePath), materializer); - - CompletableFuture responseFuture = responseCompletionStage.toCompletableFuture(); - - Optional 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 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 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 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 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)); - //# - } -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/WebSocketDirectivesExamplesTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/directives/WebSocketDirectivesExamplesTest.java deleted file mode 100644 index 14f15fa1ae..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/directives/WebSocketDirectivesExamplesTest.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2016-2016 Lightbend Inc. - */ -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 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 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 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 - } -} diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/testkit/MyAppService.java b/akka-docs/rst/java/code/docs/http/javadsl/server/testkit/MyAppService.java deleted file mode 100644 index bc9836fac8..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/testkit/MyAppService.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -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 diff --git a/akka-docs/rst/java/code/docs/http/javadsl/server/testkit/TestkitExampleTest.java b/akka-docs/rst/java/code/docs/http/javadsl/server/testkit/TestkitExampleTest.java deleted file mode 100644 index 214aabb691..0000000000 --- a/akka-docs/rst/java/code/docs/http/javadsl/server/testkit/TestkitExampleTest.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -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 \ No newline at end of file diff --git a/akka-docs/rst/java/http/client-side/client-https-support.rst b/akka-docs/rst/java/http/client-side/client-https-support.rst deleted file mode 100644 index 32d8773508..0000000000 --- a/akka-docs/rst/java/http/client-side/client-https-support.rst +++ /dev/null @@ -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 `. - -.. 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 TLS–for 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 diff --git a/akka-docs/rst/java/http/client-side/connection-level.rst b/akka-docs/rst/java/http/client-side/connection-level.rst deleted file mode 100644 index 0cc3438a26..0000000000 --- a/akka-docs/rst/java/http/client-side/connection-level.rst +++ /dev/null @@ -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``, -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. \ No newline at end of file diff --git a/akka-docs/rst/java/http/client-side/host-level.rst b/akka-docs/rst/java/http/client-side/host-level.rst deleted file mode 100644 index 5b0ab79ece..0000000000 --- a/akka-docs/rst/java/http/client-side/host-level.rst +++ /dev/null @@ -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`` 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`` 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 diff --git a/akka-docs/rst/java/http/client-side/index.rst b/akka-docs/rst/java/http/client-side/index.rst deleted file mode 100644 index fb36b40b43..0000000000 --- a/akka-docs/rst/java/http/client-side/index.rst +++ /dev/null @@ -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 \ No newline at end of file diff --git a/akka-docs/rst/java/http/client-side/request-level.rst b/akka-docs/rst/java/http/client-side/request-level.rst deleted file mode 100644 index 2a7163eb45..0000000000 --- a/akka-docs/rst/java/http/client-side/request-level.rst +++ /dev/null @@ -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``. 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 diff --git a/akka-docs/rst/java/http/client-side/websocket-support.rst b/akka-docs/rst/java/http/client-side/websocket-support.rst deleted file mode 100644 index 4f376771c9..0000000000 --- a/akka-docs/rst/java/http/client-side/websocket-support.rst +++ /dev/null @@ -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`` to or a ``Flow`` that you connect a ``Source`` -and a ``Sink`` 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`` for binary and ``Source`` -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`` 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>``. - -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>``. - -.. _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)`` -=========================================== ================================================================================ - diff --git a/akka-docs/rst/java/http/common/de-coding.rst b/akka-docs/rst/java/http/common/de-coding.rst deleted file mode 100644 index 022c19c907..0000000000 --- a/akka-docs/rst/java/http/common/de-coding.rst +++ /dev/null @@ -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 ` 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 - diff --git a/akka-docs/rst/java/http/common/index.rst b/akka-docs/rst/java/http/common/index.rst deleted file mode 100644 index d156611329..0000000000 --- a/akka-docs/rst/java/http/common/index.rst +++ /dev/null @@ -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 `, :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 diff --git a/akka-docs/rst/java/http/common/json-support.rst b/akka-docs/rst/java/http/common/json-support.rst deleted file mode 100644 index 0fd1ee1514..0000000000 --- a/akka-docs/rst/java/http/common/json-support.rst +++ /dev/null @@ -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`` 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`` 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 \ No newline at end of file diff --git a/akka-docs/rst/java/http/common/marshalling.rst b/akka-docs/rst/java/http/common/marshalling.rst deleted file mode 100644 index b3feb49fed..0000000000 --- a/akka-docs/rst/java/http/common/marshalling.rst +++ /dev/null @@ -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 `. - -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 diff --git a/akka-docs/rst/java/http/common/timeouts.rst b/akka-docs/rst/java/http/common/timeouts.rst deleted file mode 100644 index 69dcade043..0000000000 --- a/akka-docs/rst/java/http/common/timeouts.rst +++ /dev/null @@ -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. \ No newline at end of file diff --git a/akka-docs/rst/java/http/common/unmarshalling.rst b/akka-docs/rst/java/http/common/unmarshalling.rst deleted file mode 100644 index 37979b5c91..0000000000 --- a/akka-docs/rst/java/http/common/unmarshalling.rst +++ /dev/null @@ -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>`` and as such quite a bit simpler -than its :ref:`marshalling ` 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 `. - -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 - diff --git a/akka-docs/rst/java/http/configuration.rst b/akka-docs/rst/java/http/configuration.rst deleted file mode 100644 index 1926c7f766..0000000000 --- a/akka-docs/rst/java/http/configuration.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/http-model.rst b/akka-docs/rst/java/http/http-model.rst deleted file mode 100644 index d74ff3d0cd..0000000000 --- a/akka-docs/rst/java/http/http-model.rst +++ /dev/null @@ -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`` 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. \ No newline at end of file diff --git a/akka-docs/rst/java/http/implications-of-streaming-http-entity.rst b/akka-docs/rst/java/http/implications-of-streaming-http-entity.rst deleted file mode 100644 index 2cce433037..0000000000 --- a/akka-docs/rst/java/http/implications-of-streaming-http-entity.rst +++ /dev/null @@ -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 layers–from 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`` 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 `_ - as well as `issue #18540 `_ ; as always, contributions are very welcome! - diff --git a/akka-docs/rst/java/http/index.rst b/akka-docs/rst/java/http/index.rst index c0b86256b8..84685596e2 100644 --- a/akka-docs/rst/java/http/index.rst +++ b/akka-docs/rst/java/http/index.rst @@ -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/ `_. diff --git a/akka-docs/rst/java/http/routing-dsl/directives/alphabetically.rst b/akka-docs/rst/java/http/routing-dsl/directives/alphabetically.rst deleted file mode 100644 index 53188a52c6..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/alphabetically.rst +++ /dev/null @@ -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`` -:ref:`-authenticateBasicAsync-java-` Wraps the inner route with Http Basic authentication support using a given ``AsyncAuthenticator`` -:ref:`-authenticateBasicPF-java-` Wraps the inner route with Http Basic authentication support using a given ``AuthenticatorPF`` -:ref:`-authenticateBasicPFAsync-java-` Wraps the inner route with Http Basic authentication support using a given ``AsyncAuthenticatorPF`` -:ref:`-authenticateOAuth2-java-` Wraps the inner route with OAuth Bearer Token authentication support using a given ``AuthenticatorPF`` -:ref:`-authenticateOAuth2Async-java-` Wraps the inner route with OAuth Bearer Token authentication support using a given ``AsyncAuthenticator`` -:ref:`-authenticateOAuth2PF-java-` Wraps the inner route with OAuth Bearer Token authentication support using a given ``AuthenticatorPF`` -:ref:`-authenticateOAuth2PFAsync-java-` Wraps the inner route with OAuth Bearer Token authentication support using a given ``AsyncAuthenticatorPF`` -: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`` 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`` -:ref:`-formFieldMultiMap-java-` Extracts a number of HTTP form field from the request as a ``Map`` -:ref:`-formFieldList-java-` Extracts a number of HTTP form field from the request as a ``List>`` -: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`` 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`` -: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`` 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`` function -:ref:`-mapRouteResult-java-` Transforms the ``RouteResult`` with a ``RouteResult ⇒ RouteResult`` function -:ref:`-mapRouteResultFuture-java-` Transforms the ``RouteResult`` future with a ``CompletionStage`` function -:ref:`-mapRouteResultPF-java-` Transforms the ``RouteResult`` with a ``PartialFunction`` -:ref:`-mapRouteResultWith-java-` Transforms the ``RouteResult`` with a ``RouteResult ⇒ CompletionStage`` function -:ref:`-mapRouteResultWithPF-java-` Transforms the ``RouteResult`` with a ``PartialFunction`` -: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`` and runs the inner route after future completion with the future's value as an extraction of type ``Try`` -:ref:`-onCompleteWithBreaker-java-` "Unwraps" a ``CompletionStage`` inside a ``CircuitBreaker`` and runs the inner route after future completion with the future's value as an extraction of type ``Try`` -:ref:`-onSuccess-java-` "Unwraps" a ``CompletionStage`` 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`` -:ref:`-optionalHeaderValue-java-` Extracts an optional HTTP header value using a given ``HttpHeader ⇒ Option`` 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`` -: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`` -:ref:`-parameterMultiMap-java-` Extracts the request's query parameters as a ``Map>`` -:ref:`-parameterList-java-` Extracts the request's query parameters as a ``Seq>`` -: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`` 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 ` 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 ` 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 -================================================ ============================================================================ - diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/cancelRejection.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/cancelRejection.rst deleted file mode 100644 index f1912765e2..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/cancelRejection.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/cancelRejections.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/cancelRejections.rst deleted file mode 100644 index 5204437de4..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/cancelRejections.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extract.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extract.rst deleted file mode 100644 index 4896f35e98..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extract.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractActorSystem.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractActorSystem.rst deleted file mode 100644 index 2ef2f70af5..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractActorSystem.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractDataBytes.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractDataBytes.rst deleted file mode 100644 index a74722579b..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractDataBytes.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractExecutionContext.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractExecutionContext.rst deleted file mode 100644 index ad37d1975c..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractExecutionContext.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractLog.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractLog.rst deleted file mode 100644 index 939090ea95..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractLog.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractMaterializer.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractMaterializer.rst deleted file mode 100644 index f1ede20d2f..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractMaterializer.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractRequest.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractRequest.rst deleted file mode 100644 index 91c532ea11..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractRequest.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractRequestContext.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractRequestContext.rst deleted file mode 100644 index 3abec29650..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractRequestContext.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractRequestEntity.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractRequestEntity.rst deleted file mode 100644 index bdbeea2af7..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractRequestEntity.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractSettings.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractSettings.rst deleted file mode 100644 index 3983ba7e79..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractSettings.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractStrictEntity.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractStrictEntity.rst deleted file mode 100644 index 3b2279c1aa..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractStrictEntity.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractUnmatchedPath.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractUnmatchedPath.rst deleted file mode 100644 index 4cabc34f83..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractUnmatchedPath.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractUri.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractUri.rst deleted file mode 100644 index 38985f0d68..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/extractUri.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/index.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/index.rst deleted file mode 100644 index 5b507d1b8b..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/index.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapInnerRoute.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapInnerRoute.rst deleted file mode 100644 index a88cb022bc..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapInnerRoute.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapRejections.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapRejections.rst deleted file mode 100644 index 351e903cc5..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapRejections.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapRequest.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapRequest.rst deleted file mode 100644 index a11e8ef1b8..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapRequest.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapRequestContext.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapRequestContext.rst deleted file mode 100644 index f5546fa409..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapRequestContext.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapResponse.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapResponse.rst deleted file mode 100644 index 912556d536..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapResponse.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapResponseEntity.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapResponseEntity.rst deleted file mode 100644 index 799c9618c5..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapResponseEntity.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapResponseHeaders.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapResponseHeaders.rst deleted file mode 100644 index fae2264127..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapResponseHeaders.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapRouteResult.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapRouteResult.rst deleted file mode 100644 index 764734e1f9..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapRouteResult.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapRouteResultFuture.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapRouteResultFuture.rst deleted file mode 100644 index efc21b4515..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapRouteResultFuture.rst +++ /dev/null @@ -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>`` -instead of ``Function>`` 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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapRouteResultPF.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapRouteResultPF.rst deleted file mode 100644 index 7ed461d4e3..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapRouteResultPF.rst +++ /dev/null @@ -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 - diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapRouteResultWith.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapRouteResultWith.rst deleted file mode 100644 index 7757074126..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapRouteResultWith.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapRouteResultWithPF.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapRouteResultWithPF.rst deleted file mode 100644 index e9f1c5d6eb..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapRouteResultWithPF.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapSettings.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapSettings.rst deleted file mode 100644 index b54127a8fc..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapSettings.rst +++ /dev/null @@ -1,16 +0,0 @@ -.. _-mapSettings-java-: - -mapSettings -=========== - -Description ------------ - -Transforms the ``RoutingSettings`` with a ``Function``. - -See also :ref:`-withSettings-java-` or :ref:`-extractSettings-java-`. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#mapSettings diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapUnmatchedPath.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapUnmatchedPath.rst deleted file mode 100644 index de38d61c31..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/mapUnmatchedPath.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/pass.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/pass.rst deleted file mode 100644 index 06dc518837..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/pass.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/provide.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/provide.rst deleted file mode 100644 index 305ea9319a..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/provide.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/recoverRejections.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/recoverRejections.rst deleted file mode 100644 index 78994357c8..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/recoverRejections.rst +++ /dev/null @@ -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, 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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/recoverRejectionsWith.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/recoverRejectionsWith.rst deleted file mode 100644 index 7220a2cfe7..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/recoverRejectionsWith.rst +++ /dev/null @@ -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, CompletionStage>``. - -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/toStrictEntity.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/toStrictEntity.rst deleted file mode 100644 index a1950e6b42..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/toStrictEntity.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/withExecutionContext.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/withExecutionContext.rst deleted file mode 100644 index d8de735585..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/withExecutionContext.rst +++ /dev/null @@ -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 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/withLog.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/withLog.rst deleted file mode 100644 index e98d6ef0c2..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/withLog.rst +++ /dev/null @@ -1,18 +0,0 @@ -.. _-withLog-java-: - -withLog -======= - -Description ------------ - -Allows running an inner route using an alternative :class:`LoggingAdapter` in place of the default one. - -The logging adapter can be extracted in an inner route using :ref:`-extractLog-java-` directly, -or used by directives which internally extract the materializer without surfacing this fact in the API. - - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#withLog diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/withMaterializer.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/withMaterializer.rst deleted file mode 100644 index 510b02058e..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/withMaterializer.rst +++ /dev/null @@ -1,18 +0,0 @@ -.. _-withMaterializer-java-: - -withMaterializer -================ - -Description ------------ - -Allows running an inner route using an alternative ``Materializer`` in place of the default one. - -The materializer can be extracted in an inner route using :ref:`-extractMaterializer-java-` directly, -or used by directives which internally extract the materializer without sufracing this fact in the API -(e.g. responding with a Chunked entity). - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#withMaterializer diff --git a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/withSettings.rst b/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/withSettings.rst deleted file mode 100644 index b284726c08..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/basic-directives/withSettings.rst +++ /dev/null @@ -1,18 +0,0 @@ -.. _-withSettings-java-: - -withSettings -============ - -Description ------------ - -Allows running an inner route using an alternative :class:`RoutingSettings` in place of the default one. - -The execution context can be extracted in an inner route using :ref:`-extractSettings-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#withSettings - diff --git a/akka-docs/rst/java/http/routing-dsl/directives/by-trait.rst b/akka-docs/rst/java/http/routing-dsl/directives/by-trait.rst deleted file mode 100644 index 4970fdb2d1..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/by-trait.rst +++ /dev/null @@ -1,110 +0,0 @@ -Predefined Directives (by trait) -================================ - -All predefined directives are organized into traits that form one part of the overarching ``Directives`` trait. - -.. _Request Directives-java: - -Directives filtering or extracting from the request ---------------------------------------------------- - -:ref:`MethodDirectives-java` - Filter and extract based on the request method. - -:ref:`HeaderDirectives-java` - Filter and extract based on request headers. - -:ref:`PathDirectives-java` - Filter and extract from the request URI path. - -:ref:`HostDirectives-java` - Filter and extract based on the target host. - -:ref:`ParameterDirectives-java`, :ref:`FormFieldDirectives-java` - Filter and extract based on query parameters or form fields. - -:ref:`CodingDirectives-java` - Filter and decode compressed request content. - -:ref:`MarshallingDirectives-java` - Extract the request entity. - -:ref:`SchemeDirectives-java` - Filter and extract based on the request scheme. - -:ref:`SecurityDirectives-java` - Handle authentication data from the request. - -:ref:`CookieDirectives-java` - Filter and extract cookies. - -:ref:`BasicDirectives-java` and :ref:`MiscDirectives-java` - Directives handling request properties. - -:ref:`FileUploadDirectives-java` - Handle file uploads. - - -.. _Response Directives-java: - -Directives creating or transforming the response ------------------------------------------------- - -:ref:`CacheConditionDirectives-java` - Support for conditional requests (``304 Not Modified`` responses). - -:ref:`CookieDirectives-java` - Set, modify, or delete cookies. - -:ref:`CodingDirectives-java` - Compress responses. - -:ref:`FileAndResourceDirectives-java` - Deliver responses from files and resources. - -:ref:`RangeDirectives-java` - Support for range requests (``206 Partial Content`` responses). - -:ref:`RespondWithDirectives-java` - Change response properties. - -:ref:`RouteDirectives-java` - Complete or reject a request with a response. - -:ref:`BasicDirectives-java` and :ref:`MiscDirectives-java` - Directives handling or transforming response properties. - -:ref:`TimeoutDirectives-java` - Configure request timeouts and automatic timeout responses. - - -List of predefined directives by trait --------------------------------------- - -.. toctree:: - :maxdepth: 1 - - basic-directives/index - cache-condition-directives/index - coding-directives/index - cookie-directives/index - debugging-directives/index - execution-directives/index - file-and-resource-directives/index - file-upload-directives/index - form-field-directives/index - future-directives/index - header-directives/index - host-directives/index - marshalling-directives/index - method-directives/index - misc-directives/index - parameter-directives/index - path-directives/index - range-directives/index - respond-with-directives/index - route-directives/index - scheme-directives/index - security-directives/index - websocket-directives/index - timeout-directives/index diff --git a/akka-docs/rst/java/http/routing-dsl/directives/cache-condition-directives/conditional.rst b/akka-docs/rst/java/http/routing-dsl/directives/cache-condition-directives/conditional.rst deleted file mode 100644 index a18d63c5c2..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/cache-condition-directives/conditional.rst +++ /dev/null @@ -1,33 +0,0 @@ -.. _-conditional-java-: - -conditional -=========== - -Description ------------ - -Wraps its inner route with support for Conditional Requests as defined -by http://tools.ietf.org/html/draft-ietf-httpbis-p4-conditional-26. - - -Depending on the given ``eTag`` and ``lastModified`` values this directive immediately responds with -``304 Not Modified`` or ``412 Precondition Failed`` (without calling its inner route) if the request comes with the -respective conditional headers. Otherwise the request is simply passed on to its inner route. - -The algorithm implemented by this directive closely follows what is defined in `this section`__ of the -`HTTPbis spec`__. - -All responses (the ones produces by this directive itself as well as the ones coming back from the inner route) are -augmented with respective ``ETag`` and ``Last-Modified`` response headers. - -Since this directive requires the ``EntityTag`` and ``lastModified`` time stamp for the resource as concrete arguments -it is usually used quite deep down in the route structure (i.e. close to the leaf-level), where the exact resource -targeted by the request has already been established and the respective ETag/Last-Modified values can be determined. - - -The :ref:`FileAndResourceDirectives-java` internally use the ``conditional`` directive for ETag and Last-Modified support -(if the ``akka.http.routing.file-get-conditional`` setting is enabled). - -__ http://tools.ietf.org/html/draft-ietf-httpbis-p4-conditional-26#section-6 -__ https://datatracker.ietf.org/wg/httpbis/ - diff --git a/akka-docs/rst/java/http/routing-dsl/directives/cache-condition-directives/index.rst b/akka-docs/rst/java/http/routing-dsl/directives/cache-condition-directives/index.rst deleted file mode 100644 index 8734c2cc82..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/cache-condition-directives/index.rst +++ /dev/null @@ -1,9 +0,0 @@ -.. _CacheConditionDirectives-java: - -CacheConditionDirectives -======================== - -.. toctree:: - :maxdepth: 1 - - conditional diff --git a/akka-docs/rst/java/http/routing-dsl/directives/coding-directives/decodeRequest.rst b/akka-docs/rst/java/http/routing-dsl/directives/coding-directives/decodeRequest.rst deleted file mode 100644 index 75d3fe1f27..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/coding-directives/decodeRequest.rst +++ /dev/null @@ -1,14 +0,0 @@ -.. _-decodeRequest-java-: - -decodeRequest -============= - -Description ------------ - -Decompresses the incoming request if it is ``gzip`` or ``deflate`` compressed. Uncompressed requests are passed through untouched. If the request encoded with another encoding the request is rejected with an ``UnsupportedRequestEncodingRejection``. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/CodingDirectivesExamplesTest.java#decodeRequest diff --git a/akka-docs/rst/java/http/routing-dsl/directives/coding-directives/decodeRequestWith.rst b/akka-docs/rst/java/http/routing-dsl/directives/coding-directives/decodeRequestWith.rst deleted file mode 100644 index d4c151a2ac..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/coding-directives/decodeRequestWith.rst +++ /dev/null @@ -1,14 +0,0 @@ -.. _-decodeRequestWith-java-: - -decodeRequestWith -================= - -Description ------------ - -Decodes the incoming request if it is encoded with one of the given encoders. If the request encoding doesn't match one of the given encoders the request is rejected with an ``UnsupportedRequestEncodingRejection``. If no decoders are given the default encoders (``Gzip``, ``Deflate``, ``NoCoding``) are used. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/CodingDirectivesExamplesTest.java#decodeRequestWith diff --git a/akka-docs/rst/java/http/routing-dsl/directives/coding-directives/encodeResponse.rst b/akka-docs/rst/java/http/routing-dsl/directives/coding-directives/encodeResponse.rst deleted file mode 100644 index 8a6eb7cf17..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/coding-directives/encodeResponse.rst +++ /dev/null @@ -1,20 +0,0 @@ -.. _-encodeResponse-java-: - -encodeResponse -============== - -Description ------------ - -Encodes the response with the encoding that is requested by the client via the ``Accept-Encoding`` header or rejects the request with an ``UnacceptedResponseEncodingRejection(supportedEncodings)``. - -The response encoding is determined by the rules specified in RFC7231_. - -If the ``Accept-Encoding`` header is missing or empty or specifies an encoding other than identity, gzip or deflate then no encoding is used. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/CodingDirectivesExamplesTest.java#encodeResponse - -.. _RFC7231: http://tools.ietf.org/html/rfc7231#section-5.3.4 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/coding-directives/encodeResponseWith.rst b/akka-docs/rst/java/http/routing-dsl/directives/coding-directives/encodeResponseWith.rst deleted file mode 100644 index f49ac53ef0..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/coding-directives/encodeResponseWith.rst +++ /dev/null @@ -1,23 +0,0 @@ -.. _-encodeResponseWith-java-: - -encodeResponseWith -================== - -Description ------------ - -Encodes the response with the encoding that is requested by the client via the ``Accept-Encoding`` if it is among the provided encoders or rejects the request with an ``UnacceptedResponseEncodingRejection(supportedEncodings)``. - -The response encoding is determined by the rules specified in RFC7231_. - -If the ``Accept-Encoding`` header is missing then the response is encoded using the ``first`` encoder. - -If the ``Accept-Encoding`` header is empty and ``NoCoding`` is part of the encoders then no -response encoding is used. Otherwise the request is rejected. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/CodingDirectivesExamplesTest.java#encodeResponseWith - -.. _RFC7231: http://tools.ietf.org/html/rfc7231#section-5.3.4 diff --git a/akka-docs/rst/java/http/routing-dsl/directives/coding-directives/index.rst b/akka-docs/rst/java/http/routing-dsl/directives/coding-directives/index.rst deleted file mode 100644 index 2974708076..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/coding-directives/index.rst +++ /dev/null @@ -1,14 +0,0 @@ -.. _CodingDirectives-java: - -CodingDirectives -================ - -.. toctree:: - :maxdepth: 1 - - decodeRequest - decodeRequestWith - encodeResponse - encodeResponseWith - requestEncodedWith - responseEncodingAccepted diff --git a/akka-docs/rst/java/http/routing-dsl/directives/coding-directives/requestEncodedWith.rst b/akka-docs/rst/java/http/routing-dsl/directives/coding-directives/requestEncodedWith.rst deleted file mode 100644 index a57d355117..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/coding-directives/requestEncodedWith.rst +++ /dev/null @@ -1,15 +0,0 @@ -.. _-requestEncodedWith-java-: - -requestEncodedWith -================== - -Description ------------ - -Passes the request to the inner route if the request is encoded with the argument encoding. Otherwise, rejects the request with an ``UnacceptedRequestEncodingRejection(encoding)``. - -This directive is the building block for ``decodeRequest`` to reject unsupported encodings. - -Example -------- -TODO: Example snippets for JavaDSL are subject to community contributions! Help us complete the docs, read more about it here: `write example snippets for Akka HTTP Java DSL #20466 `_. diff --git a/akka-docs/rst/java/http/routing-dsl/directives/coding-directives/responseEncodingAccepted.rst b/akka-docs/rst/java/http/routing-dsl/directives/coding-directives/responseEncodingAccepted.rst deleted file mode 100644 index c3ca799b13..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/coding-directives/responseEncodingAccepted.rst +++ /dev/null @@ -1,14 +0,0 @@ -.. _-responseEncodingAccepted-java-: - -responseEncodingAccepted -======================== - -Description ------------ - -Passes the request to the inner route if the request accepts the argument encoding. Otherwise, rejects the request with an ``UnacceptedResponseEncodingRejection(encoding)``. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/CodingDirectivesExamplesTest.java#responseEncodingAccepted diff --git a/akka-docs/rst/java/http/routing-dsl/directives/cookie-directives/cookie.rst b/akka-docs/rst/java/http/routing-dsl/directives/cookie-directives/cookie.rst deleted file mode 100644 index 3509ca33f8..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/cookie-directives/cookie.rst +++ /dev/null @@ -1,17 +0,0 @@ -.. _-cookie-java-: - -cookie -====== - -Description ------------ -Extracts a cookie with a given name from a request or otherwise rejects the request with a ``MissingCookieRejection`` if -the cookie is missing. - -Use the :ref:`-optionalCookie-java-` directive instead if you want to support missing cookies in your inner route. - - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/CookieDirectivesExamplesTest.java#cookie diff --git a/akka-docs/rst/java/http/routing-dsl/directives/cookie-directives/deleteCookie.rst b/akka-docs/rst/java/http/routing-dsl/directives/cookie-directives/deleteCookie.rst deleted file mode 100644 index daa7834e87..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/cookie-directives/deleteCookie.rst +++ /dev/null @@ -1,15 +0,0 @@ -.. _-deleteCookie-java-: - -deleteCookie -============ - -Description ------------ -Adds a header to the response to request the removal of the cookie with the given name on the client. - -Use the :ref:`-setCookie-java-` directive to update a cookie. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/CookieDirectivesExamplesTest.java#deleteCookie diff --git a/akka-docs/rst/java/http/routing-dsl/directives/cookie-directives/index.rst b/akka-docs/rst/java/http/routing-dsl/directives/cookie-directives/index.rst deleted file mode 100644 index 1ffc38de8e..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/cookie-directives/index.rst +++ /dev/null @@ -1,12 +0,0 @@ -.. _CookieDirectives-java: - -CookieDirectives -================ - -.. toctree:: - :maxdepth: 1 - - cookie - deleteCookie - optionalCookie - setCookie diff --git a/akka-docs/rst/java/http/routing-dsl/directives/cookie-directives/optionalCookie.rst b/akka-docs/rst/java/http/routing-dsl/directives/cookie-directives/optionalCookie.rst deleted file mode 100644 index 763920914b..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/cookie-directives/optionalCookie.rst +++ /dev/null @@ -1,16 +0,0 @@ -.. _-optionalCookie-java-: - -optionalCookie -============== - -Description ------------ -Extracts an optional cookie with a given name from a request. - -Use the :ref:`-cookie-java-` directive instead if the inner route does not handle a missing cookie. - - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/CookieDirectivesExamplesTest.java#optionalCookie diff --git a/akka-docs/rst/java/http/routing-dsl/directives/cookie-directives/setCookie.rst b/akka-docs/rst/java/http/routing-dsl/directives/cookie-directives/setCookie.rst deleted file mode 100644 index d4bedb0179..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/cookie-directives/setCookie.rst +++ /dev/null @@ -1,16 +0,0 @@ -.. _-setCookie-java-: - -setCookie -========= - -Description ------------ -Adds a header to the response to request the update of the cookie with the given name on the client. - -Use the :ref:`-deleteCookie-java-` directive to delete a cookie. - - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/CookieDirectivesExamplesTest.java#setCookie diff --git a/akka-docs/rst/java/http/routing-dsl/directives/custom-directives.rst b/akka-docs/rst/java/http/routing-dsl/directives/custom-directives.rst deleted file mode 100644 index f001d12c95..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/custom-directives.rst +++ /dev/null @@ -1,35 +0,0 @@ -.. _Custom Directives-java: - -Custom Directives -================= -Part of the power of akka-http directives comes from the ease with which it’s possible to define -custom directives at differing levels of abstraction. - -There are essentially three ways of creating custom directives: - -1. By introducing new “labels” for configurations of existing directives -2. By transforming existing directives -3. By writing a directive “from scratch” - -Configuration Labeling -______________________ -The easiest way to create a custom directive is to simply assign a new name for a certain configuration -of one or more existing directives. In fact, most of the predefined akka-http directives can be considered -named configurations of more low-level directives. - -The basic technique is explained in the chapter about Composing Directives, where, for example, a new directive -``getOrPut`` is defined like this: - -.. includecode2:: ../../../code/docs/http/javadsl/server/directives/CustomDirectivesExamplesTest.java - :snippet: labeling - -Multiple directives can be nested to produce a single directive out of multiple like this: - -.. includecode2:: ../../../code/docs/http/javadsl/server/directives/CustomDirectivesExamplesTest.java - :snippet: composition - - -Another example is the :ref:`MethodDirectives-java` which are simply instances of a preconfigured :ref:`-method-java-` directive. -The low-level directives that most often form the basis of higher-level “named configuration” directives are grouped -together in the :ref:`BasicDirectives-java` trait. - diff --git a/akka-docs/rst/java/http/routing-dsl/directives/debugging-directives/index.rst b/akka-docs/rst/java/http/routing-dsl/directives/debugging-directives/index.rst deleted file mode 100644 index 406b615bbf..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/debugging-directives/index.rst +++ /dev/null @@ -1,11 +0,0 @@ -.. _DebuggingDirectives-java: - -DebuggingDirectives -=================== - -.. toctree:: - :maxdepth: 1 - - logRequest - logRequestResult - logResult diff --git a/akka-docs/rst/java/http/routing-dsl/directives/debugging-directives/logRequest.rst b/akka-docs/rst/java/http/routing-dsl/directives/debugging-directives/logRequest.rst deleted file mode 100644 index 6dfc3a6cf2..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/debugging-directives/logRequest.rst +++ /dev/null @@ -1,19 +0,0 @@ -.. _-logRequest-java-: - -logRequest -========== - -Description ------------ - -Logs the request. The directive is available with the following parameters: - - * A marker to prefix each log message with. - * A log level. - * A function that creates a :class:``LogEntry`` from the :class:``HttpRequest`` - -Use ``logResult`` for logging the response, or ``logRequestResult`` for logging both. - -Example -------- -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/DebuggingDirectivesExamplesTest.java#logRequest diff --git a/akka-docs/rst/java/http/routing-dsl/directives/debugging-directives/logRequestResult.rst b/akka-docs/rst/java/http/routing-dsl/directives/debugging-directives/logRequestResult.rst deleted file mode 100644 index a71b4f2a14..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/debugging-directives/logRequestResult.rst +++ /dev/null @@ -1,23 +0,0 @@ -.. _-logRequestResult-java-: - -logRequestResult -================ - -Description ------------ -Logs both, the request and the response. - -This directive is a combination of :ref:`-logRequest-java-` and :ref:`-logResult-java-`. - -See :ref:`-logRequest-java-` for the general description how these directives work. - -Example -------- -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/DebuggingDirectivesExamplesTest.java#logRequestResult - -Longer Example --------------- - -This example shows how to log the response time of the request using the Debugging Directive - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/DebuggingDirectivesExamplesTest.java#logRequestResultWithResponseTime diff --git a/akka-docs/rst/java/http/routing-dsl/directives/debugging-directives/logResult.rst b/akka-docs/rst/java/http/routing-dsl/directives/debugging-directives/logResult.rst deleted file mode 100644 index 0ebdc96ad1..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/debugging-directives/logResult.rst +++ /dev/null @@ -1,16 +0,0 @@ -.. _-logResult-java-: - -logResult -========= - -Description ------------ -Logs the response. - -See :ref:`-logRequest-java-` for the general description how these directives work. - -Use ``logRequest`` for logging the request, or ``logRequestResult`` for logging both. - -Example -------- -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/DebuggingDirectivesExamplesTest.java#logResult diff --git a/akka-docs/rst/java/http/routing-dsl/directives/execution-directives/handleExceptions.rst b/akka-docs/rst/java/http/routing-dsl/directives/execution-directives/handleExceptions.rst deleted file mode 100644 index 56165e0340..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/execution-directives/handleExceptions.rst +++ /dev/null @@ -1,18 +0,0 @@ -.. _-handleExceptions-java-: - -handleExceptions -================ - -Description ------------ -Catches exceptions thrown by the inner route and handles them using the specified ``ExceptionHandler``. - -Using this directive is an alternative to using a global implicitly defined ``ExceptionHandler`` that -applies to the complete route. - -See :ref:`exception-handling-java` for general information about options for handling exceptions. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/ExecutionDirectivesExamplesTest.java#handleExceptions diff --git a/akka-docs/rst/java/http/routing-dsl/directives/execution-directives/handleRejections.rst b/akka-docs/rst/java/http/routing-dsl/directives/execution-directives/handleRejections.rst deleted file mode 100644 index 619155c4e7..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/execution-directives/handleRejections.rst +++ /dev/null @@ -1,17 +0,0 @@ -.. _-handleRejections-java-: - -handleRejections -================ - -Description ------------ - -Using this directive is an alternative to using a global implicitly defined ``RejectionHandler`` that -applies to the complete route. - -See :ref:`rejections-java` for general information about options for handling rejections. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/ExecutionDirectivesExamplesTest.java#handleRejections diff --git a/akka-docs/rst/java/http/routing-dsl/directives/execution-directives/index.rst b/akka-docs/rst/java/http/routing-dsl/directives/execution-directives/index.rst deleted file mode 100644 index 0f69dcf109..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/execution-directives/index.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. _ExecutionDirectives-java: - -ExecutionDirectives -=================== - -.. toctree:: - :maxdepth: 1 - - handleExceptions - handleRejections diff --git a/akka-docs/rst/java/http/routing-dsl/directives/file-and-resource-directives/getFromBrowseableDirectories.rst b/akka-docs/rst/java/http/routing-dsl/directives/file-and-resource-directives/getFromBrowseableDirectories.rst deleted file mode 100644 index 0aed8331c3..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/file-and-resource-directives/getFromBrowseableDirectories.rst +++ /dev/null @@ -1,23 +0,0 @@ -.. _-getFromBrowseableDirectories-java-: - -getFromBrowseableDirectories -============================ - -Description ------------ - -The ``getFromBrowseableDirectories`` is a combination of serving files from the specified directories -(like ``getFromDirectory``) and listing a browseable directory with ``listDirectoryContents``. - -Nesting this directive beneath ``get`` is not necessary as this directive will only respond to ``GET`` requests. - -Use ``getFromBrowseableDirectory`` to serve only one directory. - -Use ``getFromDirectory`` if directory browsing isn't required. - -For more details refer to :ref:`-getFromBrowseableDirectory-java-`. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/FileAndResourceDirectivesExamplesTest.java#getFromBrowseableDirectories diff --git a/akka-docs/rst/java/http/routing-dsl/directives/file-and-resource-directives/getFromBrowseableDirectory.rst b/akka-docs/rst/java/http/routing-dsl/directives/file-and-resource-directives/getFromBrowseableDirectory.rst deleted file mode 100644 index 72c4ae7d97..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/file-and-resource-directives/getFromBrowseableDirectory.rst +++ /dev/null @@ -1,44 +0,0 @@ -.. _-getFromBrowseableDirectory-java-: - -getFromBrowseableDirectory -========================== - -Description ------------ - -The ``getFromBrowseableDirectories`` is a combination of serving files from the specified directories (like -``getFromDirectory``) and listing a browseable directory with ``listDirectoryContents``. - -Nesting this directive beneath ``get`` is not necessary as this directive will only respond to ``GET`` requests. - -Use ``getFromBrowseableDirectory`` to serve only one directory. - -Use ``getFromDirectory`` if directory browsing isn't required. - -For more details refer to :ref:`-getFromBrowseableDirectory-java-`. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/FileAndResourceDirectivesExamplesTest.java#getFromBrowseableDirectory - - -Default file listing page example -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Directives which list directories (e.g. ``getFromBrowsableDirectory``) use an implicit ``DirectoryRenderer`` -instance to perfm the actual rendering of the file listing. This rendered can be easily overriden by simply -providing one in-scope for the directives to use, so you can build your custom directory listings. - - -The default renderer is ``akka.http.scaladsl.server.directives.FileAndResourceDirectives.defaultDirectoryRenderer``, -and renders a listing which looks like this: - -.. figure:: ../../../../../images/akka-http-file-listing.png - :scale: 75% - :align: center - - Example page rendered by the ``defaultDirectoryRenderer``. - -It's possible to turn off rendering the footer stating which version of Akka HTTP is rendering this page by configuring -the ``akka.http.routing.render-vanity-footer`` configuration option to ``off``. diff --git a/akka-docs/rst/java/http/routing-dsl/directives/file-and-resource-directives/getFromDirectory.rst b/akka-docs/rst/java/http/routing-dsl/directives/file-and-resource-directives/getFromDirectory.rst deleted file mode 100644 index 7fe40d9675..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/file-and-resource-directives/getFromDirectory.rst +++ /dev/null @@ -1,31 +0,0 @@ -.. _-getFromDirectory-java-: - -getFromDirectory -================ - -Description ------------ - -Allows exposing a directory's files for GET requests for its contents. - -The ``unmatchedPath`` (see :ref:`-extractUnmatchedPath-java-`) of the ``RequestContext`` is first transformed by -the given ``pathRewriter`` function, before being appended to the given directory name to build the final file name. - -To serve a single file use :ref:`-getFromFile-java-`. -To serve browsable directory listings use :ref:`-getFromBrowseableDirectories-java-`. -To serve files from a classpath directory use :ref:`-getFromResourceDirectory-java-` instead. - -Note that it's not required to wrap this directive with ``get`` as this directive will only respond to ``GET`` requests. - -.. note:: - The file's contents will be read using an Akka Streams `Source` which *automatically uses - a pre-configured dedicated blocking io dispatcher*, which separates the blocking file operations from the rest of the stream. - - Note also that thanks to using Akka Streams internally, the file will be served at the highest speed reachable by - the client, and not faster – i.e. the file will *not* end up being loaded in full into memory before writing it to - the client. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/FileAndResourceDirectivesExamplesTest.java#getFromDirectory diff --git a/akka-docs/rst/java/http/routing-dsl/directives/file-and-resource-directives/getFromFile.rst b/akka-docs/rst/java/http/routing-dsl/directives/file-and-resource-directives/getFromFile.rst deleted file mode 100644 index 5042cc5749..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/file-and-resource-directives/getFromFile.rst +++ /dev/null @@ -1,31 +0,0 @@ -.. _-getFromFile-java-: - -getFromFile -=========== - -Description ------------ - -Allows exposing a file to be streamed to the client issuing the request. - -The ``unmatchedPath`` (see :ref:`-extractUnmatchedPath-java-`) of the ``RequestContext`` is first transformed by -the given ``pathRewriter`` function, before being appended to the given directory name to build the final file name. - -To files from a given directory use :ref:`-getFromDirectory-java-`. -To serve browsable directory listings use :ref:`-getFromBrowseableDirectories-java-`. -To serve files from a classpath directory use :ref:`-getFromResourceDirectory-java-` instead. - -Note that it's not required to wrap this directive with ``get`` as this directive will only respond to ``GET`` requests. - -.. note:: - The file's contents will be read using an Akka Streams `Source` which *automatically uses - a pre-configured dedicated blocking io dispatcher*, which separates the blocking file operations from the rest of the stream. - - Note also that thanks to using Akka Streams internally, the file will be served at the highest speed reachable by - the client, and not faster – i.e. the file will *not* end up being loaded in full into memory before writing it to - the client. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/FileAndResourceDirectivesExamplesTest.java#getFromFile diff --git a/akka-docs/rst/java/http/routing-dsl/directives/file-and-resource-directives/getFromResource.rst b/akka-docs/rst/java/http/routing-dsl/directives/file-and-resource-directives/getFromResource.rst deleted file mode 100644 index d7032776df..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/file-and-resource-directives/getFromResource.rst +++ /dev/null @@ -1,19 +0,0 @@ -.. _-getFromResource-java-: - -getFromResource -=============== - -Description ------------ - -Completes ``GET`` requests with the content of the given classpath resource. - -For details refer to :ref:`-getFromFile-java-` which works the same way but obtaining the file from the filesystem -instead of the applications classpath. - -Note that it's not required to wrap this directive with ``get`` as this directive will only respond to ``GET`` requests. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/FileAndResourceDirectivesExamplesTest.java#getFromResource diff --git a/akka-docs/rst/java/http/routing-dsl/directives/file-and-resource-directives/getFromResourceDirectory.rst b/akka-docs/rst/java/http/routing-dsl/directives/file-and-resource-directives/getFromResourceDirectory.rst deleted file mode 100644 index 1e56be9cff..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/file-and-resource-directives/getFromResourceDirectory.rst +++ /dev/null @@ -1,19 +0,0 @@ -.. _-getFromResourceDirectory-java-: - -getFromResourceDirectory -======================== - -Description ------------ - -Completes ``GET`` requests with the content of the given classpath resource directory. - -For details refer to :ref:`-getFromDirectory-java-` which works the same way but obtaining the file from the filesystem -instead of the applications classpath. - -Note that it's not required to wrap this directive with ``get`` as this directive will only respond to ``GET`` requests. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/FileAndResourceDirectivesExamplesTest.java#getFromResourceDirectory diff --git a/akka-docs/rst/java/http/routing-dsl/directives/file-and-resource-directives/index.rst b/akka-docs/rst/java/http/routing-dsl/directives/file-and-resource-directives/index.rst deleted file mode 100644 index d1a4adb5fa..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/file-and-resource-directives/index.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. _FileAndResourceDirectives-java: - -FileAndResourceDirectives -========================= - -Like the :ref:`RouteDirectives-java` the ``FileAndResourceDirectives`` are somewhat special in akka-http's routing DSL. -Contrary to all other directives they do not produce instances of type ``Directive[L <: HList]`` but rather "plain" -routes of type ``Route``. -The reason is that they are not meant for wrapping an inner route (like most other directives, as intermediate-level -elements of a route structure, do) but rather form the actual route structure **leaves**. - -So in most cases the inner-most element of a route structure branch is one of the :ref:`RouteDirectives-java` or -``FileAndResourceDirectives``. - -.. toctree:: - :maxdepth: 1 - - getFromBrowseableDirectories - getFromBrowseableDirectory - getFromDirectory - getFromFile - getFromResource - getFromResourceDirectory - listDirectoryContents diff --git a/akka-docs/rst/java/http/routing-dsl/directives/file-and-resource-directives/listDirectoryContents.rst b/akka-docs/rst/java/http/routing-dsl/directives/file-and-resource-directives/listDirectoryContents.rst deleted file mode 100644 index d8e58f51e3..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/file-and-resource-directives/listDirectoryContents.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. _-listDirectoryContents-java-: - -listDirectoryContents -===================== - -Description ------------ - -Completes GET requests with a unified listing of the contents of all given directories. The actual rendering of the -directory contents is performed by the in-scope ``Marshaller[DirectoryListing]``. - -To just serve files use :ref:`-getFromDirectory-java-`. - -To serve files and provide a browseable directory listing use :ref:`-getFromBrowseableDirectories-java-` instead. - -The rendering can be overridden by providing a custom ``Marshaller[DirectoryListing]``, you can read more about it in -:ref:`-getFromDirectory-java-` 's documentation. - -Note that it's not required to wrap this directive with ``get`` as this directive will only respond to ``GET`` requests. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/FileAndResourceDirectivesExamplesTest.java#listDirectoryContents diff --git a/akka-docs/rst/java/http/routing-dsl/directives/file-upload-directives/fileUpload.rst b/akka-docs/rst/java/http/routing-dsl/directives/file-upload-directives/fileUpload.rst deleted file mode 100644 index 01991357bf..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/file-upload-directives/fileUpload.rst +++ /dev/null @@ -1,22 +0,0 @@ -.. _-fileUpload-java-: - -fileUpload -========== - -Description ------------ -Simple access to the stream of bytes for a file uploaded as a multipart form together with metadata -about the upload as extracted value. - -If there is no field with the given name the request will be rejected, if there are multiple file parts -with the same name, the first one will be used and the subsequent ones ignored. - - -Example -------- -.. includecode2:: ../../../../code/docs/http/javadsl/server/directives/FileUploadDirectivesExamplesTest.java - :snippet: fileUpload - -:: - - curl --form "csv=@uploadFile.txt" http://: diff --git a/akka-docs/rst/java/http/routing-dsl/directives/file-upload-directives/index.rst b/akka-docs/rst/java/http/routing-dsl/directives/file-upload-directives/index.rst deleted file mode 100644 index b008255cad..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/file-upload-directives/index.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. _FileUploadDirectives-java: - -FileUploadDirectives -==================== - -.. toctree:: - :maxdepth: 1 - - uploadedFile - fileUpload diff --git a/akka-docs/rst/java/http/routing-dsl/directives/file-upload-directives/uploadedFile.rst b/akka-docs/rst/java/http/routing-dsl/directives/file-upload-directives/uploadedFile.rst deleted file mode 100644 index f6ffe06511..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/file-upload-directives/uploadedFile.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. _-uploadedFile-java-: - -uploadedFile -============ - -Description ------------ -Streams the contents of a file uploaded as a multipart form into a temporary file on disk and provides the file and -metadata about the upload as extracted value. - -If there is an error writing to disk the request will be failed with the thrown exception, if there is no field -with the given name the request will be rejected, if there are multiple file parts with the same name, the first -one will be used and the subsequent ones ignored. - -.. note:: - This directive will stream contents of the request into a file, however one can not start processing these - until the file has been written completely. For streaming APIs it is preferred to use the :ref:`-fileUpload-java-` - directive, as it allows for streaming handling of the incoming data bytes. - - -Example -------- -.. includecode2:: ../../../../code/docs/http/javadsl/server/directives/FileUploadDirectivesExamplesTest.java - :snippet: uploadedFile \ No newline at end of file diff --git a/akka-docs/rst/java/http/routing-dsl/directives/form-field-directives/formField.rst b/akka-docs/rst/java/http/routing-dsl/directives/form-field-directives/formField.rst deleted file mode 100644 index 5b9265ea8d..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/form-field-directives/formField.rst +++ /dev/null @@ -1,12 +0,0 @@ -.. _-formField-java-: - -formField -========= - ------------ -Allows extracting a single Form field sent in the request. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/FormFieldDirectivesExamplesTest.java#formField diff --git a/akka-docs/rst/java/http/routing-dsl/directives/form-field-directives/formFieldList.rst b/akka-docs/rst/java/http/routing-dsl/directives/form-field-directives/formFieldList.rst deleted file mode 100644 index 7f6ba96934..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/form-field-directives/formFieldList.rst +++ /dev/null @@ -1,21 +0,0 @@ -.. _-formFieldList-java-: - -formFieldList -============= - -Description ------------ -Extracts all HTTP form fields at once in the original order as (name, value) tuples of type ``Map.Entry``. - -This directive can be used if the exact order of form fields is important or if parameters can occur several times. - -Warning -------- -The directive reads all incoming HTT form fields without any configured upper bound. -It means, that requests with form fields holding significant amount of data (ie. during a file upload) -can cause performance issues or even an ``OutOfMemoryError`` s. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/FormFieldDirectivesExamplesTest.java#formFieldList diff --git a/akka-docs/rst/java/http/routing-dsl/directives/form-field-directives/formFieldMap.rst b/akka-docs/rst/java/http/routing-dsl/directives/form-field-directives/formFieldMap.rst deleted file mode 100644 index 5b678f0518..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/form-field-directives/formFieldMap.rst +++ /dev/null @@ -1,20 +0,0 @@ -.. _-formFieldMap-java-: - -formFieldMap -============ - -Description ------------ -Extracts all HTTP form fields at once as a ``Map`` mapping form field names to form field values. - -If form data contain a field value several times, the map will contain the last one. - -Warning -------- -Use of this directive can result in performance degradation or even in ``OutOfMemoryError`` s. -See :ref:`-formFieldList-java-` for details. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/FormFieldDirectivesExamplesTest.java#formFieldMap diff --git a/akka-docs/rst/java/http/routing-dsl/directives/form-field-directives/formFieldMultiMap.rst b/akka-docs/rst/java/http/routing-dsl/directives/form-field-directives/formFieldMultiMap.rst deleted file mode 100644 index a922975c5b..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/form-field-directives/formFieldMultiMap.rst +++ /dev/null @@ -1,23 +0,0 @@ -.. _-formFieldMultiMap-java-: - -formFieldMultiMap -================= - -Description ------------ - -Extracts all HTTP form fields at once as a multi-map of type ``Map>`` mapping -a form name to a list of all its values. - -This directive can be used if form fields can occur several times. - -The order of values is *not* specified. - -Warning -------- -Use of this directive can result in performance degradation or even in ``OutOfMemoryError`` s. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/FormFieldDirectivesExamplesTest.java#formFieldMultiMap diff --git a/akka-docs/rst/java/http/routing-dsl/directives/form-field-directives/index.rst b/akka-docs/rst/java/http/routing-dsl/directives/form-field-directives/index.rst deleted file mode 100644 index c773f9e96c..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/form-field-directives/index.rst +++ /dev/null @@ -1,12 +0,0 @@ -.. _FormFieldDirectives-java: - -FormFieldDirectives -=================== - -.. toctree:: - :maxdepth: 1 - - formField - formFieldList - formFieldMap - formFieldMultiMap diff --git a/akka-docs/rst/java/http/routing-dsl/directives/future-directives/completeOrRecoverWith.rst b/akka-docs/rst/java/http/routing-dsl/directives/future-directives/completeOrRecoverWith.rst deleted file mode 100644 index d889436972..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/future-directives/completeOrRecoverWith.rst +++ /dev/null @@ -1,18 +0,0 @@ -.. _-completeOrRecoverWith-java-: - -completeOrRecoverWith -===================== - -Description ------------ -"Unwraps" a ``CompletionStage`` and runs the inner route when the stage has failed -with the stage's failure exception as an extraction of type ``Throwable``. -If the completion stage succeeds the request is completed using the values marshaller -(This directive therefore requires a marshaller for the completion stage value type to be -provided.) - -To handle the successful case manually as well, use the :ref:`-onComplete-java-` directive, instead. - -Example -------- -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/FutureDirectivesExamplesTest.java#completeOrRecoverWith diff --git a/akka-docs/rst/java/http/routing-dsl/directives/future-directives/index.rst b/akka-docs/rst/java/http/routing-dsl/directives/future-directives/index.rst deleted file mode 100644 index 5aa06d193d..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/future-directives/index.rst +++ /dev/null @@ -1,15 +0,0 @@ -.. _FutureDirectives-java: - -FuturesDirectives -================= - -Future directives can be used to run inner routes once the provided ``Future[T]`` has been completed. - -.. toctree:: - :maxdepth: 1 - - onComplete - onCompleteWithBreaker - onSuccess - completeOrRecoverWith - diff --git a/akka-docs/rst/java/http/routing-dsl/directives/future-directives/onComplete.rst b/akka-docs/rst/java/http/routing-dsl/directives/future-directives/onComplete.rst deleted file mode 100644 index c521a84838..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/future-directives/onComplete.rst +++ /dev/null @@ -1,18 +0,0 @@ -.. _-onComplete-java-: - -onComplete -========== - -Description ------------ -Evaluates its parameter of type ``CompletionStage``, and once it has been completed, extracts its -result as a value of type ``Try`` and passes it to the inner route. A ``Try`` can either be a ``Success`` containing -the ``T`` value or a ``Failure`` containing the ``Throwable``. - -To handle the ``Failure`` case automatically and only work with the result value, use :ref:`-onSuccess-java-`. - -To complete with a successful result automatically and just handle the failure result, use :ref:`-completeOrRecoverWith-java-`, instead. - -Example -------- -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/FutureDirectivesExamplesTest.java#onComplete diff --git a/akka-docs/rst/java/http/routing-dsl/directives/future-directives/onCompleteWithBreaker.rst b/akka-docs/rst/java/http/routing-dsl/directives/future-directives/onCompleteWithBreaker.rst deleted file mode 100644 index 67bb4f4c5f..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/future-directives/onCompleteWithBreaker.rst +++ /dev/null @@ -1,19 +0,0 @@ -.. _-onCompleteWithBreaker-java-: - -onCompleteWithBreaker -===================== - -Description ------------ -Evaluates its parameter of type ``CompletionStage`` protecting it with the specified ``CircuitBreaker``. -Refer to :ref:`Akka Circuit Breaker` for a detailed description of this pattern. - -If the ``CircuitBreaker`` is open, the request is rejected with a ``CircuitBreakerOpenRejection``. -Note that in this case the request's entity databytes stream is cancelled, and the connection is closed -as a consequence. - -Otherwise, the same behaviour provided by :ref:`-onComplete-java-` is to be expected. - -Example -------- -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/FutureDirectivesExamplesTest.java#onCompleteWithBreaker diff --git a/akka-docs/rst/java/http/routing-dsl/directives/future-directives/onSuccess.rst b/akka-docs/rst/java/http/routing-dsl/directives/future-directives/onSuccess.rst deleted file mode 100644 index 569a875f1b..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/future-directives/onSuccess.rst +++ /dev/null @@ -1,17 +0,0 @@ -.. _-onSuccess-java-: - -onSuccess -========= - -Description ------------ -Evaluates its parameter of type ``CompletionStage``, and once it has been completed successfully, -extracts its result as a value of type ``T`` and passes it to the inner route. - -If the future fails its failure throwable is bubbled up to the nearest ``ExceptionHandler``. - -To handle the ``Failure`` case manually as well, use :ref:`-onComplete-java-`, instead. - -Example -------- -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/FutureDirectivesExamplesTest.java#onSuccess diff --git a/akka-docs/rst/java/http/routing-dsl/directives/header-directives/checkSameOrigin.rst b/akka-docs/rst/java/http/routing-dsl/directives/header-directives/checkSameOrigin.rst deleted file mode 100644 index 6d1de6df73..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/header-directives/checkSameOrigin.rst +++ /dev/null @@ -1,17 +0,0 @@ -.. _-checkSameOrigin-java-: - -checkSameOrigin -=============== - -Description ------------ -Checks that request comes from the same origin. Extracts the ``Origin`` header value and verifies that allowed range -contains the obtained value. In the case of absent of the ``Origin`` header rejects with a ``MissingHeaderRejection``. -If the origin value is not in the allowed range rejects with an ``InvalidOriginHeaderRejection`` -and ``StatusCodes.FORBIDDEN`` status. - -Example -------- -Checking the ``Origin`` header: - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/HeaderDirectivesExamplesTest.java#checkSameOrigin diff --git a/akka-docs/rst/java/http/routing-dsl/directives/header-directives/headerValue.rst b/akka-docs/rst/java/http/routing-dsl/directives/header-directives/headerValue.rst deleted file mode 100644 index e21dbb2644..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/header-directives/headerValue.rst +++ /dev/null @@ -1,22 +0,0 @@ -.. _-headerValue-java-: - -headerValue -=========== - -Description ------------ -Traverses the list of request headers with the specified function and extracts the first value the function returns as -``Optional[value]``. - -The :ref:`-headerValue-java-` directive is a mixture of ``map`` and ``find`` on the list of request headers. The specified function -is called once for each header until the function returns ``Optional(value)``. This value is extracted and presented to the -inner route. If the function throws an exception the request is rejected with a ``MalformedHeaderRejection``. If the -function returns ``Optional.empty`` for every header the request is rejected as "NotFound". - -This directive is the basis for building other request header related directives. - -See also :ref:`-headerValuePF-java-` for a nicer syntactic alternative. - -Example -------- -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/HeaderDirectivesExamplesTest.java#headerValue \ No newline at end of file diff --git a/akka-docs/rst/java/http/routing-dsl/directives/header-directives/headerValueByName.rst b/akka-docs/rst/java/http/routing-dsl/directives/header-directives/headerValueByName.rst deleted file mode 100644 index 798fe4fa9f..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/header-directives/headerValueByName.rst +++ /dev/null @@ -1,17 +0,0 @@ -.. _-headerValueByName-java-: - -headerValueByName -================= - -Description ------------ -Extracts the value of the HTTP request header with the given name. - -If no header with a matching name is found the request is rejected with a ``MissingHeaderRejection``. - -If the header is expected to be missing in some cases or to customize -handling when the header is missing use the :ref:`-optionalHeaderValueByName-java-` directive instead. - -Example -------- -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/HeaderDirectivesExamplesTest.java#headerValueByName \ No newline at end of file diff --git a/akka-docs/rst/java/http/routing-dsl/directives/header-directives/headerValueByType.rst b/akka-docs/rst/java/http/routing-dsl/directives/header-directives/headerValueByType.rst deleted file mode 100644 index 2ae1a7764d..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/header-directives/headerValueByType.rst +++ /dev/null @@ -1,22 +0,0 @@ -.. _-headerValueByType-java-: - -headerValueByType -================= - -Description ------------ -Traverses the list of request headers and extracts the first header of the given type. - -The ``headerValueByType`` directive finds a header of the given type in the list of request header. If no header of -the given type is found the request is rejected with a ``MissingHeaderRejection``. - -If the header is expected to be missing in some cases or to customize handling when the header -is missing use the :ref:`-optionalHeaderValueByType-java-` directive instead. - -.. note:: - Custom headers will only be matched by this directive if they extend ``ModeledCustomHeader`` - from the Scala DSL and there is currently no API for the Java DSL (Ticket #20415) - -Example -------- -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/HeaderDirectivesExamplesTest.java#headerValueByType diff --git a/akka-docs/rst/java/http/routing-dsl/directives/header-directives/headerValuePF.rst b/akka-docs/rst/java/http/routing-dsl/directives/header-directives/headerValuePF.rst deleted file mode 100644 index fd6d44b370..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/header-directives/headerValuePF.rst +++ /dev/null @@ -1,19 +0,0 @@ -.. _-headerValuePF-java-: - -headerValuePF -============= - -Description ------------ -Calls the specified partial function with the first request header the function is ``isDefinedAt`` and extracts the -result of calling the function. - -The ``headerValuePF`` directive is an alternative syntax version of :ref:`-headerValue-java-`. - -If the function throws an exception the request is rejected with a ``MalformedHeaderRejection``. - -If the function is not defined for any header the request is rejected as "NotFound". - -Example -------- -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/HeaderDirectivesExamplesTest.java#headerValuePF \ No newline at end of file diff --git a/akka-docs/rst/java/http/routing-dsl/directives/header-directives/index.rst b/akka-docs/rst/java/http/routing-dsl/directives/header-directives/index.rst deleted file mode 100644 index 8461fdc3cb..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/header-directives/index.rst +++ /dev/null @@ -1,20 +0,0 @@ -.. _HeaderDirectives-java: - -HeaderDirectives -================ - -Header directives can be used to extract header values from the request. To change -response headers use one of the :ref:`RespondWithDirectives-java`. - -.. toctree:: - :maxdepth: 1 - - headerValue - headerValueByName - headerValueByType - headerValuePF - optionalHeaderValue - optionalHeaderValueByName - optionalHeaderValueByType - optionalHeaderValuePF - checkSameOrigin diff --git a/akka-docs/rst/java/http/routing-dsl/directives/header-directives/optionalHeaderValue.rst b/akka-docs/rst/java/http/routing-dsl/directives/header-directives/optionalHeaderValue.rst deleted file mode 100644 index 2340367742..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/header-directives/optionalHeaderValue.rst +++ /dev/null @@ -1,16 +0,0 @@ -.. _-optionalHeaderValue-java-: - -optionalHeaderValue -=================== - -Description ------------ -Traverses the list of request headers with the specified function and extracts the first value the function returns as -``Optional[value]``. - -The ``optionalHeaderValue`` directive is similar to the :ref:`-headerValue-java-` directive but always extracts an ``Option`` -value instead of rejecting the request if no matching header could be found. - -Example -------- -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/HeaderDirectivesExamplesTest.java#optionalHeaderValue \ No newline at end of file diff --git a/akka-docs/rst/java/http/routing-dsl/directives/header-directives/optionalHeaderValueByName.rst b/akka-docs/rst/java/http/routing-dsl/directives/header-directives/optionalHeaderValueByName.rst deleted file mode 100644 index f80aef8c74..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/header-directives/optionalHeaderValueByName.rst +++ /dev/null @@ -1,15 +0,0 @@ -.. _-optionalHeaderValueByName-java-: - -optionalHeaderValueByName -========================= - -Description ------------ -Optionally extracts the value of the HTTP request header with the given name. - -The ``optionalHeaderValueByName`` directive is similar to the :ref:`-headerValueByName-java-` directive but always extracts -an ``Optional`` value instead of rejecting the request if no matching header could be found. - -Example -------- -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/HeaderDirectivesExamplesTest.java#optionalHeaderValueByName \ No newline at end of file diff --git a/akka-docs/rst/java/http/routing-dsl/directives/header-directives/optionalHeaderValueByType.rst b/akka-docs/rst/java/http/routing-dsl/directives/header-directives/optionalHeaderValueByType.rst deleted file mode 100644 index 0ef5356e05..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/header-directives/optionalHeaderValueByType.rst +++ /dev/null @@ -1,21 +0,0 @@ -.. _-optionalHeaderValueByType-java-: - -optionalHeaderValueByType -========================= - -Description ------------ -Optionally extracts the value of the HTTP request header of the given type. - -The ``optionalHeaderValueByType`` directive is similar to the :ref:`-headerValueByType-java-` directive but always extracts -an ``Optional`` value instead of rejecting the request if no matching header could be found. - -.. note:: - Custom headers will only be matched by this directive if they extend ``ModeledCustomHeader`` - from the Scala DSL and there is currently no API for the Java DSL (Ticket #20415) - - To learn more about defining custom headers, read: :ref:`custom-headers-scala`. - -Example -------- -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/HeaderDirectivesExamplesTest.java#optionalHeaderValueByType \ No newline at end of file diff --git a/akka-docs/rst/java/http/routing-dsl/directives/header-directives/optionalHeaderValuePF.rst b/akka-docs/rst/java/http/routing-dsl/directives/header-directives/optionalHeaderValuePF.rst deleted file mode 100644 index 773a06e851..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/header-directives/optionalHeaderValuePF.rst +++ /dev/null @@ -1,16 +0,0 @@ -.. _-optionalHeaderValuePF-java-: - -optionalHeaderValuePF -===================== - -Description ------------ -Calls the specified partial function with the first request header the function is ``isDefinedAt`` and extracts the -result of calling the function. - -The ``optionalHeaderValuePF`` directive is similar to the :ref:`-headerValuePF-java-` directive but always extracts an ``Optional`` -value instead of rejecting the request if no matching header could be found. - -Example -------- -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/HeaderDirectivesExamplesTest.java#optionalHeaderValuePF \ No newline at end of file diff --git a/akka-docs/rst/java/http/routing-dsl/directives/host-directives/extractHostName.rst b/akka-docs/rst/java/http/routing-dsl/directives/host-directives/extractHostName.rst deleted file mode 100644 index ac50fe175a..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/host-directives/extractHostName.rst +++ /dev/null @@ -1,13 +0,0 @@ -.. _-extractHost-java-: - -extractHostName -=============== - -Extract the hostname part of the ``Host`` request header and expose it as a ``String`` extraction -to its inner route. - - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/HostDirectivesExamplesTest.java#extractHostname diff --git a/akka-docs/rst/java/http/routing-dsl/directives/host-directives/host.rst b/akka-docs/rst/java/http/routing-dsl/directives/host-directives/host.rst deleted file mode 100644 index 1a9aaff0ae..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/host-directives/host.rst +++ /dev/null @@ -1,44 +0,0 @@ -.. _-host-java-: - -host -==== - -Filter requests matching conditions against the hostname part of the Host header value -in the request. - -There are a few variants: - -* reject all requests with a hostname different from the given ones -* reject all requests for which the hostname does not satisfy the given predicate -* reject all requests for which the hostname does not satisfy the given regular expression - -The regular expression matching works a little bit different: it rejects all requests with a hostname -that doesn't have a prefix matching the given regular expression and also extracts a ``String`` to its -inner route following this rules: - - * For all matching requests the prefix string matching the regex is extracted and passed to the inner route. - * If the regex contains a capturing group only the string matched by this group is extracted. - * If the regex contains more than one capturing group an ``IllegalArgumentException`` is thrown. - -Example -------- - -Matching a list of hosts: - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/HostDirectivesExamplesTest.java#host1 - -Making sure the host satisfies the given predicate - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/HostDirectivesExamplesTest.java#host2 - -Using a regular expressions: - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/HostDirectivesExamplesTest.java#matchAndExtractHost - -Beware that in the case of introducing multiple capturing groups in the regex such as in the case bellow, the -directive will fail at runtime, at the moment the route tree is evaluated for the first time. This might cause -your http handler actor to enter in a fail/restart loop depending on your supervision strategy. - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/HostDirectivesExamplesTest.java#failing-matchAndExtractHost - - diff --git a/akka-docs/rst/java/http/routing-dsl/directives/host-directives/index.rst b/akka-docs/rst/java/http/routing-dsl/directives/host-directives/index.rst deleted file mode 100644 index 269b3da2d5..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/host-directives/index.rst +++ /dev/null @@ -1,15 +0,0 @@ -.. _HostDirectives-java: - -HostDirectives -============== - -HostDirectives allow you to filter requests based on the hostname part of the ``Host`` header -contained in incoming requests as well as extracting its value for usage in inner routes. - -.. toctree:: - :maxdepth: 1 - - host - extractHostName - - \ No newline at end of file diff --git a/akka-docs/rst/java/http/routing-dsl/directives/index.rst b/akka-docs/rst/java/http/routing-dsl/directives/index.rst deleted file mode 100644 index e11a9f7f84..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/index.rst +++ /dev/null @@ -1,88 +0,0 @@ -.. _directives-java: - -Directives -========== - -A "Directive" is a small building block used for creating arbitrarily complex :ref:`route structures `. -Akka HTTP already pre-defines a large number of directives and you can easily construct your own: - -.. toctree:: - :maxdepth: 1 - - alphabetically - by-trait - custom-directives - - -Basics ------- - -:ref:`Routes-java` effectively are simply highly specialised functions that take a ``RequestContext`` and eventually ``complete`` it, -which could (and often should) happen asynchronously. - -With the :ref:`-complete-java-` directive this becomes even shorter:: - - Route route = complete("yeah"); - -Writing multiple routes that are tried as alternatives (in-order of definition), is as simple as using the ``route(route1, route2)``, -method:: - - Route routes = route( - pathSingleSlash(() -> - getFromResource("web/calculator.html") - ), - path("hello", () -> complete("World!)) - ); - - -You could also simply define a "catch all" completion by providing it as the last route to attempt to match. -In the example below we use the ``get()`` (one of the :ref:`MethodDirectives-java`) to match all incoming ``GET`` -requests for that route, and all other requests will be routed towards the other "catch all" route, that completes the route:: - - Route route = - get( - () -> complete("Received GET") - ).orElse( - () -> complete("Received something else") - ) - - -If no route matches a given request, a default ``404 Not Found`` response will be returned as response. - -Structure ---------- - -The general anatomy of a directive is as follows:: - - directiveName(arguments [, ...], (extractions [, ...]) -> { - ... // inner route - }) - -It has a name, zero or more arguments and optionally an inner route (The :ref:`RouteDirectives-java` are special in that they -are always used at the leaf-level and as such cannot have inner routes). - -Additionally directives can "extract" a number of values and make them available to their inner routes as function -arguments. When seen "from the outside" a directive with its inner route form an expression of type ``Route``. - - -What Directives do ------------------- - -A directive can do one or more of the following: - -.. rst-class:: wide - -* Transform the incoming ``RequestContext`` before passing it on to its inner route (i.e. modify the request) -* Filter the ``RequestContext`` according to some logic, i.e. only pass on certain requests and reject others -* Extract values from the ``RequestContext`` and make them available to its inner route as "extractions" -* Chain some logic into the :class:`RouteResult` future transformation chain (i.e. modify the response or rejection) -* Complete the request - -This means a ``Directive`` completely wraps the functionality of its inner route and can apply arbitrarily complex -transformations, both (or either) on the request and on the response side. - - -Composing Directives --------------------- - -TODO rewrite for Java API diff --git a/akka-docs/rst/java/http/routing-dsl/directives/marshalling-directives/completeWith.rst b/akka-docs/rst/java/http/routing-dsl/directives/marshalling-directives/completeWith.rst deleted file mode 100644 index e272cbac19..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/marshalling-directives/completeWith.rst +++ /dev/null @@ -1,34 +0,0 @@ -.. _-completeWith-java-: - -completeWith -============ - -Description ------------ -Uses the marshaller for a given type to produce a completion function that is passed to its -inner route. You can use it to decouple marshaller resolution from request completion. - -The ``completeWith`` directive works in conjuction with ``instanceOf`` and ``spray.httpx.marshalling`` -to convert higher-level (object) structure into some lower-level serialized "wire format". -:ref:`The marshalling documentation ` explains this process in detail. -This directive simplifies exposing types to clients via a route while providing some -form of access to the current context. - -``completeWith`` is similar to ``handleWith``. The main difference is with ``completeWith`` you must eventually call -the completion function generated by ``completeWith``. ``handleWith`` will automatically call ``complete`` when the -``handleWith`` function returns. - -Examples --------- - -The following example uses ``spray-json`` to marshall a simple ``Person`` class to a json -response. It utilizes ``SprayJsonSupport`` via the ``PersonJsonSupport`` object as the in-scope -unmarshaller. - -TODO: Add example snippets - -The ``findPerson`` takes an argument of type ``Person => Unit`` which is generated by the ``completeWith`` -call. We can handle any logic we want in ``findPerson`` and call our completion function to -complete the request. - -TODO: Example snippets for JavaDSL are subject to community contributions! Help us complete the docs, read more about it here: `write example snippets for Akka HTTP Java DSL #20466 `_. diff --git a/akka-docs/rst/java/http/routing-dsl/directives/marshalling-directives/entity.rst b/akka-docs/rst/java/http/routing-dsl/directives/marshalling-directives/entity.rst deleted file mode 100644 index c4fb9b2caf..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/marshalling-directives/entity.rst +++ /dev/null @@ -1,43 +0,0 @@ -.. _-entity-java-: - -entity -====== - -Description ------------ -Unmarshalls the request entity to the given type and passes it to its inner Route. An unmarshaller -returns an ``Either`` with ``Right(value)`` if successful or ``Left(exception)`` for a failure. -The ``entity`` method will either pass the ``value`` to the inner route or map the ``exception`` to a -:class:``akka.http.javadsl.server.Rejection``. - -The ``entity`` directive works in conjuction with ``as`` and ``akka.http.scaladsl.unmarshalling`` to -convert some serialized "wire format" value into a higher-level object structure. -:ref:`The unmarshalling documentation ` explains this process in detail. -This directive simplifies extraction and error handling to the specified type from the request. - -An unmarshaller will return a ``Left(exception)`` in the case of an error. This is converted to a -``akka.http.scaladsl.server.Rejection`` within the ``entity`` directive. The following table lists how exceptions -are mapped to rejections: - -========================== ============ -Left(exception) Rejection --------------------------- ------------ -``ContentExpected`` ``RequestEntityExpectedRejection`` -``UnsupportedContentType`` ``UnsupportedRequestContentTypeRejection``, which lists the supported types -``MaformedContent`` ``MalformedRequestContentRejection``, with an error message and cause -========================== ============ - -Examples --------- - -The following example uses ``spray-json`` to unmarshall a json request into a simple ``Person`` -class. It utilizes ``SprayJsonSupport`` via the ``PersonJsonSupport`` object as the in-scope unmarshaller. - -TODO: Add example snippets. - -It is also possible to use the ``entity`` directive to obtain raw ``JsValue`` ( spray-json_ ) objects, by simply using -``as[JsValue]``, or any other JSON type for which you have marshallers in-scope. - -TODO: Example snippets for JavaDSL are subject to community contributions! Help us complete the docs, read more about it here: `write example snippets for Akka HTTP Java DSL #20466 `_. - -.. _spray-json: https://github.com/spray/spray-json diff --git a/akka-docs/rst/java/http/routing-dsl/directives/marshalling-directives/handleWith.rst b/akka-docs/rst/java/http/routing-dsl/directives/marshalling-directives/handleWith.rst deleted file mode 100644 index 979ab3c00f..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/marshalling-directives/handleWith.rst +++ /dev/null @@ -1,37 +0,0 @@ -.. _-handleWith-java-: - -handleWith -========== - -Description ------------ -Completes the request using the given function. The input to the function is produced with -the in-scope entity unmarshaller and the result value of the function is marshalled with -the in-scope marshaller. ``handleWith`` can be a convenient method combining ``entity`` with -``complete``. - -The ``handleWith`` directive is used when you want to handle a route with a given function of -type A ⇒ B. ``handleWith`` will use both an in-scope unmarshaller to convert a request into -type A and an in-scope marshaller to convert type B into a response. This is helpful when your -core business logic resides in some other class or you want your business logic to be independent -of the REST interface written with akka-http. You can use ``handleWith`` to "hand off" processing -to a given function without requiring any akka-http-specific functionality. - -``handleWith`` is similar to ``produce``. The main difference is ``handleWith`` automatically -calls ``complete`` when the function passed to ``handleWith`` returns. Using ``produce`` you -must explicity call the completion function passed from the ``produce`` function. - -See :ref:`marshalling ` and :ref:`unmarshalling ` for guidance -on marshalling entities with akka-http. - -Examples --------- - -The following example uses an ``updatePerson`` function with a ``Person`` case class as an input and output. We plug this function into our route using ``handleWith``. -TODO: Example snippets for JavaDSL are subject to community contributions! Help us complete the docs, read more about it here: `write example snippets for Akka HTTP Java DSL #20466 `_. - -TODO: Example snippets for JavaDSL are subject to community contributions! Help us complete the docs, read more about it here: `write example snippets for Akka HTTP Java DSL #20466 `_. - -The PersonJsonSupport object handles both marshalling and unmarshalling of the Person case class. - -TODO: Example snippets for JavaDSL are subject to community contributions! Help us complete the docs, read more about it here: `write example snippets for Akka HTTP Java DSL #20466 `_. diff --git a/akka-docs/rst/java/http/routing-dsl/directives/marshalling-directives/index.rst b/akka-docs/rst/java/http/routing-dsl/directives/marshalling-directives/index.rst deleted file mode 100644 index 013bc9613c..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/marshalling-directives/index.rst +++ /dev/null @@ -1,34 +0,0 @@ -.. _MarshallingDirectives-java: - -Marshalling Directives -====================== - -Marshalling directives work in conjunction with ``akka.http.scaladsl.marshalling`` and ``akka.http.scaladsl.unmarshalling`` to convert -a request entity to a specific type or a type to a response. - -See :ref:`marshalling ` and :ref:`unmarshalling ` for specific -serialization (also known as pickling) guidance. - -Marshalling directives usually rely on an in-scope implicit marshaller to handle conversion. - -.. toctree:: - :maxdepth: 1 - - completeWith - entity - handleWith - -Understanding Specific Marshalling Directives ---------------------------------------------- - -======================================= ======================================= -directive behavior -======================================= ======================================= -:ref:`-completeWith-java-` Uses a marshaller for a given type to produce a completion function for an inner route. Used in conjuction with *instanceOf* to format responses. -:ref:`-entity-java-` Unmarshalls the request entity to the given type and passes it to its inner route. Used in conjection with *as* to convert requests to objects. -:ref:`-handleWith-java-` Completes a request with a given function, using an in-scope unmarshaller for an input and in-scope marshaller for the output. -======================================= ======================================= - - - - diff --git a/akka-docs/rst/java/http/routing-dsl/directives/method-directives/delete.rst b/akka-docs/rst/java/http/routing-dsl/directives/method-directives/delete.rst deleted file mode 100644 index 205881387d..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/method-directives/delete.rst +++ /dev/null @@ -1,19 +0,0 @@ -.. _-delete-java-: - -delete -====== -Matches requests with HTTP method ``DELETE``. - -Description ------------ - -This directive filters an incoming request by its HTTP method. Only requests with -method ``DELETE`` are passed on to the inner route. All others are rejected with a -``MethodRejection``, which is translated into a ``405 Method Not Allowed`` response -by the default ``RejectionHandler``. - - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/MethodDirectivesExamplesTest.java#delete diff --git a/akka-docs/rst/java/http/routing-dsl/directives/method-directives/extractMethod.rst b/akka-docs/rst/java/http/routing-dsl/directives/method-directives/extractMethod.rst deleted file mode 100644 index 042f028dd5..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/method-directives/extractMethod.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. _-extractMethod-java-: - -extractMethod -============= - -Description ------------ - -Extracts the :class:`HttpMethod` from the request context and provides it for use for other directives explicitly. - -Example -------- - -In the below example our route first matches all ``GET`` requests, and if an incoming request wasn't a ``GET``, -the matching continues and the extractMethod route will be applied which we can use to programatically -print what type of request it was - independent of what actual HttpMethod it was: - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/MethodDirectivesExamplesTest.java#extractMethod - -Custom Http Method ------------------- - -When you define a custom HttpMethod, you can define a route using extractMethod. - - .. includecode:: ../../../../code/docs/http/javadsl/server/directives/CustomHttpMethodExamplesTest.java#customHttpMethod diff --git a/akka-docs/rst/java/http/routing-dsl/directives/method-directives/get.rst b/akka-docs/rst/java/http/routing-dsl/directives/method-directives/get.rst deleted file mode 100644 index 6113d28bfa..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/method-directives/get.rst +++ /dev/null @@ -1,19 +0,0 @@ -.. _-get-java-: - -get -=== -Matches requests with HTTP method ``GET``. - -Description ------------ - -This directive filters the incoming request by its HTTP method. Only requests with -method ``GET`` are passed on to the inner route. All others are rejected with a -``MethodRejection``, which is translated into a ``405 Method Not Allowed`` response -by the default ``RejectionHandler``. - - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/MethodDirectivesExamplesTest.java#get diff --git a/akka-docs/rst/java/http/routing-dsl/directives/method-directives/head.rst b/akka-docs/rst/java/http/routing-dsl/directives/method-directives/head.rst deleted file mode 100644 index ff7419303b..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/method-directives/head.rst +++ /dev/null @@ -1,22 +0,0 @@ -.. _-head-java-: - -head -==== -Matches requests with HTTP method ``HEAD``. - -Description ------------ - -This directive filters the incoming request by its HTTP method. Only requests with -method ``HEAD`` are passed on to the inner route. All others are rejected with a -``MethodRejection``, which is translated into a ``405 Method Not Allowed`` response -by the default ``RejectionHandler``. - -.. note:: By default, akka-http handles HEAD-requests transparently by dispatching a GET-request to the handler and - stripping of the result body. See the ``akka.http.server.transparent-head-requests`` setting for how to disable - this behavior. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/MethodDirectivesExamplesTest.java#head diff --git a/akka-docs/rst/java/http/routing-dsl/directives/method-directives/index.rst b/akka-docs/rst/java/http/routing-dsl/directives/method-directives/index.rst deleted file mode 100644 index 10b6776de0..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/method-directives/index.rst +++ /dev/null @@ -1,18 +0,0 @@ -.. _MethodDirectives-java: - -MethodDirectives -================ - -.. toctree:: - :maxdepth: 1 - - delete - extractMethod - get - head - method - options - overrideMethodWithParameter - patch - post - put diff --git a/akka-docs/rst/java/http/routing-dsl/directives/method-directives/method.rst b/akka-docs/rst/java/http/routing-dsl/directives/method-directives/method.rst deleted file mode 100644 index 6ce7afd472..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/method-directives/method.rst +++ /dev/null @@ -1,19 +0,0 @@ -.. _-method-java-: - -method -====== - -Matches HTTP requests based on their method. - -Description ------------ - -This directive filters the incoming request by its HTTP method. Only requests with -the specified method are passed on to the inner route. All others are rejected with a -``MethodRejection``, which is translated into a ``405 Method Not Allowed`` response -by the default ``RejectionHandler``. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/MethodDirectivesExamplesTest.java#method-example diff --git a/akka-docs/rst/java/http/routing-dsl/directives/method-directives/options.rst b/akka-docs/rst/java/http/routing-dsl/directives/method-directives/options.rst deleted file mode 100644 index c20e71d950..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/method-directives/options.rst +++ /dev/null @@ -1,18 +0,0 @@ -.. _-options-java-: - -options -======= -Matches requests with HTTP method ``OPTIONS``. - -Description ------------ - -This directive filters the incoming request by its HTTP method. Only requests with -method ``OPTIONS`` are passed on to the inner route. All others are rejected with a -``MethodRejection``, which is translated into a ``405 Method Not Allowed`` response -by the default ``RejectionHandler``. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/MethodDirectivesExamplesTest.java#options diff --git a/akka-docs/rst/java/http/routing-dsl/directives/method-directives/overrideMethodWithParameter.rst b/akka-docs/rst/java/http/routing-dsl/directives/method-directives/overrideMethodWithParameter.rst deleted file mode 100644 index ee709eac90..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/method-directives/overrideMethodWithParameter.rst +++ /dev/null @@ -1,23 +0,0 @@ -.. _-overrideMethodWithParameter-java-: - -overrideMethodWithParameter -=========================== - -Changes the HTTP method of the request to the value of the specified query string parameter. - -Description ------------ - -If the query string parameter is not specified this directive has no effect. If the query string is specified as something that is not -a HTTP method, then this directive completes the request with a `501 Not Implemented` response. - - -This directive is useful for: - -- Use in combination with JSONP (JSONP only supports GET) -- Supporting older browsers that lack support for certain HTTP methods. E.g. IE8 does not support PATCH - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/MethodDirectivesExamplesTest.java#overrideMethodWithParameter diff --git a/akka-docs/rst/java/http/routing-dsl/directives/method-directives/patch.rst b/akka-docs/rst/java/http/routing-dsl/directives/method-directives/patch.rst deleted file mode 100644 index 6137df7460..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/method-directives/patch.rst +++ /dev/null @@ -1,20 +0,0 @@ -.. _-patch-java-: - -patch -===== - -Matches requests with HTTP method ``PATCH``. - -Description ------------ - -This directive filters the incoming request by its HTTP method. Only requests with -method ``PATCH`` are passed on to the inner route. All others are rejected with a -``MethodRejection``, which is translated into a ``405 Method Not Allowed`` response -by the default ``RejectionHandler``. - - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/MethodDirectivesExamplesTest.java#patch diff --git a/akka-docs/rst/java/http/routing-dsl/directives/method-directives/post.rst b/akka-docs/rst/java/http/routing-dsl/directives/method-directives/post.rst deleted file mode 100644 index 30ce625fac..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/method-directives/post.rst +++ /dev/null @@ -1,20 +0,0 @@ -.. _-post-java-: - -post -==== - -Matches requests with HTTP method ``POST``. - -Description ------------ - -This directive filters the incoming request by its HTTP method. Only requests with -method ``POST`` are passed on to the inner route. All others are rejected with a -``MethodRejection``, which is translated into a ``405 Method Not Allowed`` response -by the default ``RejectionHandler``. - - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/MethodDirectivesExamplesTest.java#post diff --git a/akka-docs/rst/java/http/routing-dsl/directives/method-directives/put.rst b/akka-docs/rst/java/http/routing-dsl/directives/method-directives/put.rst deleted file mode 100644 index 2ae9d994a8..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/method-directives/put.rst +++ /dev/null @@ -1,19 +0,0 @@ -.. _-put-java-: - -put -=== - -Matches requests with HTTP method ``PUT``. - -Description ------------ - -This directive filters the incoming request by its HTTP method. Only requests with -method ``PUT`` are passed on to the inner route. All others are rejected with a -``MethodRejection``, which is translated into a ``405 Method Not Allowed`` response -by the default ``RejectionHandler``. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/MethodDirectivesExamplesTest.java#put diff --git a/akka-docs/rst/java/http/routing-dsl/directives/misc-directives/extractClientIP.rst b/akka-docs/rst/java/http/routing-dsl/directives/misc-directives/extractClientIP.rst deleted file mode 100644 index e6fdd3db9e..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/misc-directives/extractClientIP.rst +++ /dev/null @@ -1,16 +0,0 @@ -.. _-extractClientIP-java-: - -extractClientIP -=============== - -Description ------------ -Provides the value of ``X-Forwarded-For``, ``Remote-Address``, or ``X-Real-IP`` headers as an instance of ``HttpIp``. - -The akka-http server engine adds the ``Remote-Address`` header to every request automatically if the respective -setting ``akka.http.server.remote-address-header`` is set to ``on``. Per default it is set to ``off``. - -Example -------- -TODO: Example snippets for JavaDSL are subject to community contributions! Help us complete the docs, read more about it here: `write example snippets for Akka HTTP Java DSL #20466 `_. - diff --git a/akka-docs/rst/java/http/routing-dsl/directives/misc-directives/index.rst b/akka-docs/rst/java/http/routing-dsl/directives/misc-directives/index.rst deleted file mode 100644 index 997731d05b..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/misc-directives/index.rst +++ /dev/null @@ -1,16 +0,0 @@ -.. _MiscDirectives-java: - -MiscDirectives -============== - -.. toctree:: - :maxdepth: 1 - - extractClientIP - rejectEmptyResponse - requestEntityEmpty - requestEntityPresent - selectPreferredLanguage - validate - withoutSizeLimit - withSizeLimit diff --git a/akka-docs/rst/java/http/routing-dsl/directives/misc-directives/rejectEmptyResponse.rst b/akka-docs/rst/java/http/routing-dsl/directives/misc-directives/rejectEmptyResponse.rst deleted file mode 100644 index d1d5bb9556..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/misc-directives/rejectEmptyResponse.rst +++ /dev/null @@ -1,16 +0,0 @@ -.. _-rejectEmptyResponse-java-: - -rejectEmptyResponse -=================== - -Description ------------ -Replaces a response with no content with an empty rejection. - -The ``rejectEmptyResponse`` directive is mostly used with marshalling ``Option[T]`` instances. The value ``None`` is -usually marshalled to an empty but successful result. In many cases ``None`` should instead be handled as -``404 Not Found`` which is the effect of using ``rejectEmptyResponse``. - -Example -------- -TODO: Example snippets for JavaDSL are subject to community contributions! Help us complete the docs, read more about it here: `write example snippets for Akka HTTP Java DSL #20466 `_. diff --git a/akka-docs/rst/java/http/routing-dsl/directives/misc-directives/requestEntityEmpty.rst b/akka-docs/rst/java/http/routing-dsl/directives/misc-directives/requestEntityEmpty.rst deleted file mode 100644 index fe59040063..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/misc-directives/requestEntityEmpty.rst +++ /dev/null @@ -1,17 +0,0 @@ -.. _-requestEntityEmpty-java-: - -requestEntityEmpty -================== - -Description ------------ -A filter that checks if the request entity is empty and only then passes processing to the inner route. -Otherwise, the request is rejected. - - -See also :ref:`-requestEntityPresent-java-` for the opposite effect. - - -Example -------- -TODO: Example snippets for JavaDSL are subject to community contributions! Help us complete the docs, read more about it here: `write example snippets for Akka HTTP Java DSL #20466 `_. diff --git a/akka-docs/rst/java/http/routing-dsl/directives/misc-directives/requestEntityPresent.rst b/akka-docs/rst/java/http/routing-dsl/directives/misc-directives/requestEntityPresent.rst deleted file mode 100644 index 44ec6e4130..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/misc-directives/requestEntityPresent.rst +++ /dev/null @@ -1,16 +0,0 @@ -.. _-requestEntityPresent-java-: - -requestEntityPresent -==================== - -Description ------------ -A simple filter that checks if the request entity is present and only then passes processing to the inner route. -Otherwise, the request is rejected. - -See also :ref:`-requestEntityEmpty-java-` for the opposite effect. - - -Example -------- -TODO: Example snippets for JavaDSL are subject to community contributions! Help us complete the docs, read more about it here: `write example snippets for Akka HTTP Java DSL #20466 `_. diff --git a/akka-docs/rst/java/http/routing-dsl/directives/misc-directives/selectPreferredLanguage.rst b/akka-docs/rst/java/http/routing-dsl/directives/misc-directives/selectPreferredLanguage.rst deleted file mode 100644 index 1a277e6c16..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/misc-directives/selectPreferredLanguage.rst +++ /dev/null @@ -1,17 +0,0 @@ -.. _-selectPreferredLanguage-java-: - -selectPreferredLanguage -======================= - -Description ------------ -Inspects the request's ``Accept-Language`` header and determines, -which of a given set of language alternatives is preferred by the client according to content negotiation rules -defined by http://tools.ietf.org/html/rfc7231#section-5.3.5. - -If there are several best language alternatives that the client has equal preference for -(even if this preference is zero!) the order of the arguments is used as a tie breaker (first one wins). - -Example -------- -TODO: Example snippets for JavaDSL are subject to community contributions! Help us complete the docs, read more about it here: `write example snippets for Akka HTTP Java DSL #20466 `_. diff --git a/akka-docs/rst/java/http/routing-dsl/directives/misc-directives/validate.rst b/akka-docs/rst/java/http/routing-dsl/directives/misc-directives/validate.rst deleted file mode 100644 index f91dd642bb..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/misc-directives/validate.rst +++ /dev/null @@ -1,14 +0,0 @@ -.. _-validate-java-: - -validate -======== -Allows validating a precondition before handling a route. - -Description ------------ -Checks an arbitrary condition and passes control to the inner route if it returns ``true``. -Otherwise, rejects the request with a ``ValidationRejection`` containing the given error message. - -Example -------- -TODO: Example snippets for JavaDSL are subject to community contributions! Help us complete the docs, read more about it here: `write example snippets for Akka HTTP Java DSL #20466 `_. diff --git a/akka-docs/rst/java/http/routing-dsl/directives/misc-directives/withSizeLimit.rst b/akka-docs/rst/java/http/routing-dsl/directives/misc-directives/withSizeLimit.rst deleted file mode 100644 index faef2f3ebd..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/misc-directives/withSizeLimit.rst +++ /dev/null @@ -1,20 +0,0 @@ -.. _-withSizeLimit-java-: - -withSizeLimit -=============== - -Description ------------ -Fails the stream with ``EntityStreamSizeException`` if its request entity size exceeds given limit. Limit given -as parameter overrides limit configured with ``akka.http.parsing.max-content-length``. - -The whole mechanism of entity size checking is intended to prevent certain Denial-of-Service attacks. -So suggested setup is to have ``akka.http.parsing.max-content-length`` relatively low and use ``withSizeLimit`` -directive for endpoints which expects bigger entities. - -See also :ref:`-withoutSizeLimit-java-` for skipping request entity size check. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/MiscDirectivesExamplesTest.java#withSizeLimitExample diff --git a/akka-docs/rst/java/http/routing-dsl/directives/misc-directives/withoutSizeLimit.rst b/akka-docs/rst/java/http/routing-dsl/directives/misc-directives/withoutSizeLimit.rst deleted file mode 100644 index de16f30131..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/misc-directives/withoutSizeLimit.rst +++ /dev/null @@ -1,19 +0,0 @@ -.. _-withoutSizeLimit-java-: - -withoutSizeLimit -================ - -Description ------------ -Skips request entity size verification. - -The whole mechanism of entity size checking is intended to prevent certain Denial-of-Service attacks. -So suggested setup is to have ``akka.http.parsing.max-content-length`` relatively low and use ``withoutSizeLimit`` -directive just for endpoints for which size verification should not be performed. - -See also :ref:`-withSizeLimit-java-` for setting request entity size limit. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/MiscDirectivesExamplesTest.java#withSizeLimitExample diff --git a/akka-docs/rst/java/http/routing-dsl/directives/parameter-directives/index.rst b/akka-docs/rst/java/http/routing-dsl/directives/parameter-directives/index.rst deleted file mode 100644 index abc97ca249..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/parameter-directives/index.rst +++ /dev/null @@ -1,45 +0,0 @@ -.. _ParameterDirectives-java: - -ParameterDirectives -=================== - -.. toctree:: - :maxdepth: 1 - - parameter - parameters - parameterMap - parameterMultiMap - parameterSeq - -.. _which-parameter-directive-java: - -When to use which parameter directive? --------------------------------------- - -Usually, you want to use the high-level :ref:`-parameter-java-` directive. When you need -more low-level access you can use the table below to decide which directive -to use which shows properties of different parameter directives. - -================================ ====== ======== ===== -directive level ordering multi -================================ ====== ======== ===== -:ref:`-parameter-java-` high no no -:ref:`-parameterMap-java-` low no no -:ref:`-parameterMultiMap-java-` low no yes -:ref:`-parameterList-java-` low yes yes -================================ ====== ======== ===== - -level - high-level parameter directives extract subset of all parameters by name and allow conversions - and automatically report errors if expectations are not met, low-level directives give you - all parameters at once, leaving all further processing to you - -ordering - original ordering from request URL is preserved - -multi - multiple values per parameter name are possible - -.. note:: - If you need to extract multiple parameters, apply the ``parameter`` directive multiple times. diff --git a/akka-docs/rst/java/http/routing-dsl/directives/parameter-directives/parameter.rst b/akka-docs/rst/java/http/routing-dsl/directives/parameter-directives/parameter.rst deleted file mode 100644 index 76ad0cf33d..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/parameter-directives/parameter.rst +++ /dev/null @@ -1,16 +0,0 @@ -.. _-parameter-java-: - -parameter -========= -Extracts a *query* parameter value from the request. - -Description ------------ -See :ref:`-parameter-java-` for a detailed description of this directive. - -See :ref:`which-parameter-directive-java` to understand when to use which directive. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/ParameterDirectivesExamplesTest.java#parameter diff --git a/akka-docs/rst/java/http/routing-dsl/directives/parameter-directives/parameterMap.rst b/akka-docs/rst/java/http/routing-dsl/directives/parameter-directives/parameterMap.rst deleted file mode 100644 index 0001549794..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/parameter-directives/parameterMap.rst +++ /dev/null @@ -1,16 +0,0 @@ -.. _-parameterMap-java-: - -parameterMap -============ -Extracts all parameters at once as a ``Map`` mapping parameter names to parameter values. - -Description ------------ -If a query contains a parameter value several times, the map will contain the last one. - -See also :ref:`which-parameter-directive-java` to understand when to use which directive. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/ParameterDirectivesExamplesTest.java#parameterMap diff --git a/akka-docs/rst/java/http/routing-dsl/directives/parameter-directives/parameterMultiMap.rst b/akka-docs/rst/java/http/routing-dsl/directives/parameter-directives/parameterMultiMap.rst deleted file mode 100644 index 124272a93f..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/parameter-directives/parameterMultiMap.rst +++ /dev/null @@ -1,21 +0,0 @@ -.. _-parameterMultiMap-java-: - -parameterMultiMap -================= - -Description ------------ - -Extracts all parameters at once as a multi-map of type ``Map>`` mapping -a parameter name to a list of all its values. - -This directive can be used if parameters can occur several times. - -The order of values is *not* specified. - -See :ref:`which-parameter-directive-java` to understand when to use which directive. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/ParameterDirectivesExamplesTest.java#parameterMultiMap diff --git a/akka-docs/rst/java/http/routing-dsl/directives/parameter-directives/parameterSeq.rst b/akka-docs/rst/java/http/routing-dsl/directives/parameter-directives/parameterSeq.rst deleted file mode 100644 index c767389cf7..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/parameter-directives/parameterSeq.rst +++ /dev/null @@ -1,17 +0,0 @@ -.. _-parameterList-java-: - -parameterList -============= - -Description ------------ -Extracts all parameters at once in the original order as (name, value) tuples of type ``Map.Entry``. - -This directive can be used if the exact order of parameters is important or if parameters can occur several times. - -See :ref:`which-parameter-directive-java` to understand when to use which directive. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/ParameterDirectivesExamplesTest.java#parameterSeq diff --git a/akka-docs/rst/java/http/routing-dsl/directives/parameter-directives/parameters.rst b/akka-docs/rst/java/http/routing-dsl/directives/parameter-directives/parameters.rst deleted file mode 100644 index f451a63e91..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/parameter-directives/parameters.rst +++ /dev/null @@ -1,15 +0,0 @@ -.. _-parameters-java-: - -parameters -========== -Extracts multiple *query* parameter values from the request. - -Description ------------ - -See :ref:`which-parameter-directive-java` to understand when to use which directive. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/ParameterDirectivesExamplesTest.java#parameters diff --git a/akka-docs/rst/java/http/routing-dsl/directives/path-directives.rst b/akka-docs/rst/java/http/routing-dsl/directives/path-directives.rst deleted file mode 100644 index cf2e3f0d29..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/path-directives.rst +++ /dev/null @@ -1,94 +0,0 @@ -.. _path-directives-java: - -PathDirectives -============== - -Path directives are the most basic building blocks for routing requests depending on the URI path. - -When a request (or rather the respective ``RequestContext`` instance) enters the route structure it has an -"unmatched path" that is identical to the ``request.uri.path``. As it descends the routing tree and passes through one -or more ``pathPrefix`` or ``path`` directives the "unmatched path" progressively gets "eaten into" from the -left until, in most cases, it eventually has been consumed completely. - -The two main directives are ``path`` and ``pathPrefix``. The ``path`` directive tries to match the complete remaining -unmatched path against the specified "path matchers", the ``pathPrefix`` directive only matches a prefix and passes the -remaining unmatched path to nested directives. Both directives automatically match a slash from the beginning, so -that matching slashes in a hierarchy of nested ``pathPrefix`` and ``path`` directives is usually not needed. - -Path directives take a variable amount of arguments. Each argument must be a ``PathMatcher`` or a string (which is -automatically converted to a path matcher using ``PathMatchers.segment``). In the case of ``path`` and ``pathPrefix``, -if multiple arguments are supplied, a slash is assumed between any of the supplied path matchers. The ``rawPathX`` -variants of those directives on the other side do no such preprocessing, so that slashes must be matched manually. - -Path Matchers -------------- - -A path matcher is a description of a part of a path to match. The simplest path matcher is ``PathMatcher.segment`` which -matches exactly one path segment against the supplied constant string. - -Other path matchers defined in ``PathMatchers`` match the end of the path (``PathMatchers.END``), a single slash -(``PathMatchers.SLASH``), or nothing at all (``PathMatchers.NEUTRAL``). - -Many path matchers are hybrids that can both match (by using them with one of the PathDirectives) and extract values, -Extracting a path matcher value (i.e. using it with ``handleWithX``) is only allowed if it nested inside a path -directive that uses that path matcher and so specifies at which position the value should be extracted from the path. - -Predefined path matchers allow extraction of various types of values: - -``PathMatchers.segment(String)`` - Strings simply match themselves and extract no value. - Note that strings are interpreted as the decoded representation of the path, so if they include a '/' character - this character will match "%2F" in the encoded raw URI! - -``PathMatchers.regex`` - You can use a regular expression instance as a path matcher, which matches whatever the regex matches and extracts - one ``String`` value. A ``PathMatcher`` created from a regular expression extracts either the complete match (if the - regex doesn't contain a capture group) or the capture group (if the regex contains exactly one capture group). - If the regex contains more than one capture group an ``IllegalArgumentException`` will be thrown. - -``PathMatchers.SLASH`` - Matches exactly one path-separating slash (``/``) character. - -``PathMatchers.END`` - Matches the very end of the path, similar to ``$`` in regular expressions. - -``PathMatchers.Segment`` - Matches if the unmatched path starts with a path segment (i.e. not a slash). - If so the path segment is extracted as a ``String`` instance. - -``PathMatchers.Remaining`` - Matches and extracts the complete remaining unmatched part of the request's URI path as an (encoded!) String. - If you need access to the remaining *decoded* elements of the path use ``RemainingPath`` instead. - -``PathMatchers.intValue`` - Efficiently matches a number of decimal digits (unsigned) and extracts their (non-negative) ``Int`` value. The matcher - will not match zero digits or a sequence of digits that would represent an ``Int`` value larger than ``Integer.MAX_VALUE``. - -``PathMatchers.longValue`` - Efficiently matches a number of decimal digits (unsigned) and extracts their (non-negative) ``Long`` value. The matcher - will not match zero digits or a sequence of digits that would represent an ``Long`` value larger than ``Long.MAX_VALUE``. - -``PathMatchers.hexIntValue`` - Efficiently matches a number of hex digits and extracts their (non-negative) ``Int`` value. The matcher will not match - zero digits or a sequence of digits that would represent an ``Int`` value larger than ``Integer.MAX_VALUE``. - -``PathMatchers.hexLongValue`` - Efficiently matches a number of hex digits and extracts their (non-negative) ``Long`` value. The matcher will not - match zero digits or a sequence of digits that would represent an ``Long`` value larger than ``Long.MAX_VALUE``. - -``PathMatchers.uuid`` - Matches and extracts a ``java.util.UUID`` instance. - -``PathMatchers.NEUTRAL`` - A matcher that always matches, doesn't consume anything and extracts nothing. - Serves mainly as a neutral element in ``PathMatcher`` composition. - -``PathMatchers.segments`` - Matches all remaining segments as a list of strings. Note that this can also be "no segments" resulting in the empty - list. If the path has a trailing slash this slash will *not* be matched, i.e. remain unmatched and to be consumed by - potentially nested directives. - -Here's a collection of path matching examples: - -.. includecode:: ../../../code/docs/http/javadsl/server/PathDirectiveExampleTest.java - :include: path-examples \ No newline at end of file diff --git a/akka-docs/rst/java/http/routing-dsl/directives/path-directives/index.rst b/akka-docs/rst/java/http/routing-dsl/directives/path-directives/index.rst deleted file mode 100644 index 842b211064..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/path-directives/index.rst +++ /dev/null @@ -1,21 +0,0 @@ -.. _PathDirectives-java: - -PathDirectives -============== - -.. toctree:: - :maxdepth: 1 - - path - pathEnd - pathEndOrSingleSlash - pathPrefix - pathPrefixTest - pathSingleSlash - pathSuffix - pathSuffixTest - rawPathPrefix - rawPathPrefixTest - redirectToNoTrailingSlashIfPresent - redirectToTrailingSlashIfMissing - ../path-directives diff --git a/akka-docs/rst/java/http/routing-dsl/directives/path-directives/path.rst b/akka-docs/rst/java/http/routing-dsl/directives/path-directives/path.rst deleted file mode 100644 index 67cb583dcd..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/path-directives/path.rst +++ /dev/null @@ -1,34 +0,0 @@ -.. _-path-java-: - -path -==== - -Description ------------ -Matches the complete unmatched path of the ``RequestContext`` against the given ``PathMatcher``, potentially extracts -one or more values (depending on the type of the argument). - -This directive filters incoming requests based on the part of their URI that hasn't been matched yet by other -potentially existing :ref:`-pathPrefix-java-` directives on higher levels of the routing structure. -Its one parameter is usually an expression evaluating to a ``PathMatcher`` instance (see also: :ref:`pathmatcher-dsl`). - -As opposed to the :ref:`-rawPathPrefix-java-` or :ref:`-rawPathPrefixTest-java-` directives ``path`` automatically adds a leading -slash to its ``PathMatcher`` argument, you therefore don't have to start your matching expression with an explicit slash. - -The ``path`` directive attempts to match the **complete** remaining path, not just a prefix. If you only want to match -a path prefix and then delegate further filtering to a lower level in your routing structure use the :ref:`-pathPrefix-java-` -directive instead. As a consequence it doesn't make sense to nest a ``path`` or :ref:`-pathPrefix-java-` directive -underneath another ``path`` directive, as there is no way that they will ever match (since the unmatched path underneath -a ``path`` directive will always be empty). - -Depending on the type of its ``PathMatcher`` argument the ``path`` directive extracts zero or more values from the URI. -If the match fails the request is rejected with an :ref:`empty rejection set `. - -.. note:: The empty string (also called empty word or identity) is a **neutral element** of string concatenation operation, - so it will match everything, but remember that ``path`` requires whole remaining path being matched, so (``/``) will succeed - and (``/whatever``) will fail. The :ref:`-pathPrefix-java-` provides more liberal behaviour. - - -Example -------- -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/PathDirectivesExamplesTest.java#path-dsl diff --git a/akka-docs/rst/java/http/routing-dsl/directives/path-directives/pathEnd.rst b/akka-docs/rst/java/http/routing-dsl/directives/path-directives/pathEnd.rst deleted file mode 100644 index a1d6207393..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/path-directives/pathEnd.rst +++ /dev/null @@ -1,18 +0,0 @@ -.. _-pathEnd-java-: - -pathEnd -======= - -Description ------------ -Only passes the request to its inner route if the unmatched path of the ``RequestContext`` is empty, i.e. the request -path has been fully matched by a higher-level :ref:`-path-java-` or :ref:`-pathPrefix-java-` directive. - - -This directive is a simple alias for ``rawPathPrefix(PathEnd)`` and is mostly used on an -inner-level to discriminate "path already fully matched" from other alternatives (see the example below). - - -Example -------- -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/PathDirectivesExamplesTest.java#path-end diff --git a/akka-docs/rst/java/http/routing-dsl/directives/path-directives/pathEndOrSingleSlash.rst b/akka-docs/rst/java/http/routing-dsl/directives/path-directives/pathEndOrSingleSlash.rst deleted file mode 100644 index 3d2818ec21..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/path-directives/pathEndOrSingleSlash.rst +++ /dev/null @@ -1,19 +0,0 @@ -.. _-pathEndOrSingleSlash-java-: - -pathEndOrSingleSlash -==================== - -Description ------------ -Only passes the request to its inner route if the unmatched path of the ``RequestContext`` is either empty -or contains only one single slash. - -This directive is a simple alias for ``rawPathPrefix(Slash.? ~ PathEnd)`` and is mostly used on an inner-level to -discriminate "path already fully matched" from other alternatives (see the example below). - -It is equivalent to ``pathEnd | pathSingleSlash`` but slightly more efficient. - - -Example -------- -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/PathDirectivesExamplesTest.java#path-end-or-single-slash diff --git a/akka-docs/rst/java/http/routing-dsl/directives/path-directives/pathPrefix.rst b/akka-docs/rst/java/http/routing-dsl/directives/path-directives/pathPrefix.rst deleted file mode 100644 index 8fe5670d59..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/path-directives/pathPrefix.rst +++ /dev/null @@ -1,27 +0,0 @@ -.. _-pathPrefix-java-: - -pathPrefix -========== - -Description ------------ -Matches and consumes a prefix of the unmatched path of the ``RequestContext`` against the given ``PathMatcher``, -potentially extracts one or more values (depending on the type of the argument). - -This directive filters incoming requests based on the part of their URI that hasn't been matched yet by other -potentially existing ``pathPrefix`` or :ref:`-rawPathPrefix-java-` directives on higher levels of the routing structure. -Its one parameter is usually an expression evaluating to a ``PathMatcher`` instance (see also: :ref:`pathmatcher-dsl`). - -As opposed to its :ref:`-rawPathPrefix-java-` counterpart ``pathPrefix`` automatically adds a leading slash to its -``PathMatcher`` argument, you therefore don't have to start your matching expression with an explicit slash. - -Depending on the type of its ``PathMatcher`` argument the ``pathPrefix`` directive extracts zero or more values from -the URI. If the match fails the request is rejected with an :ref:`empty rejection set `. - -.. note:: The empty string (also called empty word or identity) is a **neutral element** of string concatenation operation, - so it will match everything and consume nothing. The :ref:`-path-java-` provides more strict behaviour. - - -Example -------- -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/PathDirectivesExamplesTest.java#path-prefix diff --git a/akka-docs/rst/java/http/routing-dsl/directives/path-directives/pathPrefixTest.rst b/akka-docs/rst/java/http/routing-dsl/directives/path-directives/pathPrefixTest.rst deleted file mode 100644 index d1684857d4..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/path-directives/pathPrefixTest.rst +++ /dev/null @@ -1,27 +0,0 @@ -.. _-pathPrefixTest-java-: - -pathPrefixTest -============== - -Description ------------ -Checks whether the unmatched path of the ``RequestContext`` has a prefix matched by the given ``PathMatcher``. -Potentially extracts one or more values (depending on the type of the argument) but doesn't consume its match from -the unmatched path. - -This directive is very similar to the :ref:`-pathPrefix-java-` directive with the one difference that the path prefix -it matched (if it matched) is *not* consumed. The unmatched path of the ``RequestContext`` is therefore left as -is even in the case that the directive successfully matched and the request is passed on to its inner route. - -For more info on how to create a ``PathMatcher`` see :ref:`pathmatcher-dsl`. - -As opposed to its :ref:`-rawPathPrefixTest-java-` counterpart ``pathPrefixTest`` automatically adds a leading slash to its -``PathMatcher`` argument, you therefore don't have to start your matching expression with an explicit slash. - -Depending on the type of its ``PathMatcher`` argument the ``pathPrefixTest`` directive extracts zero or more values from -the URI. If the match fails the request is rejected with an :ref:`empty rejection set `. - - -Example -------- -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/PathDirectivesExamplesTest.java#path-prefix-test diff --git a/akka-docs/rst/java/http/routing-dsl/directives/path-directives/pathSingleSlash.rst b/akka-docs/rst/java/http/routing-dsl/directives/path-directives/pathSingleSlash.rst deleted file mode 100644 index 16a115282e..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/path-directives/pathSingleSlash.rst +++ /dev/null @@ -1,17 +0,0 @@ -.. _-pathSingleSlash-java-: - -pathSingleSlash -=============== - -Description ------------ -Only passes the request to its inner route if the unmatched path of the ``RequestContext`` -contains exactly one single slash. - -This directive is a simple alias for ``pathPrefix(PathEnd)`` and is mostly used for matching requests to the root URI -(``/``) on an inner-level to discriminate "all path segments matched" from other alternatives (see the example below). - - -Example -------- -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/PathDirectivesExamplesTest.java#path-single-slash diff --git a/akka-docs/rst/java/http/routing-dsl/directives/path-directives/pathSuffix.rst b/akka-docs/rst/java/http/routing-dsl/directives/path-directives/pathSuffix.rst deleted file mode 100644 index 3efcf9ef98..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/path-directives/pathSuffix.rst +++ /dev/null @@ -1,27 +0,0 @@ -.. _-pathSuffix-java-: - -pathSuffix -========== - -Description ------------ -Matches and consumes a suffix of the unmatched path of the ``RequestContext`` against the given ``PathMatcher``, -potentially extracts one or more values (depending on the type of the argument). - -This directive filters incoming requests based on the part of their URI that hasn't been matched yet by other -potentially existing path matching directives on higher levels of the routing structure. -Its one parameter is usually an expression evaluating to a ``PathMatcher`` instance (see also: :ref:`pathmatcher-dsl`). - -As opposed to :ref:`-pathPrefix-java-` this directive matches and consumes the unmatched path from the right, i.e. the end. - -.. caution:: For efficiency reasons, the given ``PathMatcher`` must match the desired suffix in reversed-segment - order, i.e. ``pathSuffix("baz" / "bar")`` would match ``/foo/bar/baz``! The order within a segment match is - not reversed. - -Depending on the type of its ``PathMatcher`` argument the ``pathPrefix`` directive extracts zero or more values from -the URI. If the match fails the request is rejected with an :ref:`empty rejection set `. - - -Example -------- -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/PathDirectivesExamplesTest.java#path-suffix diff --git a/akka-docs/rst/java/http/routing-dsl/directives/path-directives/pathSuffixTest.rst b/akka-docs/rst/java/http/routing-dsl/directives/path-directives/pathSuffixTest.rst deleted file mode 100644 index f0263e9bd9..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/path-directives/pathSuffixTest.rst +++ /dev/null @@ -1,28 +0,0 @@ -.. _-pathSuffixTest-java-: - -pathSuffixTest -============== - -Description ------------ -Checks whether the unmatched path of the ``RequestContext`` has a suffix matched by the given ``PathMatcher``. -Potentially extracts one or more values (depending on the type of the argument) but doesn't consume its match from -the unmatched path. - -This directive is very similar to the :ref:`-pathSuffix-java-` directive with the one difference that the path suffix -it matched (if it matched) is *not* consumed. The unmatched path of the ``RequestContext`` is therefore left as -is even in the case that the directive successfully matched and the request is passed on to its inner route. - -As opposed to :ref:`-pathPrefixTest-java-` this directive matches and consumes the unmatched path from the right, i.e. the end. - -.. caution:: For efficiency reasons, the given ``PathMatcher`` must match the desired suffix in reversed-segment - order, i.e. ``pathSuffixTest("baz" / "bar")`` would match ``/foo/bar/baz``! The order within a segment match is - not reversed. - -Depending on the type of its ``PathMatcher`` argument the ``pathSuffixTest`` directive extracts zero or more values from -the URI. If the match fails the request is rejected with an :ref:`empty rejection set `. - - -Example -------- -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/PathDirectivesExamplesTest.java#path-suffix-test diff --git a/akka-docs/rst/java/http/routing-dsl/directives/path-directives/rawPathPrefix.rst b/akka-docs/rst/java/http/routing-dsl/directives/path-directives/rawPathPrefix.rst deleted file mode 100644 index 78269a610b..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/path-directives/rawPathPrefix.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. _-rawPathPrefix-java-: - -rawPathPrefix -============= - -Description ------------ -Matches and consumes a prefix of the unmatched path of the ``RequestContext`` against the given ``PathMatcher``, -potentially extracts one or more values (depending on the type of the argument). - -This directive filters incoming requests based on the part of their URI that hasn't been matched yet by other -potentially existing ``rawPathPrefix`` or :ref:`-pathPrefix-java-` directives on higher levels of the routing structure. -Its one parameter is usually an expression evaluating to a ``PathMatcher`` instance (see also: :ref:`pathmatcher-dsl`). - -As opposed to its :ref:`-pathPrefix-java-` counterpart ``rawPathPrefix`` does *not* automatically add a leading slash to its -``PathMatcher`` argument. Rather its ``PathMatcher`` argument is applied to the unmatched path as is. - -Depending on the type of its ``PathMatcher`` argument the ``rawPathPrefix`` directive extracts zero or more values from -the URI. If the match fails the request is rejected with an :ref:`empty rejection set `. - - -Example -------- -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/PathDirectivesExamplesTest.java#raw-path-prefix-test diff --git a/akka-docs/rst/java/http/routing-dsl/directives/path-directives/rawPathPrefixTest.rst b/akka-docs/rst/java/http/routing-dsl/directives/path-directives/rawPathPrefixTest.rst deleted file mode 100644 index e37ec18813..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/path-directives/rawPathPrefixTest.rst +++ /dev/null @@ -1,27 +0,0 @@ -.. _-rawPathPrefixTest-java-: - -rawPathPrefixTest -================= - -Description ------------ -Checks whether the unmatched path of the ``RequestContext`` has a prefix matched by the given ``PathMatcher``. -Potentially extracts one or more values (depending on the type of the argument) but doesn't consume its match from -the unmatched path. - -This directive is very similar to the :ref:`-pathPrefix-java-` directive with the one difference that the path prefix -it matched (if it matched) is *not* consumed. The unmatched path of the ``RequestContext`` is therefore left as -is even in the case that the directive successfully matched and the request is passed on to its inner route. - -For more info on how to create a ``PathMatcher`` see :ref:`pathmatcher-dsl`. - -As opposed to its :ref:`-pathPrefixTest-java-` counterpart ``rawPathPrefixTest`` does *not* automatically add a leading slash -to its ``PathMatcher`` argument. Rather its ``PathMatcher`` argument is applied to the unmatched path as is. - -Depending on the type of its ``PathMatcher`` argument the ``rawPathPrefixTest`` directive extracts zero or more values -from the URI. If the match fails the request is rejected with an :ref:`empty rejection set `. - - -Example -------- -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/PathDirectivesExamplesTest.java#raw-path-prefix-test diff --git a/akka-docs/rst/java/http/routing-dsl/directives/path-directives/redirectToNoTrailingSlashIfPresent.rst b/akka-docs/rst/java/http/routing-dsl/directives/path-directives/redirectToNoTrailingSlashIfPresent.rst deleted file mode 100644 index 8de32d9d8b..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/path-directives/redirectToNoTrailingSlashIfPresent.rst +++ /dev/null @@ -1,29 +0,0 @@ -.. _-redirectToNoTrailingSlashIfPresent-java-: - -redirectToNoTrailingSlashIfPresent -================================== - -Description ------------ -If the requested path does end with a trailing ``/`` character, -redirects to the same path without that trailing slash.. - -Redirects the HTTP Client to the same resource yet without the trailing ``/``, in case the request contained it. -When redirecting an HttpResponse with the given redirect response code (i.e. ``MovedPermanently`` or ``TemporaryRedirect`` -etc.) as well as a simple HTML page containing a "*click me to follow redirect*" link to be used in case the client can not, -or refuses to for security reasons, automatically follow redirects. - -Please note that the inner paths **MUST NOT** end with an explicit trailing slash (e.g. ``"things"./``) -for the re-directed-to route to match. - -A good read on the subject of how to deal with trailing slashes is available on `Google Webmaster Central - To Slash or not to Slash`_. - -See also :ref:`-redirectToTrailingSlashIfMissing-java-` for the opposite behaviour. - -.. _Google Webmaster Central - To Slash or not to Slash: http://googlewebmastercentral.blogspot.de/2010/04/to-slash-or-not-to-slash.html - -Example -------- -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/PathDirectivesExamplesTest.java#redirect-notrailing-slash-present - -See also :ref:`-redirectToTrailingSlashIfMissing-java-` which achieves the opposite - redirecting paths in case they do *not* have a trailing slash. diff --git a/akka-docs/rst/java/http/routing-dsl/directives/path-directives/redirectToTrailingSlashIfMissing.rst b/akka-docs/rst/java/http/routing-dsl/directives/path-directives/redirectToTrailingSlashIfMissing.rst deleted file mode 100644 index f8d81bc9c9..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/path-directives/redirectToTrailingSlashIfMissing.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. _-redirectToTrailingSlashIfMissing-java-: - -redirectToTrailingSlashIfMissing -================================ - -Description ------------ -If the requested path does not end with a trailing ``/`` character, -redirects to the same path followed by such trailing slash. - -Redirects the HTTP Client to the same resource yet followed by a trailing ``/``, in case the request did not contain it. -When redirecting an HttpResponse with the given redirect response code (i.e. ``MovedPermanently`` or ``TemporaryRedirect`` -etc.) as well as a simple HTML page containing a "*click me to follow redirect*" link to be used in case the client can not, -or refuses to for security reasons, automatically follow redirects. - -Please note that the inner paths **MUST** end with an explicit trailing slash (e.g. ``"things"./``) for the -re-directed-to route to match. - -See also :ref:`-redirectToNoTrailingSlashIfPresent-java-` for the opposite behaviour. - -Example -------- -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/PathDirectivesExamplesTest.java#redirect-notrailing-slash-missing - -See also :ref:`-redirectToNoTrailingSlashIfPresent-java-` which achieves the opposite - redirecting paths in case they do have a trailing slash. diff --git a/akka-docs/rst/java/http/routing-dsl/directives/range-directives/index.rst b/akka-docs/rst/java/http/routing-dsl/directives/range-directives/index.rst deleted file mode 100644 index 2602efb419..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/range-directives/index.rst +++ /dev/null @@ -1,9 +0,0 @@ -.. _RangeDirectives-java: - -RangeDirectives -=============== - -.. toctree:: - :maxdepth: 1 - - withRangeSupport diff --git a/akka-docs/rst/java/http/routing-dsl/directives/range-directives/withRangeSupport.rst b/akka-docs/rst/java/http/routing-dsl/directives/range-directives/withRangeSupport.rst deleted file mode 100644 index 4387e7afe6..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/range-directives/withRangeSupport.rst +++ /dev/null @@ -1,31 +0,0 @@ -.. _-withRangeSupport-java-: - -withRangeSupport -================ - -Description ------------ -Transforms the response from its inner route into a ``206 Partial Content`` -response if the client requested only part of the resource with a ``Range`` header. - -Augments responses to ``GET`` requests with an ``Accept-Ranges: bytes`` header and converts them into partial responses -if the request contains a valid ``Range`` request header. The requested byte-ranges are coalesced (merged) if they -lie closer together than the specified ``rangeCoalescingThreshold`` argument. - -In order to prevent the server from becoming overloaded with trying to prepare ``multipart/byteranges`` responses for -high numbers of potentially very small ranges the directive rejects requests requesting more than ``rangeCountLimit`` -ranges with a ``TooManyRangesRejection``. -Requests with unsatisfiable ranges are rejected with an ``UnsatisfiableRangeRejection``. - -The ``withRangeSupport()`` form (without parameters) uses the ``range-coalescing-threshold`` and ``range-count-limit`` -settings from the ``akka.http.routing`` configuration. - -This directive is transparent to non-``GET`` requests. - -See also: https://tools.ietf.org/html/rfc7233 - - -Example -------- -.. includecode2:: ../../../../code/docs/http/javadsl/server/directives/RangeDirectivesExamplesTest.java - :snippet: withRangeSupport \ No newline at end of file diff --git a/akka-docs/rst/java/http/routing-dsl/directives/respond-with-directives/index.rst b/akka-docs/rst/java/http/routing-dsl/directives/respond-with-directives/index.rst deleted file mode 100644 index 210f0a5bf9..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/respond-with-directives/index.rst +++ /dev/null @@ -1,13 +0,0 @@ -.. _RespondWithDirectives-java: - -RespondWithDirectives -===================== - -.. toctree:: - :maxdepth: 1 - - respondWithDefaultHeader - respondWithDefaultHeaders - respondWithHeader - respondWithHeaders - respondWithHeaders diff --git a/akka-docs/rst/java/http/routing-dsl/directives/respond-with-directives/respondWithDefaultHeader.rst b/akka-docs/rst/java/http/routing-dsl/directives/respond-with-directives/respondWithDefaultHeader.rst deleted file mode 100644 index 078f54bb17..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/respond-with-directives/respondWithDefaultHeader.rst +++ /dev/null @@ -1,21 +0,0 @@ -.. _-respondWithDefaultHeader-java-: - -respondWithDefaultHeader -======================== - -Description ------------ -Adds a given HTTP header to all responses coming back from its inner route only if a header with the same name doesn't -exist yet in the response. - - -This directive transforms ``HttpResponse`` and ``ChunkedResponseStart`` messages coming back from its inner route by -potentially adding the given ``HttpHeader`` instance to the headers list. -The header is only added if there is no header instance with the same name (case insensitively) already present in the -response. - -See also :ref:`-respondWithDefaultHeaders-java-` if you'd like to add more than one header. - -Example -------- -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/RespondWithDirectivesExamplesTest.java#respondWithDefaultHeader diff --git a/akka-docs/rst/java/http/routing-dsl/directives/respond-with-directives/respondWithDefaultHeaders.rst b/akka-docs/rst/java/http/routing-dsl/directives/respond-with-directives/respondWithDefaultHeaders.rst deleted file mode 100644 index 09b0b6cf35..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/respond-with-directives/respondWithDefaultHeaders.rst +++ /dev/null @@ -1,35 +0,0 @@ -.. _-respondWithDefaultHeaders-java-: - -respondWithDefaultHeaders -========================= - -Description ------------ -Adds the given HTTP headers to all responses coming back from its inner route only if a respective header with the same -name doesn't exist yet in the response. - - -This directive transforms ``HttpResponse`` and ``ChunkedResponseStart`` messages coming back from its inner route by -potentially adding the given ``HttpHeader`` instances to the headers list. -A header is only added if there is no header instance with the same name (case insensitively) already present in the -response. - -See also :ref:`-respondWithDefaultHeader-java-` if you'd like to add only a single header. - - -Example -------- - -The ``respondWithDefaultHeaders`` directive is equivalent to the ``respondWithDefaultHeader`` directive which -is shown in the example below, however it allows including multiple default headers at once in the directive, like so:: - - respondWithDefaultHeaders( - Origin(HttpOrigin("http://akka.io"), - RawHeader("X-Fish-Name", "Blippy"))) { /*...*/ } - - -The semantics remain the same however, as explained by the following example: - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/RespondWithDirectivesExamplesTest.java#respondWithDefaultHeaders - -See the :ref:`-respondWithDefaultHeader-java-` directive for an example with only one header. diff --git a/akka-docs/rst/java/http/routing-dsl/directives/respond-with-directives/respondWithHeader.rst b/akka-docs/rst/java/http/routing-dsl/directives/respond-with-directives/respondWithHeader.rst deleted file mode 100644 index 93409c4bfa..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/respond-with-directives/respondWithHeader.rst +++ /dev/null @@ -1,17 +0,0 @@ -.. _-respondWithHeader-java-: - -respondWithHeader -================= - -Description ------------ -Adds a given HTTP header to all responses coming back from its inner route. - -This directive transforms ``HttpResponse`` and ``ChunkedResponseStart`` messages coming back from its inner route by -adding the given ``HttpHeader`` instance to the headers list. - -See also :ref:`-respondWithHeaders-java-` if you'd like to add more than one header. - -Example -------- -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/RespondWithDirectivesExamplesTest.java#respondWithHeader diff --git a/akka-docs/rst/java/http/routing-dsl/directives/respond-with-directives/respondWithHeaders.rst b/akka-docs/rst/java/http/routing-dsl/directives/respond-with-directives/respondWithHeaders.rst deleted file mode 100644 index 5ae1323ade..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/respond-with-directives/respondWithHeaders.rst +++ /dev/null @@ -1,18 +0,0 @@ -.. _-respondWithHeaders-java-: - -respondWithHeaders -================== - -Description ------------ -Adds the given HTTP headers to all responses coming back from its inner route. - -This directive transforms ``HttpResponse`` and ``ChunkedResponseStart`` messages coming back from its inner route by -adding the given ``HttpHeader`` instances to the headers list. - -See also :ref:`-respondWithHeader-java-` if you'd like to add just a single header. - - -Example -------- -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/RespondWithDirectivesExamplesTest.java#respondWithHeaders diff --git a/akka-docs/rst/java/http/routing-dsl/directives/route-directives/complete.rst b/akka-docs/rst/java/http/routing-dsl/directives/route-directives/complete.rst deleted file mode 100644 index d5e8f70629..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/route-directives/complete.rst +++ /dev/null @@ -1,21 +0,0 @@ -.. _-complete-java-: - -complete -======== - -Description ------------ - -Completes the request using the given argument(s). - -``complete`` uses the given arguments to construct a ``Route`` which simply calls ``complete`` on the ``RequestContext`` -with the respective ``HttpResponse`` instance. -Completing the request will send the response "back up" the route structure where all the logic runs that wrapping -directives have potentially chained into the :class:`RouteResult` future transformation chain. - -Please note that the ``complete`` directive has multiple variants, like - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/RouteDirectivesExamplesTest.java#complete diff --git a/akka-docs/rst/java/http/routing-dsl/directives/route-directives/failWith.rst b/akka-docs/rst/java/http/routing-dsl/directives/route-directives/failWith.rst deleted file mode 100644 index 66d6e1e8d6..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/route-directives/failWith.rst +++ /dev/null @@ -1,28 +0,0 @@ -.. _-failWith-java-: - -failWith -======== - -Description ------------ -Bubbles up the given error through the route structure where it is dealt with by the closest ``handleExceptions`` -directive and its :class:`ExceptionHandler`. - -``failWith`` explicitly raises an exception that gets bubbled up through the route structure to be picked up by the -nearest ``handleExceptions`` directive. Using ``failWith`` rather than simply throwing an exception enables the route -structure's :ref:`exception-handling-java` mechanism to deal with the exception even if the current route is executed -asynchronously on another thread (e.g. in a ``Future`` or separate actor). - -If no ``handleExceptions`` is present above the respective location in the -route structure the top-level routing logic will handle the exception and translate it into a corresponding -``HttpResponse`` using the in-scope ``ExceptionHandler`` (see also the :ref:`exception-handling-java` chapter). - -There is one notable special case: If the given exception is a ``RejectionError`` exception it is *not* bubbled up, -but rather the wrapped exception is unpacked and "executed". This allows the "tunneling" of a rejection via an -exception. - - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/RouteDirectivesExamplesTest.java#failWith diff --git a/akka-docs/rst/java/http/routing-dsl/directives/route-directives/index.rst b/akka-docs/rst/java/http/routing-dsl/directives/route-directives/index.rst deleted file mode 100644 index af790f6afc..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/route-directives/index.rst +++ /dev/null @@ -1,21 +0,0 @@ -.. _RouteDirectives-java: - -RouteDirectives -=============== - -The ``RouteDirectives`` have a special role in akka-http's routing DSL. Contrary to all other directives (except most -:ref:`FileAndResourceDirectives-java`) they do not produce instances of type ``Directive[L <: HList]`` but rather "plain" -routes of type ``Route``. -The reason is that the ``RouteDirectives`` are not meant for wrapping an inner route (like most other directives, as -intermediate-level elements of a route structure, do) but rather form the leaves of the actual route structure **leaves**. - -So in most cases the inner-most element of a route structure branch is one of the ``RouteDirectives`` (or -:ref:`FileAndResourceDirectives-java`): - -.. toctree:: - :maxdepth: 1 - - complete - failWith - redirect - reject diff --git a/akka-docs/rst/java/http/routing-dsl/directives/route-directives/redirect.rst b/akka-docs/rst/java/http/routing-dsl/directives/route-directives/redirect.rst deleted file mode 100644 index 07f9db4377..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/route-directives/redirect.rst +++ /dev/null @@ -1,17 +0,0 @@ -.. _-redirect-java-: - -redirect -======== - -Description ------------ -Completes the request with a redirection response to a given targer URI and of a given redirection type (status code). - -``redirect`` is a convenience helper for completing the request with a redirection response. -It is equivalent to this snippet relying on the ``complete`` directive: - - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/RouteDirectivesExamplesTest.java#redirect diff --git a/akka-docs/rst/java/http/routing-dsl/directives/route-directives/reject.rst b/akka-docs/rst/java/http/routing-dsl/directives/route-directives/reject.rst deleted file mode 100644 index e31ec91878..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/route-directives/reject.rst +++ /dev/null @@ -1,23 +0,0 @@ -.. _-reject-java-: - -reject -====== - -Description ------------ -Explicitly rejects the request optionally using the given rejection(s). - -``reject`` uses the given rejection instances (which might be the empty ``Seq``) to construct a ``Route`` which simply -calls ``requestContext.reject``. See the chapter on :ref:`rejections-java` for more information on what this means. - -After the request has been rejected at the respective point it will continue to flow through the routing structure in -the search for a route that is able to complete it. - -The explicit ``reject`` directive is used mostly when building :ref:`Custom Directives`, e.g. inside of a ``flatMap`` -modifier for "filtering out" certain cases. - - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/RouteDirectivesExamplesTest.java#reject diff --git a/akka-docs/rst/java/http/routing-dsl/directives/scheme-directives/extractScheme.rst b/akka-docs/rst/java/http/routing-dsl/directives/scheme-directives/extractScheme.rst deleted file mode 100644 index 50c99da03a..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/scheme-directives/extractScheme.rst +++ /dev/null @@ -1,15 +0,0 @@ -.. _-extractScheme-java-: - -extractScheme -============= - -Description ------------ -Extracts the Uri scheme (i.e. "``http``", "``https``", etc.) for an incoming request. - -For rejecting a request if it doesn't match a specified scheme name, see the :ref:`-scheme-java-` directive. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/SchemeDirectivesExamplesTest.java#extractScheme diff --git a/akka-docs/rst/java/http/routing-dsl/directives/scheme-directives/index.rst b/akka-docs/rst/java/http/routing-dsl/directives/scheme-directives/index.rst deleted file mode 100644 index 66b46793c5..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/scheme-directives/index.rst +++ /dev/null @@ -1,13 +0,0 @@ -.. _SchemeDirectives-java: - -SchemeDirectives -================ - -Scheme directives can be used to extract the Uri scheme (i.e. "http", "https", etc.) -from requests or to reject any request that does not match a specified scheme name. - -.. toctree:: - :maxdepth: 1 - - extractScheme - scheme diff --git a/akka-docs/rst/java/http/routing-dsl/directives/scheme-directives/scheme.rst b/akka-docs/rst/java/http/routing-dsl/directives/scheme-directives/scheme.rst deleted file mode 100644 index 119e40fad8..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/scheme-directives/scheme.rst +++ /dev/null @@ -1,22 +0,0 @@ -.. _-scheme-java-: - -scheme -====== - -Description ------------ -Rejects a request if its Uri scheme does not match a given one. - -The ``scheme`` directive can be used to match requests by their Uri scheme, only passing -through requests that match the specified scheme and rejecting all others. - -A typical use case for the ``scheme`` directive would be to reject requests coming in over -http instead of https, or to redirect such requests to the matching https URI with a -``MovedPermanently``. - -For simply extracting the scheme name, see the :ref:`-extractScheme-java-` directive. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/SchemeDirectivesExamplesTest.java#scheme diff --git a/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authenticateBasic.rst b/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authenticateBasic.rst deleted file mode 100644 index 16fd9479c8..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authenticateBasic.rst +++ /dev/null @@ -1,31 +0,0 @@ -.. _-authenticateBasic-java-: - -authenticateBasic -================= -Wraps the inner route with Http Basic authentication support using a given ``Authenticator``. - -Description ------------ -Provides support for handling `HTTP Basic Authentication`_. - -Given a function returning an ``Optional`` with a value upon successful authentication and an empty ``Optional`` otherwise, -respectively applies the inner route or rejects the request with a :class:`AuthenticationFailedRejection` rejection, -which by default is mapped to an ``401 Unauthorized`` response. - -Longer-running authentication tasks (like looking up credentials in a database) should use the :ref:`-authenticateBasicAsync-java-` -variant of this directive which allows it to run without blocking routing layer of Akka HTTP, freeing it for other requests. - -Standard HTTP-based authentication which uses the ``WWW-Authenticate`` header containing challenge data and -``Authorization`` header for receiving credentials is implemented in subclasses of ``HttpAuthenticator``. - -See :ref:`credentials-and-timing-attacks-java` for details about verifying the secret. - -.. warning:: - Make sure to use basic authentication only over SSL/TLS because credentials are transferred in plaintext. - -.. _HTTP Basic Authentication: https://en.wikipedia.org/wiki/Basic_auth - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/SecurityDirectivesExamplesTest.java#authenticateBasic diff --git a/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authenticateBasicAsync.rst b/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authenticateBasicAsync.rst deleted file mode 100644 index 2267737a5a..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authenticateBasicAsync.rst +++ /dev/null @@ -1,29 +0,0 @@ -.. _-authenticateBasicAsync-java-: - -authenticateBasicAsync -====================== -Wraps the inner route with Http Basic authentication support using a given ``AsyncAuthenticator``. - -Description ------------ -This variant of the :ref:`-authenticateBasic-java-` directive returns a ``Future>`` which allows freeing up the routing -layer of Akka HTTP, freeing it for other requests. It should be used whenever an authentication is expected to take -a longer amount of time (e.g. looking up the user in a database). - -In case the returned option is an empty ``Optional`` the request is rejected with a :class:`AuthenticationFailedRejection`, -which by default is mapped to an ``401 Unauthorized`` response. - -Standard HTTP-based authentication which uses the ``WWW-Authenticate`` header containing challenge data and -``Authorization`` header for receiving credentials is implemented in subclasses of ``HttpAuthenticator``. - -See :ref:`credentials-and-timing-attacks-java` for details about verifying the secret. - -.. warning:: - Make sure to use basic authentication only over SSL/TLS because credentials are transferred in plaintext. - -.. _HTTP Basic Authentication: https://en.wikipedia.org/wiki/Basic_auth - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/SecurityDirectivesExamplesTest.java#authenticateBasicAsync diff --git a/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authenticateBasicPF.rst b/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authenticateBasicPF.rst deleted file mode 100644 index 9617e2a3c1..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authenticateBasicPF.rst +++ /dev/null @@ -1,29 +0,0 @@ -.. _-authenticateBasicPF-java-: - -authenticateBasicPF -=================== -Wraps the inner route with Http Basic authentication support using a given ``AuthenticatorPF``. - -Description ------------ -Provides support for handling `HTTP Basic Authentication`_. - -Refer to :ref:`-authenticateBasic-java-` for a detailed description of this directive. - -Its semantics are equivalent to ``authenticateBasicPF`` 's, where not handling a case in the Partial Function (PF) -leaves the request to be rejected with a :class:`AuthenticationFailedRejection` rejection. - -Longer-running authentication tasks (like looking up credentials in a database) should use :ref:`-authenticateBasicAsync-java-` -or :ref:`-authenticateBasicPFAsync-java-` if you prefer to use the ``PartialFunction`` syntax. - -See :ref:`credentials-and-timing-attacks-java` for details about verifying the secret. - -.. warning:: - Make sure to use basic authentication only over SSL/TLS because credentials are transferred in plaintext. - -.. _HTTP Basic Authentication: https://en.wikipedia.org/wiki/Basic_auth - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/SecurityDirectivesExamplesTest.java#authenticateBasicPF diff --git a/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authenticateBasicPFAsync.rst b/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authenticateBasicPFAsync.rst deleted file mode 100644 index e0c5e5118d..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authenticateBasicPFAsync.rst +++ /dev/null @@ -1,26 +0,0 @@ -.. _-authenticateBasicPFAsync-java-: - -authenticateBasicPFAsync -======================== -Wraps the inner route with Http Basic authentication support using a given ``AsyncAuthenticatorPF``. - -Description ------------ -Provides support for handling `HTTP Basic Authentication`_. - -Refer to :ref:`-authenticateBasic-java-` for a detailed description of this directive. - -Its semantics are equivalent to ``authenticateBasicPF`` 's, where not handling a case in the Partial Function (PF) -leaves the request to be rejected with a :class:`AuthenticationFailedRejection` rejection. - -See :ref:`credentials-and-timing-attacks-java` for details about verifying the secret. - -.. warning:: - Make sure to use basic authentication only over SSL/TLS because credentials are transferred in plaintext. - -.. _HTTP Basic Authentication: https://en.wikipedia.org/wiki/Basic_auth - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/SecurityDirectivesExamplesTest.java#authenticateBasicPFAsync diff --git a/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authenticateOAuth2.rst b/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authenticateOAuth2.rst deleted file mode 100644 index 38fa54e681..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authenticateOAuth2.rst +++ /dev/null @@ -1,33 +0,0 @@ -.. _-authenticateOAuth2-java-: - -authenticateOAuth2 -================== -Wraps the inner route with OAuth Bearer Token authentication support using a given ``AuthenticatorPF`` - -Description ------------ -Provides support for extracting the so-called "*Bearer Token*" from the :class:`Authorization` HTTP Header, -which is used to initiate an OAuth2 authorization. - -.. warning:: - This directive does not implement the complete OAuth2 protocol, but instead enables implementing it, - by extracting the needed token from the HTTP headers. - -Given a function returning ``Some`` upon successful authentication and ``None`` otherwise, -respectively applies the inner route or rejects the request with a :class:`AuthenticationFailedRejection` rejection, -which by default is mapped to an ``401 Unauthorized`` response. - -Longer-running authentication tasks (like looking up credentials in a database) should use the :ref:`-authenticateOAuth2Async-java-` -variant of this directive which allows it to run without blocking routing layer of Akka HTTP, freeing it for other requests. - -See :ref:`credentials-and-timing-attacks-java` for details about verifying the secret. - -For more information on how OAuth2 works see `RFC 6750`_. - -.. _RFC 6750: https://tools.ietf.org/html/rfc6750 - -Example -------- - -Usage in code is exactly the same as :ref:`-authenticateBasic-java-`, -with the difference that one must validate the token as OAuth2 dictates (which is currently not part of Akka HTTP itself). diff --git a/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authenticateOAuth2Async.rst b/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authenticateOAuth2Async.rst deleted file mode 100644 index 760112e0e9..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authenticateOAuth2Async.rst +++ /dev/null @@ -1,33 +0,0 @@ -.. _-authenticateOAuth2Async-java-: - -authenticateOAuth2Async -======================= -Wraps the inner route with OAuth Bearer Token authentication support using a given ``AsyncAuthenticator``. - -Description ------------ -Provides support for extracting the so-called "*Bearer Token*" from the :class:`Authorization` HTTP Header, -which is used to initiate an OAuth2 authorization. - -.. warning:: - This directive does not implement the complete OAuth2 protocol, but instead enables implementing it, - by extracting the needed token from the HTTP headers. - -Given a function returning ``Some`` upon successful authentication and ``None`` otherwise, -respectively applies the inner route or rejects the request with a :class:`AuthenticationFailedRejection` rejection, -which by default is mapped to an ``401 Unauthorized`` response. - -See also :ref:`-authenticateOAuth2-java-` if the authorization operation is rather quick, and does not have to execute asynchronously. - -See :ref:`credentials-and-timing-attacks-java` for details about verifying the secret. - -For more information on how OAuth2 works see `RFC 6750`_. - -.. _RFC 6750: https://tools.ietf.org/html/rfc6750 - - -Example -------- - -Usage in code is exactly the same as :ref:`-authenticateBasicAsync-java-`, -with the difference that one must validate the token as OAuth2 dictates (which is currently not part of Akka HTTP itself). diff --git a/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authenticateOAuth2PF.rst b/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authenticateOAuth2PF.rst deleted file mode 100644 index 43e56e51ef..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authenticateOAuth2PF.rst +++ /dev/null @@ -1,34 +0,0 @@ -.. _-authenticateOAuth2PF-java-: - -authenticateOAuth2PF -==================== -Wraps the inner route with OAuth Bearer Token authentication support using a given ``AuthenticatorPF``. - -Description ------------ -Provides support for extracting the so-called "*Bearer Token*" from the :class:`Authorization` HTTP Header, -which is used to initiate an OAuth2 authorization. - -.. warning:: - This directive does not implement the complete OAuth2 protocol, but instead enables implementing it, - by extracting the needed token from the HTTP headers. - -Refer to :ref:`-authenticateOAuth2-java-` for a detailed description of this directive. - -Its semantics are equivalent to ``authenticateOAuth2PF`` 's, where not handling a case in the Partial Function (PF) -leaves the request to be rejected with a :class:`AuthenticationFailedRejection` rejection. - -Longer-running authentication tasks (like looking up credentials in a database) should use the :ref:`-authenticateOAuth2Async-java-` -variant of this directive which allows it to run without blocking routing layer of Akka HTTP, freeing it for other requests. - -See :ref:`credentials-and-timing-attacks-java` for details about verifying the secret. - -For more information on how OAuth2 works see `RFC 6750`_. - -.. _RFC 6750: https://tools.ietf.org/html/rfc6750 - -Example -------- - -Usage in code is exactly the same as :ref:`-authenticateBasicPF-java-`, -with the difference that one must validate the token as OAuth2 dictates (which is currently not part of Akka HTTP itself). diff --git a/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authenticateOAuth2PFAsync.rst b/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authenticateOAuth2PFAsync.rst deleted file mode 100644 index 1af2e3136c..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authenticateOAuth2PFAsync.rst +++ /dev/null @@ -1,34 +0,0 @@ -.. _-authenticateOAuth2PFAsync-java-: - -authenticateOAuth2PFAsync -========================= -Wraps the inner route with OAuth Bearer Token authentication support using a given ``AsyncAuthenticatorPF``. - -Description ------------ -Provides support for extracting the so-called "*Bearer Token*" from the :class:`Authorization` HTTP Header, -which is used to initiate an OAuth2 authorization. - -.. warning:: - This directive does not implement the complete OAuth2 protocol, but instead enables implementing it, - by extracting the needed token from the HTTP headers. - -Refer to :ref:`-authenticateOAuth2-java-` for a detailed description of this directive. - -Its semantics are equivalent to ``authenticateOAuth2PF`` 's, where not handling a case in the Partial Function (PF) -leaves the request to be rejected with a :class:`AuthenticationFailedRejection` rejection. - -See also :ref:`-authenticateOAuth2PF-java-` if the authorization operation is rather quick, and does not have to execute asynchronously. - -See :ref:`credentials-and-timing-attacks-java` for details about verifying the secret. - -For more information on how OAuth2 works see `RFC 6750`_. - -.. _RFC 6750: https://tools.ietf.org/html/rfc6750 - - -Example -------- - -Usage in code is exactly the same as :ref:`-authenticateBasicPFAsync-java-`, -with the difference that one must validate the token as OAuth2 dictates (which is currently not part of Akka HTTP itself). diff --git a/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authenticateOrRejectWithChallenge.rst b/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authenticateOrRejectWithChallenge.rst deleted file mode 100644 index 4b96af6747..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authenticateOrRejectWithChallenge.rst +++ /dev/null @@ -1,20 +0,0 @@ -.. _-authenticateOrRejectWithChallenge-java-: - -authenticateOrRejectWithChallenge -================================= -Lifts an authenticator function into a directive. - -Description ------------ -This directive allows implementing the low level challange-response type of authentication that some services may require. - -More details about challenge-response authentication are available in the `RFC 2617`_, `RFC 7616`_ and `RFC 7617`_. - -.. _RFC 2617: http://tools.ietf.org/html/rfc2617 -.. _RFC 7616: http://tools.ietf.org/html/rfc7616 -.. _RFC 7617: http://tools.ietf.org/html/rfc7617 - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/SecurityDirectivesExamplesTest.java#authenticateOrRejectWithChallenge diff --git a/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authorize.rst b/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authorize.rst deleted file mode 100644 index 6a9306ba8a..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authorize.rst +++ /dev/null @@ -1,28 +0,0 @@ -.. _-authorize-java-: - -authorize -========= -Applies the given authorization check to the request. - -Description ------------ -The user-defined authorization check can either be supplied as a ``=> Boolean`` value which is calculated -just from information out of the lexical scope, or as a function ``RequestContext => Boolean`` which can also -take information from the request itself into account. - -If the check returns ``true`` the request is passed on to the inner route unchanged, otherwise an -``AuthorizationFailedRejection`` is created, triggering a ``403 Forbidden`` response by default -(the same as in the case of an ``AuthenticationFailedRejection``). - -In a common use-case you would check if a user (e.g. supplied by any of the ``authenticate*`` family of directives, -e.g. :ref:`-authenticateBasic-java-`) is allowed to access the inner routes, e.g. by checking if the user has the needed permissions. - -See also :ref:`-authorize-java-` for the asynchronous version of this directive. - -.. note:: - See also :ref:`authentication-vs-authorization-java` to understand the differences between those. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/SecurityDirectivesExamplesTest.java#authorize diff --git a/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authorizeAsync.rst b/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authorizeAsync.rst deleted file mode 100644 index 32fa84a65a..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/security-directives/authorizeAsync.rst +++ /dev/null @@ -1,29 +0,0 @@ -.. _-authorizeAsync-java-: - -authorizeAsync -============== -Applies the given authorization check to the request. - -Description ------------ - -The user-defined authorization check can either be supplied as a ``=> Future[Boolean]`` value which is calculated -just from information out of the lexical scope, or as a function ``RequestContext => Future[Boolean]`` which can also -take information from the request itself into account. - -If the check returns ``true`` or the ``Future`` is failed the request is passed on to the inner route unchanged, -otherwise an ``AuthorizationFailedRejection`` is created, triggering a ``403 Forbidden`` response by default -(the same as in the case of an ``AuthenticationFailedRejection``). - -In a common use-case you would check if a user (e.g. supplied by any of the ``authenticate*`` family of directives, -e.g. :ref:`-authenticateBasic-java-`) is allowed to access the inner routes, e.g. by checking if the user has the needed permissions. - -See also :ref:`-authorize-java-` for the synchronous version of this directive. - -.. note:: - See also :ref:`authentication-vs-authorization-java` to understand the differences between those. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/SecurityDirectivesExamplesTest.java#authorizeAsync diff --git a/akka-docs/rst/java/http/routing-dsl/directives/security-directives/extractCredentials.rst b/akka-docs/rst/java/http/routing-dsl/directives/security-directives/extractCredentials.rst deleted file mode 100644 index d24acf4484..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/security-directives/extractCredentials.rst +++ /dev/null @@ -1,17 +0,0 @@ -.. _-extractCredentials-java-: - -extractCredentials -================== - -Description ------------ - -Extracts the potentially present ``HttpCredentials`` provided with the request's ``Authorization`` header, -which can be then used to implement some custom authentication or authorization logic. - -See :ref:`credentials-and-timing-attacks-java` for details about verifying the secret. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/SecurityDirectivesExamplesTest.java#extractCredentials diff --git a/akka-docs/rst/java/http/routing-dsl/directives/security-directives/index.rst b/akka-docs/rst/java/http/routing-dsl/directives/security-directives/index.rst deleted file mode 100644 index 476e89ebd1..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/security-directives/index.rst +++ /dev/null @@ -1,84 +0,0 @@ -.. _SecurityDirectives-java: - -SecurityDirectives -================== - -.. toctree:: - :maxdepth: 1 - - authenticateBasic - authenticateBasicAsync - authenticateBasicPF - authenticateBasicPFAsync - authenticateOrRejectWithChallenge - authenticateOAuth2 - authenticateOAuth2Async - authenticateOAuth2PF - authenticateOAuth2PFAsync - authenticateOrRejectWithChallenge - authorize - authorizeAsync - extractCredentials - - -.. _authentication-vs-authorization-java: - -Authentication vs. Authorization --------------------------------- - -**Authentication** is the process of establishing a known identity for the user, whereby 'identity' is defined in the -context of the application. This may be done with a username/password combination, a cookie, a pre-defined IP or some -other mechanism. After authentication the system believes that it knows who the user is. - -**Authorization** is the process of determining, whether a given user is allowed access to a given resource or not. In -most cases, in order to be able to authorize a user (i.e. allow access to some part of the system) the users identity -must already have been established, i.e. he/she must have been authenticated. Without prior authentication the -authorization would have to be very crude, e.g. "allow access for *all* users" or "allow access for *noone*". Only after -authentication will it be possible to, e.g., "allow access to the statistics resource for *admins*, but not for regular -*members*". - -Authentication and authorization may happen at the same time, e.g. when everyone who can properly be authenticated is -also allowed access (which is often a very simple and somewhat implicit authorization logic). In other cases the -system might have one mechanism for authentication (e.g. establishing user identity via an LDAP lookup) and another one -for authorization (e.g. a database lookup for retrieving user access rights). - - -Authentication and Authorization in HTTP ----------------------------------------- - -HTTP provides a general framework for access control and authentication, via an extensible set of challenge-response -authentication schemes, which can be used by a server to challenge a client request and by a client to provide -authentication information. The general mechanism is defined in `RFC 7235`_. - -The "HTTP Authentication Scheme Registry" defines the namespace for the authentication schemes in challenges and -credentials. You can see the currently registered schemes at http://www.iana.org/assignments/http-authschemes. - -At this point Akka HTTP only implements the "'Basic' HTTP Authentication Scheme" whose most current specification can be -found here: https://datatracker.ietf.org/doc/draft-ietf-httpauth-basicauth-update/. - -.. _RFC 7235: http://tools.ietf.org/html/rfc7235 - -Low-level OAuth2 "Bearer Token" directives ------------------------------------------- -The OAuth2 directives currently provided in Akka HTTP are not a full OAuth2 protocol implementation, -they are only a means of extracting the so called ``Bearer Token`` from the ``Authorization`` HTTP Header, -as defined in `RFC 6750`_, and allow users to validate and complete the protocol. - -.. _RFC 6750: https://tools.ietf.org/html/rfc6750 - - -.. _credentials-and-timing-attacks-java: - -Credentials and password timing attacks ---------------------------------------- - -When transforming request ``Credentials`` into an application specific user identifier the naive solution for -checking the secret (password) would be a regular string comparison, but doing this would open up the application to -timing attacks. See for example `Timing Attacks Explained`_ for an explanation of the problem. - -.. _Timing Attacks Explained: http://emerose.com/timing-attacks-explained - -To protect users of the library from that mistake the secret is not available through the API, instead the method -``Credentials.Provided.verify(String)`` should be used. It does a constant time comparison rather than returning early -upon finding the first non-equal character. - diff --git a/akka-docs/rst/java/http/routing-dsl/directives/timeout-directives/index.rst b/akka-docs/rst/java/http/routing-dsl/directives/timeout-directives/index.rst deleted file mode 100644 index fe1d4dfb74..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/timeout-directives/index.rst +++ /dev/null @@ -1,11 +0,0 @@ -.. _TimeoutDirectives-java: - -TimeoutDirectives -================= - -.. toctree:: - :maxdepth: 1 - - withRequestTimeout - withoutRequestTimeout - withRequestTimeoutResponse diff --git a/akka-docs/rst/java/http/routing-dsl/directives/timeout-directives/withRequestTimeout.rst b/akka-docs/rst/java/http/routing-dsl/directives/timeout-directives/withRequestTimeout.rst deleted file mode 100644 index 985e433fc4..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/timeout-directives/withRequestTimeout.rst +++ /dev/null @@ -1,42 +0,0 @@ -.. _-withRequestTimeout-java-: - -withRequestTimeout -================== - -Description ------------ - -This directive enables "late" (during request processing) control over the :ref:`request-timeout-java` feature in Akka HTTP. - -The timeout can be either loosened or made more tight using this directive, however one should be aware that it is -inherently racy (which may especially show with very tight timeouts) since a timeout may already have been triggered -when this directive executes. - -In case of pipelined HTTP requests (multiple requests being accepted on the same connection before sending the first response) -a the request timeout failure of the ``n-th`` request *will shut down the connection* causing the already enqueued requests -to be dropped. This is by-design, as the request timeout feature serves as a "safety net" in case of programming errors -(e.g. a Future that never completes thus potentially blocking the entire connection forever) or malicious attacks on the server. - -Optionally, a timeout handler may be provided in which is called when a time-out is triggered and must produce an -``HttpResponse`` that will be sent back to the client instead of the "too late" response (in case it'd ever arrive). -See also :ref:`-withRequestTimeoutResponse-java-` if only looking to customise the timeout response without changing the timeout itself. - -.. warning:: - Please note that setting the timeout from within a directive is inherently racy (as the "point in time from which - we're measuring the timeout" is already in the past (the moment we started handling the request), so if the existing - timeout already was triggered before your directive had the chance to change it, an timeout may still be logged. - - It is recommended to use a larger statically configured timeout (think of it as a "safety net" against programming errors - or malicious attackers) and if needed tighten it using the directives – not the other way around. - -For more information about various timeouts in Akka HTTP see :ref:`http-timeouts-java`. - -Example -------- -.. includecode2:: ../../../../code/docs/http/javadsl/server/directives/TimeoutDirectivesExamplesTest.java - :snippet: withRequestTimeout-plain - -With setting the handler at the same time: - -.. includecode2:: ../../../../code/docs/http/javadsl/server/directives/TimeoutDirectivesExamplesTest.java - :snippet: withRequestTimeout-with-handler \ No newline at end of file diff --git a/akka-docs/rst/java/http/routing-dsl/directives/timeout-directives/withRequestTimeoutResponse.rst b/akka-docs/rst/java/http/routing-dsl/directives/timeout-directives/withRequestTimeoutResponse.rst deleted file mode 100644 index dfb27824a4..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/timeout-directives/withRequestTimeoutResponse.rst +++ /dev/null @@ -1,27 +0,0 @@ -.. _-withRequestTimeoutResponse-java-: - -withRequestTimeoutResponse -========================== - -Description ------------ - -Allows customising the ``HttpResponse`` that will be sent to clients in case of a :ref:`request-timeout-java`. - -See also :ref:`-withRequestTimeout-java-` or :ref:`-withoutRequestTimeout-java-` if interested in dynamically changing the timeout -for a given route instead. - -.. warning:: - Please note that setting handler is inherently racy as the timeout is measured from starting to handle the request - to its deadline, thus if the timeout triggers before the ``withRequestTimeoutResponse`` executed it would have emitted - the default timeout HttpResponse. - - In practice this can only be a problem with very tight timeouts, so with default settings - of request timeouts being measured in seconds it shouldn't be a problem in reality (though certainly a possibility still). - -To learn more about various timeouts in Akka HTTP and how to configure them see :ref:`http-timeouts-java`. - -Example -------- -.. includecode2:: ../../../../code/docs/http/javadsl/server/directives/TimeoutDirectivesExamplesTest.java - :snippet: withRequestTimeoutResponse \ No newline at end of file diff --git a/akka-docs/rst/java/http/routing-dsl/directives/timeout-directives/withoutRequestTimeout.rst b/akka-docs/rst/java/http/routing-dsl/directives/timeout-directives/withoutRequestTimeout.rst deleted file mode 100644 index 8533ec5b5f..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/timeout-directives/withoutRequestTimeout.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. _-withoutRequestTimeout-java-: - -withoutRequestTimeout -===================== - -Description ------------ - -This directive enables "late" (during request processing) control over the :ref:`request-timeout-java` feature in Akka HTTP. - -It is not recommended to turn off request timeouts using this method as it is inherently racy and disabling request timeouts -basically turns off the safety net against programming mistakes that it provides. - -.. warning:: - Please note that setting the timeout from within a directive is inherently racy (as the "point in time from which - we're measuring the timeout" is already in the past (the moment we started handling the request), so if the existing - timeout already was triggered before your directive had the chance to change it, an timeout may still be logged. - -For more information about various timeouts in Akka HTTP see :ref:`http-timeouts-java`. - -Example -------- -.. includecode2:: ../../../../code/docs/http/javadsl/server/directives/TimeoutDirectivesExamplesTest.java - :snippet: withoutRequestTimeout \ No newline at end of file diff --git a/akka-docs/rst/java/http/routing-dsl/directives/websocket-directives/handleWebSocketMessages.rst b/akka-docs/rst/java/http/routing-dsl/directives/websocket-directives/handleWebSocketMessages.rst deleted file mode 100644 index 33d844bd52..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/websocket-directives/handleWebSocketMessages.rst +++ /dev/null @@ -1,20 +0,0 @@ -.. _-handleWebSocketMessages-java-: - -handleWebSocketMessages -======================= - -Description ------------ - -The directive first checks if the request was a valid WebSocket handshake request and if yes, it completes the request -with the passed handler. Otherwise, the request is rejected with an ``ExpectedWebSocketRequestRejection``. - -WebSocket subprotocols offered in the ``Sec-WebSocket-Protocol`` header of the request are ignored. If you want to -support several protocols use the :ref:`-handleWebSocketMessagesForProtocol-java-` directive, instead. - -For more information about the WebSocket support, see :ref:`server-side-websocket-support-java`. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/WebSocketDirectivesExamplesTest.java#handleWebSocketMessages diff --git a/akka-docs/rst/java/http/routing-dsl/directives/websocket-directives/handleWebSocketMessagesForProtocol.rst b/akka-docs/rst/java/http/routing-dsl/directives/websocket-directives/handleWebSocketMessagesForProtocol.rst deleted file mode 100644 index 5f88c2c8d3..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/websocket-directives/handleWebSocketMessagesForProtocol.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. _-handleWebSocketMessagesForProtocol-java-: - -handleWebSocketMessagesForProtocol -================================== - -Description ------------ -Handles WebSocket requests with the given handler if the given subprotocol is offered in the ``Sec-WebSocket-Protocol`` -header of the request and rejects other requests with an ``ExpectedWebSocketRequestRejection`` or an -``UnsupportedWebSocketSubprotocolRejection``. - -The directive first checks if the request was a valid WebSocket handshake request and if the request offers the passed -subprotocol name. If yes, the directive completes the request with the passed handler. Otherwise, the request is -either rejected with an ``ExpectedWebSocketRequestRejection`` or an ``UnsupportedWebSocketSubprotocolRejection``. - -To support several subprotocols, for example at the same path, several instances of ``handleWebSocketMessagesForProtocol`` can -be chained using ``~`` as you can see in the below example. - -For more information about the WebSocket support, see :ref:`server-side-websocket-support-java`. - -Example -------- - -.. includecode:: ../../../../code/docs/http/javadsl/server/directives/WebSocketDirectivesExamplesTest.java#handleWebSocketMessagesForProtocol diff --git a/akka-docs/rst/java/http/routing-dsl/directives/websocket-directives/index.rst b/akka-docs/rst/java/http/routing-dsl/directives/websocket-directives/index.rst deleted file mode 100644 index 58b568639a..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/directives/websocket-directives/index.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. _WebSocketDirectives-java: - -WebSocketDirectives -=================== - -.. toctree:: - :maxdepth: 1 - - handleWebSocketMessages - handleWebSocketMessagesForProtocol diff --git a/akka-docs/rst/java/http/routing-dsl/exception-handling.rst b/akka-docs/rst/java/http/routing-dsl/exception-handling.rst deleted file mode 100644 index f0d81bb9c9..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/exception-handling.rst +++ /dev/null @@ -1,26 +0,0 @@ -.. _exception-handling-java: - -Exception Handling -================== - -Exceptions thrown during route execution bubble up through the route structure to the next enclosing -:ref:`-handleExceptions-java-` directive or the top of your route structure. - -Similarly to the way that :ref:`rejections-java` are handled the :ref:`-handleExceptions-java-` directive delegates the actual job -of converting an exception to its argument, an ``ExceptionHandler``. - -An ``ExceptionHandler`` is a partial function, so it can choose which exceptions it would like to handle and -which not. Unhandled exceptions will simply continue to bubble up in the route structure. -At the root of the route tree any still unhandled exception will be dealt with by the top-level handler which always -handles *all* exceptions. - -``Route.seal`` internally wraps its argument route with the :ref:`-handleExceptions-java-` directive in order to "catch" and -handle any exception. - -So, if you'd like to customize the way certain exceptions are handled you need to write a custom ``ExceptionHandler``. -Once you have defined your custom ``ExceptionHandler`` you can supply it as argument to the :ref:`-handleExceptions-java-` directive. -That will apply your handler to the inner route given to that directive. - -Here is an example for wiring up a custom handler via :ref:`-handleExceptions-java-`: - -TODO diff --git a/akka-docs/rst/java/http/routing-dsl/index.rst b/akka-docs/rst/java/http/routing-dsl/index.rst deleted file mode 100644 index 85fff2bcb5..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/index.rst +++ /dev/null @@ -1,72 +0,0 @@ -.. _http-high-level-server-side-api-java: - -High-level Server-Side API -========================== - -In addition to the :ref:`http-low-level-server-side-api-java` Akka HTTP provides a very flexible "Routing DSL" for elegantly -defining RESTful web services. It picks up where the low-level API leaves off and offers much of the higher-level -functionality of typical web servers or frameworks, like deconstruction of URIs, content negotiation or -static content serving. - -To use the high-level API you need to add a dependency to the ``akka-http-experimental`` module. - -.. toctree:: - :maxdepth: 1 - - overview - routes - directives/index - marshalling - exception-handling - source-streaming-support - rejections - testkit - -Handling HTTP Server failures in the High-Level API ---------------------------------------------------- -There are various situations when failure may occur while initialising or running an Akka HTTP server. -Akka by default will log all these failures, however sometimes one may want to react to failures in addition -to them just being logged, for example by shutting down the actor system, or notifying some external monitoring -end-point explicitly. - -Bind failures -^^^^^^^^^^^^^ -For example the server might be unable to bind to the given port. For example when the port -is already taken by another application, or if the port is privileged (i.e. only usable by ``root``). -In this case the "binding future" will fail immediately, and we can react to if by listening on the CompletionStage's completion: - -.. includecode:: ../../code/docs/http/javadsl/server/HighLevelServerBindFailureExample.java - :include: binding-failure-high-level-example - - -.. note:: - For a more low-level overview of the kinds of failures that can happen and also more fine-grained control over them - refer to the :ref:`handling-http-server-failures-low-level-java` documentation. - -Failures and exceptions inside the Routing DSL -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Exception handling within the Routing DSL is done by providing :class:`ExceptionHandler` s which are documented in-depth -in the :ref:`exception-handling-java` section of the documtnation. You can use them to transform exceptions into -:class:`HttpResponse` s with apropriate error codes and human-readable failure descriptions. - -File uploads -^^^^^^^^^^^^ - -For high level directives to handle uploads see the :ref:`FileUploadDirectives-java`. - -Handling a simple file upload from for example a browser form with a `file` input can be done -by accepting a `Multipart.FormData` entity, note that the body parts are `Source` rather than -all available right away, and so is the individual body part payload so you will need to consume -those streams both for the file and for the form fields. - -Here is a simple example which just dumps the uploaded file into a temporary file on disk, collects -some form fields and saves an entry to a fictive database: - -TODO missing example 1 - -You can transform the uploaded files as they arrive rather than storing then in a temporary file as -in the previous example. In this example we accept any number of ``.csv`` files, parse those into lines -and split each line before we send it to an actor for further processing: - -TODO missing example 2 diff --git a/akka-docs/rst/java/http/routing-dsl/marshalling.rst b/akka-docs/rst/java/http/routing-dsl/marshalling.rst deleted file mode 100644 index d0376b9caa..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/marshalling.rst +++ /dev/null @@ -1,43 +0,0 @@ -.. _marshalling-java: - -Marshalling & Unmarshalling -=========================== - -"Marshalling" is the process of converting a higher-level (object) structure into some kind of lower-level -representation (and vice versa), often a binary 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 an HttpEntity, which forms the entity body -of an HTTP request or response (depending on whether used on the client or server side). - -Marshalling ------------ - -On the server-side marshalling is used to convert an application-domain object to a response (entity). Requests can -contain an ``Accept`` header that lists acceptable content types for the client. A marshaller contains the logic to -negotiate the result content types based on the ``Accept`` and the ``AcceptCharset`` headers. - -Marshallers can be specified when completing a request with ``RequestContext.complete`` or by using one of the -``RouteDirectives.complete`` directives. - -These marshallers are provided by akka-http: - - * Use :ref:`json-jackson-support-java` to create an marshaller that can convert a POJO to an ``application/json`` - response using jackson_. - * Use ``Marshaller.stringToEntity``, ``Marshaller.byteArrayToEntity``, ``Marshaller.byteStringToEntity``, - combined with ``Marshaller.entityToResponse`` to create custom marshallers. - -Unmarshalling -------------- - -On the server-side unmarshalling is used to convert a request (entity) to an application-domain object. This is done -in the ``MarshallingDirectives.request`` or ``MarshallingDirectives.entity`` directive. There are several unmarshallers -provided by akka-http: - - * Use :ref:`json-jackson-support-java` to create an unmarshaller that can convert an ``application/json`` request - to a POJO using jackson_. - * Use the predefined ``Unmarshaller.entityToString``, ``Unmarshaller.entityToByteString``, ``Unmarshaller.entityToByteArray``, - ``Unmarshaller.entityToCharArray`` to convert to those basic types. - * Use ``Unmarshaller.sync`` or ``Unmarshaller.async`` to create a custom unmarshaller. - -.. _jackson: https://github.com/FasterXML/jackson \ No newline at end of file diff --git a/akka-docs/rst/java/http/routing-dsl/overview.rst b/akka-docs/rst/java/http/routing-dsl/overview.rst deleted file mode 100644 index 727b61df91..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/overview.rst +++ /dev/null @@ -1,41 +0,0 @@ -.. _http-routing-java: - -Routing DSL Overview -==================== - -The Akka HTTP :ref:`http-low-level-server-side-api-java` provides a ``Flow``- or ``Function``-level interface that allows -an application to respond to incoming HTTP requests by simply mapping requests to responses -(excerpt from :ref:`Low-level server side example `): - -.. includecode:: ../../code/docs/http/javadsl/server/HttpServerExampleDocTest.java - :include: request-handler - -While it'd be perfectly possible to define a complete REST API service purely by inspecting the incoming -``HttpRequest`` this approach becomes somewhat unwieldy for larger services due to the amount of syntax "ceremony" -required. Also, it doesn't help in keeping your service definition as DRY_ as you might like. - -As an alternative Akka HTTP provides a flexible DSL for expressing your service behavior as a structure of -composable elements (called :ref:`directives-java`) in a concise and readable way. Directives are assembled into a so called -*route structure* which, at its top-level, can be used to create a handler ``Flow`` (or, alternatively, an -async handler function) that can be directly supplied to a ``bind`` call. - -Here's the complete example rewritten using the composable high-level API: - -.. includecode:: ../../code/docs/http/javadsl/server/HighLevelServerExample.java - :include: high-level-server-example - -The core of the Routing DSL becomes available with a single import:: - - import akka.http.javadsl.server.Directives.*; - -Or by extending the ``akka.http.javadsl.server.AllDirectives`` class which brings together all directives into a single class -for easier access:: - - extends AllDirectives - -Of course it is possible to directly import only the directives you need (i.e. ``WebSocketDirectives`` etc). - -.. _DRY: http://en.wikipedia.org/wiki/Don%27t_repeat_yourself - -.. _handling-http-server-failures-high-level-java: - diff --git a/akka-docs/rst/java/http/routing-dsl/rejections.rst b/akka-docs/rst/java/http/routing-dsl/rejections.rst deleted file mode 100644 index 5edc92c826..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/rejections.rst +++ /dev/null @@ -1,137 +0,0 @@ -.. _rejections-java: - -Rejections -========== -TODO update to Java APIs - -In the chapter about constructing :ref:`Routes` the ``~`` operator was introduced, which connects two routes in a way -that allows a second route to get a go at a request if the first route "rejected" it. The concept of "rejections" is -used by Akka HTTP for maintaining a more functional overall architecture and in order to be able to properly -handle all kinds of error scenarios. - -When a filtering directive, like the :ref:`-get-` directive, cannot let the request pass through to its inner route because -the filter condition is not satisfied (e.g. because the incoming request is not a GET request) the directive doesn't -immediately complete the request with an error response. Doing so would make it impossible for other routes chained in -after the failing filter to get a chance to handle the request. -Rather, failing filters "reject" the request in the same way as by explicitly calling ``requestContext.reject(...)``. - -After having been rejected by a route the request will continue to flow through the routing structure and possibly find -another route that can complete it. If there are more rejections all of them will be picked up and collected. - -If the request cannot be completed by (a branch of) the route structure an enclosing :ref:`-handleRejections-java-` directive -can be used to convert a set of rejections into an ``HttpResponse`` (which, in most cases, will be an error response). -``Route.seal`` internally wraps its argument route with the :ref:`-handleRejections-java-` directive in order to "catch" -and handle any rejection. - - -Predefined Rejections ---------------------- - -A rejection encapsulates a specific reason why a route was not able to handle a request. It is modeled as an object of -type ``Rejection``. Akka HTTP comes with a set of `predefined rejections`__, which are used by the many -:ref:`predefined directives `. - -Rejections are gathered up over the course of a Route evaluation and finally converted to ``HttpResponse`` replies by -the :ref:`-handleRejections-` directive if there was no way for the request to be completed. - -__ @github@/akka-http/src/main/scala/akka/http/scaladsl/server/Rejection.scala - - -.. _The RejectionHandler-java: - -The RejectionHandler --------------------- - -The :ref:`-handleRejections-` directive delegates the actual job of converting a list of rejections to its argument, a -RejectionHandler__, which is defined like this:: - - trait RejectionHandler extends (immutable.Seq[Rejection] ⇒ Option[Route]) - -__ @github@/akka-http/src/main/scala/akka/http/scaladsl/server/RejectionHandler.scala - -Since a ``RejectionHandler`` returns an ``Option[Route]`` it can choose whether it would like to handle the current set -of rejections or not. If it returns ``None`` the rejections will simply continue to flow through the route structure. - -The default ``RejectionHandler`` applied by the top-level glue code that turns a ``Route`` into a -``Flow`` or async handler function for the :ref:`low-level API ` (via -``Route.handlerFlow`` or ``Route.asyncHandler``) will handle *all* rejections that reach it. - - -Rejection Cancellation ----------------------- - -As you can see from its definition above the ``RejectionHandler`` doesn't handle single rejections but a whole list of -them. This is because some route structure produce several "reasons" why a request could not be handled. - -Take this route structure for example: - -TODO missing sample - -For uncompressed POST requests this route structure would initially yield two rejections: - -- a ``MethodRejection`` produced by the :ref:`-get-` directive (which rejected because the request is not a GET request) -- an ``UnsupportedRequestEncodingRejection`` produced by the :ref:`-decodeRequestWith-` directive (which only accepts - gzip-compressed requests here) - -In reality the route even generates one more rejection, a ``TransformationRejection`` produced by the :ref:`-post-` -directive. It "cancels" all other potentially existing *MethodRejections*, since they are invalid after the -:ref:`-post-` directive allowed the request to pass (after all, the route structure *can* deal with POST requests). -These types of rejection cancellations are resolved *before* a ``RejectionHandler`` sees the rejection list. -So, for the example above the ``RejectionHandler`` will be presented with only a single-element rejection list, -containing nothing but the ``UnsupportedRequestEncodingRejection``. - - -.. _Empty Rejections-java: - -Empty Rejections ----------------- - -Since rejections are passed around in a list (or rather immutable ``Seq``) you might ask yourself what the semantics of -an empty rejection list are. In fact, empty rejection lists have well defined semantics. They signal that a request was -not handled because the respective resource could not be found. Akka HTTP reserves the special status of "empty -rejection" to this most common failure a service is likely to produce. - -So, for example, if the :ref:`-path-` directive rejects a request it does so with an empty rejection list. The -:ref:`-host-` directive behaves in the same way. - - -Customizing Rejection Handling ------------------------------- - -If you'd like to customize the way certain rejections are handled you'll have to write a custom -:ref:`RejectionHandler `. Here is an example: - -TODO missing sample - -The easiest way to construct a ``RejectionHandler`` is via the ``RejectionHandler.Builder`` that Akka HTTP provides. -After having created a new ``Builder`` instance with ``RejectionHandler.newBuilder()`` -you can attach handling logic for certain types of rejections through three helper methods: - -handle - Handles certain rejections with the given partial function. The partial function simply produces a ``Route`` which is - run when the rejection is "caught". This makes the full power of the Routing DSL available for defining rejection - handlers and even allows for recursing back into the main route structure if required. - -handleAll[T <: Rejection] - Handles all rejections of a certain type at the same time. This is useful for cases where your need access to more - than the first rejection of a certain type, e.g. for producing the error message to an unsupported request method. - -handleNotFound - As described :ref:`above ` "Resource Not Found" is special as it is represented with an empty - rejection set. The ``handleNotFound`` helper let's you specify the "recovery route" for this case. - -Even though you could handle several different rejection types in a single partial function supplied to ``handle`` -it is recommended to split these up into distinct ``handle`` attachments instead. -This way the priority between rejections is properly defined via the order of your ``handle`` clauses rather than the -(sometimes hard to predict or control) order of rejections in the rejection set. - -Once you have defined your custom ``RejectionHandler`` you have two options for "activating" it: - -1. Bring it into implicit scope at the top-level. -2. Supply it as argument to the :ref:`-handleRejections-` directive. - -In the first case your handler will be "sealed" (which means that it will receive the default handler as a fallback for -all cases your handler doesn't handle itself) and used for all rejections that are not handled within the route structure -itself. - -The second case allows you to restrict the applicability of your handler to certain branches of your route structure. diff --git a/akka-docs/rst/java/http/routing-dsl/routes.rst b/akka-docs/rst/java/http/routing-dsl/routes.rst deleted file mode 100644 index 7975242d7c..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/routes.rst +++ /dev/null @@ -1,116 +0,0 @@ -.. _routes-java: - -Routes -====== - -The "Route" is the central concept of Akka HTTP's Routing DSL. All the structures you build with the DSL, no matter -whether they consists of a single line or span several hundred lines, are functions turning a ``RequestContext`` into -a ``CompletionStage``. - -A ``Route`` itself is a function that operates on a ``RequestContext`` and returns a ``RouteResult``. The -``RequestContext`` is a data structure that contains the current request and auxiliary data like the so far unmatched -path of the request URI that gets passed through the route structure. It also contains the current ``ExecutionContext`` -and ``akka.stream.Materializer``, so that these don't have to be passed around manually. - -Generally when a route receives a request (or rather a ``RequestContext`` for it) it can do one of these things: - -- Complete the request by returning the value of ``requestContext.complete(...)`` -- Reject the request by returning the value of ``requestContext.reject(...)`` (see :ref:`rejections-java`) -- Fail the request by returning the value of ``requestContext.fail(...)`` or by just throwing an exception (see :ref:`exception-handling-java`) -- Do any kind of asynchronous processing and instantly return a ``Future[RouteResult]`` to be eventually completed later - -The first case is pretty clear, by calling ``complete`` a given response is sent to the client as reaction to the -request. In the second case "reject" means that the route does not want to handle the request. You'll see further down -in the section about route composition what this is good for. - -A ``Route`` can be "sealed" using ``Route.seal``, which relies on the in-scope ``RejectionHandler`` and ``ExceptionHandler`` -instances to convert rejections and exceptions into appropriate HTTP responses for the client. - -Using ``Route.handlerFlow`` or ``Route.asyncHandler`` a ``Route`` can be lifted into a handler ``Flow`` or async handler -function to be used with a ``bindAndHandleXXX`` call from the :ref:`http-low-level-server-side-api`. - - -.. _request-context-java: - -RequestContext --------------- - -The request context wraps an ``HttpRequest`` instance to enrich it with additional information that are typically -required by the routing logic, like an ``ExecutionContext``, ``Materializer``, ``LoggingAdapter`` and the configured -``RoutingSettings``. It also contains the ``unmatchedPath``, a value that describes how much of the request URI has not -yet been matched by a :ref:`Path Directive `. - -The ``RequestContext`` itself is immutable but contains several helper methods which allow for convenient creation of -modified copies. - -.. _route-result-java: - -RouteResult ------------ - -The ``RouteResult`` is an opaque structure that represents possible results of evaluating a route. A ``RouteResult`` -can only be created by using one of the methods of the ``RequestContext``. A result can either be a response, if -it was generated by one of the ``completeX`` methods, or a rejection that contains information about why the route -could not handle the request. - - -Composing Routes ----------------- - -Routes are composed to form the route tree in two principle ways. - -A route can be wrapped by a "Directive" which adds some behavioral aspect to its wrapped "inner route". In the Java DSL, -a Directive is a method that returns a Route. In many cases, a Directive method will have an inner route argument that is invoked -when its semantics decide to do so, e.g. when a URL path is matched. - -Example topics for directives include: - - * filtering requests to decide which requests will get to the inner route - * transforming the request before passing it to the inner route - * transforming the response (or more generally the route result) received from the inner route - * applying side-effects around inner route processing, such as measuring the time taken to run the inner route - -The other way of composition is defining a list of ``Route`` alternatives. Alternative routes are tried one after -the other until one route "accepts" the request and provides a response. Otherwise, a route can also "reject" a request, -in which case further alternatives are explored. Alternatives are specified by passing a list of routes to -to ``RouteDirectives.route()``. - -.. _The Routing Tree-java: - -The Routing Tree ----------------- - -Essentially, when you combine routes via nesting and alternative, you build a routing -structure that forms a tree. When a request comes in it is injected into this tree at the root and flows down through -all the branches in a depth-first manner until either some node completes it or it is fully rejected. - -Consider this schematic example. In place of directiveA, directiveB, etc., you can just imagine any of the available -directives, e.g. matching a particular path, header or request parameter.:: - - import static akka.http.javadsl.server.Directives.*; - - val route = - directiveA(route(() -> - directiveB(route(() -> - directiveC( - ... // route 1 - ), - directiveD( - ... // route 2 - ), - ... // route 3 - )), - directiveE( - ... // route 4 - ) - )); - -Here five directives form a routing tree. - -- Route 1 will only be reached if directives ``a``, ``b`` and ``c`` all let the request pass through. -- Route 2 will run if ``a`` and ``b`` pass, ``c`` rejects and ``d`` passes. -- Route 3 will run if ``a`` and ``b`` pass, but ``c`` and ``d`` reject. - -Route 3 can therefore be seen as a "catch-all" route that only kicks in, if routes chained into preceding positions -reject. This mechanism can make complex filtering logic quite easy to implement: simply put the most -specific cases up front and the most general cases in the back. diff --git a/akka-docs/rst/java/http/routing-dsl/source-streaming-support.rst b/akka-docs/rst/java/http/routing-dsl/source-streaming-support.rst deleted file mode 100644 index acf46421b6..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/source-streaming-support.rst +++ /dev/null @@ -1,91 +0,0 @@ -.. _json-streaming-java: - -Source Streaming -================ - -Akka HTTP supports completing a request with an Akka ``Source``, which makes it possible to easily build -and consume streaming end-to-end APIs which apply back-pressure throughout the entire stack. - -It is possible to complete requests with raw ``Source``, however often it is more convenient to -stream on an element-by-element basis, and allow Akka HTTP to handle the rendering internally - for example as a JSON array, -or CSV stream (where each element is separated by a new-line). - -In the following sections we investigate how to make use of the JSON Streaming infrastructure, -however the general hints apply to any kind of element-by-element streaming you could imagine. - -JSON Streaming -============== - -`JSON Streaming`_ is a term refering to streaming a (possibly infinite) stream of element as independent JSON -objects as a continuous HTTP request or response. The elements are most often separated using newlines, -however do not have to be. Concatenating elements side-by-side or emitting "very long" JSON array is also another -use case. - -In the below examples, we'll be refering to the ``Tweet`` and ``Measurement`` case classes as our model, which are defined as: - -.. includecode:: ../../code/docs/http/javadsl/server/JsonStreamingExamplesTest.java#models - -.. _Json Streaming: https://en.wikipedia.org/wiki/JSON_Streaming - -Responding with JSON Streams ----------------------------- - -In this example we implement an API representing an infinite stream of tweets, very much like Twitter's `Streaming API`_. - -Firstly, we'll need to get some additional marshalling infrastructure set up, that is able to marshal to and from an -Akka Streams ``Source``. Here we'll use the ``Jackson`` helper class from ``akka-http-jackson`` (a separate library -that you should add as a dependency if you want to use Jackson with Akka HTTP). - -First we enable JSON Streaming by making an implicit ``EntityStreamingSupport`` instance available (Step 1). - -The default mode of rendering a ``Source`` is to represent it as an JSON Array. If you want to change this representation -for example to use Twitter style new-line separated JSON objects, you can do so by configuring the support trait accordingly. - -In Step 1.1. we demonstrate to configure configude the rendering to be new-line separated, and also how parallel marshalling -can be applied. We configure the Support object to render the JSON as series of new-line separated JSON objects, -simply by providing the ``start``, ``sep`` and ``end`` ByteStrings, which will be emitted at the apropriate -places in the rendered stream. Although this format is *not* valid JSON, it is pretty popular since parsing it is relatively -simple - clients need only to find the new-lines and apply JSON unmarshalling for an entire line of JSON. - -The final step is simply completing a request using a Source of tweets, as simple as that: - -.. includecode:: ../../code/docs/http/javadsl/server/JsonStreamingExamplesTest.java#response-streaming - -.. _Streaming API: https://dev.twitter.com/streaming/overview - -Consuming JSON Streaming uploads --------------------------------- - -Sometimes the client may be sending a streaming request, for example an embedded device initiated a connection with -the server and is feeding it with one line of measurement data. - -In this example, we want to consume this data in a streaming fashion from the request entity, and also apply -back-pressure to the underlying TCP connection, if the server can not cope with the rate of incoming data (back-pressure -will be applied automatically thanks to using Akka HTTP/Streams). - -.. includecode:: ../../code/docs/http/javadsl/server/JsonStreamingExamplesTest.java#formats - -.. includecode:: ../../code/docs/http/javadsl/server/JsonStreamingExamplesTest.java#incoming-request-streaming - - -Simple CSV streaming example ----------------------------- - -Akka HTTP provides another ``EntityStreamingSupport`` out of the box, namely ``csv`` (comma-separated values). -For completeness, we demonstrate its usage in the below snippet. As you'll notice, switching betweeen streaming -modes is fairly simple, one only has to make sure that an implicit ``Marshaller`` of the requested type is available, -and that the streaming support operates on the same ``Content-Type`` as the rendered values. Otherwise you'll see -an error during runtime that the marshaller did not expose the expected content type and thus we can not render -the streaming response). - -.. includecode:: ../../code/docs/http/javadsl/server/JsonStreamingExamplesTest.java#csv-example - -Implementing custom EntityStreamingSupport traits -------------------------------------------------- - -The ``EntityStreamingSupport`` infrastructure is open for extension and not bound to any single format, content type -or marshalling library. The provided JSON support does not rely on Spray JSON directly, but uses ``Marshaller`` -instances, which can be provided using any JSON marshalling library (such as Circe, Jawn or Play JSON). - -When implementing a custom support trait, one should simply extend the ``EntityStreamingSupport`` abstract class, -and implement all of it's methods. It's best to use the existing implementations as a guideline. diff --git a/akka-docs/rst/java/http/routing-dsl/testkit.rst b/akka-docs/rst/java/http/routing-dsl/testkit.rst deleted file mode 100644 index ad1bac69cf..0000000000 --- a/akka-docs/rst/java/http/routing-dsl/testkit.rst +++ /dev/null @@ -1,80 +0,0 @@ -.. _http-testkit-java: - -Route Testkit -============= - -akka-http has a testkit that provides a convenient way of testing your routes with JUnit. It allows -running requests against a route (without hitting the network) and provides means to assert against -response properties in a compact way. - -To use the testkit you need to take these steps: - -* add a dependency to the ``akka-http-testkit`` module -* derive the test class from ``JUnitRouteTest`` -* wrap the route under test with ``RouteTest.testRoute`` to create a ``TestRoute`` -* run requests against the route using ``TestRoute.run(request)`` which will return - a ``TestResponse`` -* use the methods of ``TestResponse`` to assert on properties of the response - -Example -------- - -To see the testkit in action consider the following simple calculator app service: - -.. includecode:: ../../code/docs/http/javadsl/server/testkit/MyAppService.java - :include: simple-app - -The app extends from ``HttpApp`` which brings all of the directives into scope. Method ``createRoute`` -needs to be implemented to return the complete route of the app. - -Here's how you would test that service: - -.. includecode:: ../../code/docs/http/javadsl/server/testkit/TestkitExampleTest.java - :include: simple-app-testing - - -Writing Asserting against the HttpResponse ------------------------------------------- - -The testkit supports a fluent DSL to write compact assertions on the response by chaining assertions -using "dot-syntax". To simplify working with streamed responses the entity of the response is first "strictified", i.e. -entity data is collected into a single ``ByteString`` and provided the entity is supplied as an ``HttpEntityStrict``. This -allows to write several assertions against the same entity data which wouldn't (necessarily) be possible for the -streamed version. - -All of the defined assertions provide HTTP specific error messages aiding in diagnosing problems. - -Currently, these methods are defined on ``TestResponse`` to assert on the response: - -=================================================================== ======================================================================= -Assertion Description -=================================================================== ======================================================================= -``assertStatusCode(int expectedCode)`` Asserts that the numeric response status code equals the expected one -``assertStatusCode(StatusCode expectedCode)`` Asserts that the response ``StatusCode`` equals the expected one -``assertMediaType(String expectedType)`` Asserts that the media type part of the response's content type matches - the given String -``assertMediaType(MediaType expectedType)`` Asserts that the media type part of the response's content type matches - the given ``MediaType`` -``assertEntity(String expectedStringContent)`` Asserts that the entity data interpreted as UTF8 equals the expected - String -``assertEntityBytes(ByteString expectedBytes)`` Asserts that the entity data bytes equal the expected ones -``assertEntityAs(Unmarshaller unmarshaller, expectedValue: T)`` Asserts that the entity data if unmarshalled with the given marshaller - equals the given value -``assertHeaderExists(HttpHeader expectedHeader)`` Asserts that the response contains an HttpHeader instance equal to the - expected one -``assertHeaderKindExists(String expectedHeaderName)`` Asserts that the response contains a header with the expected name -``assertHeader(String name, String expectedValue)`` Asserts that the response contains a header with the given name and - value. -=================================================================== ======================================================================= - -It's, of course, possible to use any other means of writing assertions by inspecting the properties the response -manually. As written above, ``TestResponse.entity`` and ``TestResponse.response`` return strict versions of the -entity data. - -Supporting Custom Test Frameworks ---------------------------------- - -Adding support for a custom test framework is achieved by creating new superclass analogous to -``JUnitRouteTest`` for writing tests with the custom test framwork deriving from ``akka.http.javadsl.testkit.RouteTest`` -and implementing its abstract methods. This will allow users of the test framework to use ``testRoute`` and -to write assertions using the assertion methods defined on ``TestResponse``. \ No newline at end of file diff --git a/akka-docs/rst/java/http/server-side-https-support.rst b/akka-docs/rst/java/http/server-side-https-support.rst deleted file mode 100644 index f72aeaebd2..0000000000 --- a/akka-docs/rst/java/http/server-side-https-support.rst +++ /dev/null @@ -1,119 +0,0 @@ -.. _serverSideHTTPS-java: - -Server-Side HTTPS Support -========================= - -Akka HTTP supports TLS encryption on the server-side as well as on the :ref:`client-side `. - -The central vehicle for configuring encryption is the ``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 - -On the server-side the ``bind``, and ``bindAndHandleXXX`` methods of the `akka.http.javadsl.Http`_ extension define an -optional ``httpsContext`` parameter, which can receive the HTTPS configuration in the form of an ``HttpsContext`` -instance. -If defined encryption is enabled on all accepted connections. Otherwise it is disabled (which is the default). - -For detailed documentation for client-side HTTPS support refer to :ref:`clientSideHTTPS`. - - -.. _akka.http.javadsl.Http: https://github.com/akka/akka/blob/master/akka-http-core/src/main/scala/akka/http/javadsl/Http.scala - -.. _ssl-config-java: - -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/ - -In order to use SSL-Config in Akka so it logs to the right ActorSystem-wise logger etc., the -``AkkaSSLConfig`` extension is provided. Obtaining it is as simple as: - -.. includecode2:: ../code/docs/http/javadsl/server/HttpsServerExampleTest.java - :snippet: akka-ssl-config - -While typical usage, for example for configuring http client settings would be applied globally by configuring -ssl-config in ``application.conf``, it's possible to obtain the extension and ``copy`` it while modifying any -configuration that you might need to change and then use that specific ``AkkaSSLConfig`` instance while establishing -connections be it client or server-side. - -Obtaining SSL/TLS Certificates ------------------------------- -In order to run an HTTPS server a certificate has to be provided, which usually is either obtained from a signing -authority or created by yourself for local or staging environment purposes. - -Signing authorities often provide instructions on how to create a Java keystore (typically with reference to Tomcat -configuration). If you want to generate your own certificates, the official Oracle documentation on how to generate -keystores using the JDK keytool utility can be found `here `_. - -SSL-Config provides a more targeted guide on generating certificates, so we recommend you start with the guide -titled `Generating X.509 Certificates `_. - -.. _using-https-java: - -Using HTTPS ------------ - -Once you have obtained the server certificate, using it is as simple as preparing an ``HttpsConnectionContext`` -and either setting it as the default one to be used by all servers started by the given ``Http`` extension -or passing it in explicitly when binding the server. - -The below example shows how setting up HTTPS works when using the ``akka.http.javadsl.server.HttpApp`` convenience class. -Firstly you will create and configure an instance of ``akka.http.javadsl.HttpsConnectionContext`` : - -.. includecode2:: ../../../../akka-http-tests/src/main/java/akka/http/javadsl/server/examples/simple/SimpleServerApp.java - :snippet: https-http-config - -Then pass it to ``akka.http.javadsl.Http`` class's ``setDefaultServerHttpContext`` method, like in the below ``main`` method. - -.. includecode2:: ../../../../akka-http-tests/src/main/java/akka/http/javadsl/server/examples/simple/SimpleServerApp.java - :snippet: https-http-app - -Running both HTTP and HTTPS ---------------------------- -If you want to run HTTP and HTTPS servers in a single application, you can call ``bind...`` methods twice, -one for HTTPS, and the other for HTTP. - -When configuring HTTPS, you can do it up like explained in the above :ref:`using-https-java` section, - -.. includecode2:: ../../../../akka-http-tests/src/main/java/akka/http/javadsl/server/examples/simple/SimpleServerApp.java - :snippet: https-http-config - -or via :ref:`ssl-config-java` (not explained here though). - -Then, call ``bind...`` methods twice like below. -The blow ``SimpleServerApp.useHttps(system)`` is calling the above defined HTTP ``public static HttpsConnectionContext useHttps(ActorSystem system)`` method. - -.. includecode2:: ../../../../akka-http-tests/src/main/java/akka/http/javadsl/server/examples/simple/SimpleServerHttpHttpsApp.java - :snippet: both-https-and-http - -Further reading ---------------- - -The topic of properly configuring HTTPS for your web server is an always changing one, -thus we recommend staying up to date with various security breach news and of course -keep your JVM at the latest version possible, as the default settings are often updated by -Oracle in reaction to various security updates and known issues. - -We also recommend having a look at the `Play documentation about securing your app`_, -as well as the techniques described in the Play documentation about setting up a `reverse proxy to terminate TLS in -front of your application`_ instead of terminating TLS inside the JVM, and therefore Akka HTTP, itself. - -Other excellent articles on the subject: - -- `Oracle Java SE 8: Creating a Keystore using JSSE `_ -- `Java PKI Programmer's Guide `_ -- `Fixing X.509 Certificates `_ - -.. _Play documentation about securing your app: https://www.playframework.com/documentation/2.5.x/ConfiguringHttps#ssl-certificates -.. _reverse proxy to terminate TLS in front of your application: https://www.playframework.com/documentation/2.5.x/HTTPServer \ No newline at end of file diff --git a/akka-docs/rst/java/http/server-side/low-level-server-side-api.rst b/akka-docs/rst/java/http/server-side/low-level-server-side-api.rst deleted file mode 100644 index 4c865ed90f..0000000000 --- a/akka-docs/rst/java/http/server-side/low-level-server-side-api.rst +++ /dev/null @@ -1,240 +0,0 @@ -.. _http-low-level-server-side-api-java: - -Low-Level Server-Side API -========================= - -Apart from the :ref:`HTTP Client ` Akka HTTP also provides an embedded, -`Reactive-Streams`_-based, fully asynchronous HTTP/1.1 server implemented on top of :ref:`Akka Stream `. - -It sports the following features: - -- Full support for `HTTP persistent connections`_ -- Full support for `HTTP pipelining`_ -- Full support for asynchronous HTTP streaming including "chunked" transfer encoding accessible through an idiomatic API -- Optional SSL/TLS encryption -- WebSocket support - -.. _HTTP persistent connections: http://en.wikipedia.org/wiki/HTTP_persistent_connection -.. _HTTP pipelining: http://en.wikipedia.org/wiki/HTTP_pipelining -.. _Reactive-Streams: http://www.reactive-streams.org/ - -The server-side components of Akka HTTP are split into two layers: - -1. The basic low-level server implementation in the ``akka-http-core`` module -2. Higher-level functionality in the ``akka-http`` module - -The low-level server (1) is scoped with a clear focus on the essential functionality of an HTTP/1.1 server: - -- Connection management -- Parsing and rendering of messages and headers -- Timeout management (for requests and connections) -- Response ordering (for transparent pipelining support) - -All non-core features of typical HTTP servers (like request routing, file serving, compression, etc.) are left to -the higher layers, they are not implemented by the ``akka-http-core``-level server itself. -Apart from general focus this design keeps the server core small and light-weight as well as easy to understand and -maintain. - -Depending on your needs you can either use the low-level API directly or rely on the high-level -:ref:`Routing DSL ` which can make the definition of more complex service logic much -easier. - - -Streams and HTTP ----------------- - -The Akka HTTP server is implemented on top of :ref:`Akka Stream ` and makes heavy use of it - in its -implementation as well as on all levels of its API. - -On the connection level Akka HTTP offers basically the same kind of interface as :ref:`Akka Stream IO `: -A socket binding is represented as a stream of incoming connections. The application pulls connections from this stream -source and, for each of them, provides a ``Flow`` to "translate" requests into responses. - -Apart from regarding a socket bound on the server-side as a ``Source`` and each connection as a -``Source`` with a ``Sink`` the stream abstraction is also present inside a single HTTP -message: The entities of HTTP requests and responses are generally modeled as a ``Source``. See also -the :ref:`http-model-java` for more information on how HTTP messages are represented in Akka HTTP. - - -Starting and Stopping ---------------------- - -On the most basic level an Akka HTTP server is bound by invoking the ``bind`` method of the `akka.http.javadsl.Http`_ -extension: - -.. includecode:: ../../code/docs/http/javadsl/server/HttpServerExampleDocTest.java - :include: binding-example - -Arguments to the ``Http().bind`` method specify the interface and port to bind to and register interest in handling -incoming HTTP connections. Additionally, the method also allows for the definition of socket options as well as a larger -number of settings for configuring the server according to your needs. - -The result of the ``bind`` method is a ``Source`` which must be drained by the application in -order to accept incoming connections. -The actual binding is not performed before this source is materialized as part of a processing pipeline. In -case the bind fails (e.g. because the port is already busy) the materialized stream will immediately be terminated with -a respective exception. -The binding is released (i.e. the underlying socket unbound) when the subscriber of the incoming -connection source has cancelled its subscription. Alternatively one can use the ``unbind()`` method of the -``Http.ServerBinding`` instance that is created as part of the connection source's materialization process. -The ``Http.ServerBinding`` also provides a way to get a hold of the actual local address of the bound socket, which is -useful for example when binding to port zero (and thus letting the OS pick an available port). - -.. _akka.http.javadsl.Http: @github@/akka-http-core/src/main/scala/akka/http/javadsl/Http.scala - -.. _http-low-level-server-side-example-java: - -Request-Response Cycle ----------------------- - -When a new connection has been accepted it will be published as an ``Http.IncomingConnection`` which consists -of the remote address and methods to provide a ``Flow`` to handle requests coming in over -this connection. - -Requests are handled by calling one of the ``handleWithXXX`` methods with a handler, which can either be - -- a ``Flow`` for ``handleWith``, -- a function ``Function`` for ``handleWithSyncHandler``, -- a function ``Function>`` for ``handleWithAsyncHandler``. - -Here is a complete example: - -.. includecode:: ../../code/docs/http/javadsl/server/HttpServerExampleDocTest.java - :include: full-server-example - -In this example, a request is handled by transforming the request stream with a function ``Function`` -using ``handleWithSyncHandler`` (or equivalently, Akka Stream's ``map`` operator). Depending on the use case many -other ways of providing a request handler are conceivable using Akka Stream's combinators. - -If the application provides a ``Flow`` it is also the responsibility of the application to generate exactly one response -for every request and that the ordering of responses matches the ordering of the associated requests (which is relevant -if HTTP pipelining is enabled where processing of multiple incoming requests may overlap). When relying on -``handleWithSyncHandler`` or ``handleWithAsyncHandler``, or the ``map`` or ``mapAsync`` stream operators, this -requirement will be automatically fulfilled. - -See :ref:`http-routing-java` for a more convenient high-level DSL to create request handlers. - -Streaming Request/Response Entities -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Streaming of HTTP message entities is supported through subclasses of ``HttpEntity``. The application needs to be able -to deal with streamed entities when receiving a request as well as, in many cases, when constructing responses. -See :ref:`HttpEntity-java` for a description of the alternatives. - -.. _http-closing-connection-low-level-java: - -Closing a connection -~~~~~~~~~~~~~~~~~~~~ - -The HTTP connection will be closed when the handling ``Flow`` cancels its upstream subscription or the peer closes the -connection. An often times more convenient alternative is to explicitly add a ``Connection: close`` header to an -``HttpResponse``. This response will then be the last one on the connection and the server will actively close the -connection when it has been sent out. - -Connection will also be closed if request entity has been 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 entity should be -explicitly drained by attaching it to ``Sink.ignore()``. - -.. _http-server-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 server-side the stand-alone HTTP layer forms a ``BidiFlow``, -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).serverLayer`` method, -which also allows for varying degrees of configuration. Note, that the returned instance is not reusable and can only -be materialized once. - -Controlling server parallelism ------------------------------- - -Request handling can be parallelized on two axes, by handling several connections in parallel and by -relying on HTTP pipelining to send several requests on one connection without waiting for a response first. In both -cases the client controls the number of ongoing requests. To prevent being overloaded by too many requests, Akka HTTP -can limit the number of requests it handles in parallel. - -To limit the number of simultaneously open connections, use the ``akka.http.server.max-connections`` setting. This setting -applies to all of ``Http.bindAndHandle*`` methods. If you use ``Http.bind``, incoming connections are represented by -a ``Source``. Use Akka Stream's combinators to apply backpressure to control the flow of -incoming connections, e.g. by using ``throttle`` or ``mapAsync``. - -HTTP pipelining is generally discouraged (and `disabled by most browsers `_) but -is nevertheless fully supported in Akka HTTP. The limit is applied on two levels. First, there's the -``akka.http.server.pipeline-limit`` config setting which prevents that more than the given number of outstanding requests -is ever given to the user-supplied handler-flow. On the other hand, the handler flow itself can apply any kind of throttling -itself. If you use one of the ``Http.bindAndHandleSync`` or ``Http.bindAndHandleAsync`` -entry-points, you can specify the ``parallelism`` argument (default = 1, i.e. pipelining disabled) to control the -number of concurrent requests per connection. If you use ``Http.bindAndHandle`` or ``Http.bind``, the user-supplied handler -flow has full control over how many request it accepts simultaneously by applying backpressure. In this case, you can -e.g. use Akka Stream's ``mapAsync`` combinator with a given parallelism to limit the number of concurrently handled requests. -Effectively, the more constraining one of these two measures, config setting and manual flow shaping, will determine -how parallel requests on one connection are handled. - - -.. _handling-http-server-failures-low-level-java: - -Handling HTTP Server failures in the Low-Level API --------------------------------------------------- - -There are various situations when failure may occur while initialising or running an Akka HTTP server. -Akka by default will log all these failures, however sometimes one may want to react to failures in addition to them -just being logged, for example by shutting down the actor system, or notifying some external monitoring end-point explicitly. - -There are multiple things that can fail when creating and materializing an HTTP Server (similarily, the same applied to -a plain streaming ``Tcp`` server). The types of failures that can happen on different layers of the stack, starting -from being unable to start the server, and ending with failing to unmarshal an HttpRequest, examples of failures include -(from outer-most, to inner-most): - -- Failure to ``bind`` to the specified address/port, -- Failure while accepting new ``IncommingConnection`` s, for example when the OS has run out of file descriptors or memory, -- Failure while handling a connection, for example if the incoming ``HttpRequest`` is malformed. - -This section describes how to handle each failure situation, and in which situations these failures may occur. - -Bind failures -^^^^^^^^^^^^^ - -The first type of failure is when the server is unable to bind to the given port. For example when the port -is already taken by another application, or if the port is privileged (i.e. only usable by ``root``). -In this case the "binding future" will fail immediatly, and we can react to if by listening on the CompletionStage’s completion: - -.. includecode:: ../../code/docs/http/javadsl/server/HttpServerExampleDocTest.java - :include: binding-failure-handling - -Once the server has successfully bound to a port, the ``Source`` starts running and emiting -new incoming connections. This source technically can signal a failure as well, however this should only happen in very -dramantic situations such as running out of file descriptors or memory available to the system, such that it's not able -to accept a new incoming connection. Handling failures in Akka Streams is pretty stright forward, as failures are signaled -through the stream starting from the stage which failed, all the way downstream to the final stages. - -Connections Source failures -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -In the example below we add a custom ``GraphStage`` (see :ref:`stream-customize-java`) in order to react to the -stream's failure. We signal a ``failureMonitor`` actor with the cause why the stream is going down, and let the Actor -handle the rest – maybe it'll decide to restart the server or shutdown the ActorSystem, that however is not our concern anymore. - -.. includecode:: ../../code/docs/http/javadsl/server/HttpServerExampleDocTest.java - :include: incoming-connections-source-failure-handling - -Connection failures -^^^^^^^^^^^^^^^^^^^ - -The third type of failure that can occur is when the connection has been properly established, -however afterwards is terminated abruptly – for example by the client aborting the underlying TCP connection. -To handle this failure we can use the same pattern as in the previous snippet, however apply it to the connection's Flow: - -.. includecode:: ../../code/docs/http/javadsl/server/HttpServerExampleDocTest.java - :include: connection-stream-failure-handling - -These failures can be described more or less infrastructure related, they are failing bindings or connections. -Most of the time you won't need to dive into those very deeply, as Akka will simply log errors of this kind -anyway, which is a reasonable default for such problems. diff --git a/akka-docs/rst/java/http/server-side/websocket-support.rst b/akka-docs/rst/java/http/server-side/websocket-support.rst deleted file mode 100644 index c4d699431f..0000000000 --- a/akka-docs/rst/java/http/server-side/websocket-support.rst +++ /dev/null @@ -1,131 +0,0 @@ -.. _server-side-websocket-support-java: - -Server-Side WebSocket Support -============================= - -WebSocket is a protocol that provides a bi-directional channel between browser and webserver usually run over an -upgraded HTTP(S) connection. Data is exchanged in messages whereby a message can either be binary data or unicode text. - -Akka HTTP provides a stream-based implementation of the WebSocket protocol that hides the low-level details of the -underlying binary framing wire-protocol and provides a simple API to implement services using WebSocket. - - -Model ------ - -The basic unit of data exchange in the WebSocket protocol is a message. A message can either be binary message, -i.e. a sequence of octets or a text message, i.e. a sequence of unicode code points. - -In the data model the two kinds of messages, binary and text messages, are represented by the two classes -``BinaryMessage`` and ``TextMessage`` deriving from a common superclass ``Message``. The superclass ``Message`` -contains ``isText`` and ``isBinary`` methods to distinguish a message and ``asBinaryMessage`` and ``asTextMessage`` -methods to cast a message. - -The subclasses ``BinaryMessage`` and ``TextMessage`` contain methods to access the data. Take the API of -``TextMessage`` as an example (``BinaryMessage`` is very similar with ``String`` replaced by ``ByteString``): - -.. includecode:: /../../akka-http-core/src/main/scala/akka/http/javadsl/model/ws/Message.scala - :include: message-model - -The data of a message is provided as a stream because WebSocket messages do not have a predefined size and could -(in theory) be infinitely long. However, only one message can be open per direction of the WebSocket connection, -so that many application level protocols will want to make use of the delineation into (small) messages to transport -single application-level data units like "one event" or "one chat message". - -Many messages are small enough to be sent or received in one go. As an opportunity for optimization, the model provides -the notion of a "strict" message to represent cases where a whole message was received in one go. If -``TextMessage.isStrict`` returns true, the complete data is already available and can be accessed with -``TextMessage.getStrictText`` (analogously for ``BinaryMessage``). - -When receiving data from the network connection the WebSocket implementation tries to create a strict message whenever -possible, i.e. when the complete data was received in one chunk. However, the actual chunking of messages over a network -connection and through the various streaming abstraction layers is not deterministic from the perspective of the -application. Therefore, application code must be able to handle both streamed and strict messages and not expect -certain messages to be strict. (Particularly, note that tests against ``localhost`` will behave differently than tests -against remote peers where data is received over a physical network connection.) - -For sending data, you can use the static ``TextMessage.create(String)`` method to create a strict message if the -complete message has already been assembled. Otherwise, use ``TextMessage.create(Source)`` to create -a streaming message from an Akka Stream source. - - -Server API ----------- - -The entrypoint for the WebSocket API is the synthetic ``UpgradeToWebSocket`` header which is added to a request -if Akka HTTP encounters a WebSocket upgrade request. - -The WebSocket specification mandates that details of the WebSocket connection are negotiated by placing special-purpose -HTTP-headers into request and response of the HTTP upgrade. In Akka HTTP these HTTP-level details of the WebSocket -handshake are hidden from the application and don't need to be managed manually. - -Instead, the synthetic ``UpgradeToWebSocket`` represents a valid WebSocket upgrade request. An application can detect -a WebSocket upgrade request by looking for the ``UpgradeToWebSocket`` header. It can choose to accept the upgrade and -start a WebSocket connection by responding to that request with an ``HttpResponse`` generated by one of the -``UpgradeToWebSocket.handleMessagesWith`` methods. In its most general form this method expects two arguments: -first, a handler ``Flow`` that will be used to handle WebSocket messages on this connection. -Second, the application can optionally choose one of the proposed application-level sub-protocols by inspecting the -values of ``UpgradeToWebSocket.getRequestedProtocols`` and pass the chosen protocol value to ``handleMessagesWith``. - -Handling Messages -+++++++++++++++++ - -A message handler is expected to be implemented as a ``Flow``. For typical request-response -scenarios this fits very well and such a ``Flow`` can be constructed from a simple function by using -``Flow.create().map`` or ``Flow.create().mapAsync``. - -There are other use-cases, e.g. in a server-push model, where a server message is sent spontaneously, or in a -true bi-directional scenario where input and output aren't logically connected. Providing the handler as a ``Flow`` in -these cases may not fit. An overload of ``UpgradeToWebSocket.handleMessagesWith`` is provided, instead, -which allows to pass an output-generating ``Source`` and an input-receiving ``Sink`` independently. - -Note that a handler is required to consume the data stream of each message to make place for new messages. Otherwise, -subsequent messages may be stuck and message traffic in this direction will stall. - -Example -+++++++ - -Let's look at an example_. - -WebSocket requests come in like any other requests. In the example, requests to ``/greeter`` are expected to be -WebSocket requests: - -.. includecode:: ../../code/docs/http/javadsl/server/WebSocketCoreExample.java - :include: websocket-handling - -It uses a helper method ``akka.http.javadsl.model.ws.WebSocket.handleWebSocketRequestWith`` which can be used if -only WebSocket requests are expected. The method looks for the ``UpgradeToWebSocket`` header and returns a response -that will install the passed WebSocket handler if the header is found. If the request is no WebSocket request it will -return a ``400 Bad Request`` error response. - -In the example, the passed handler expects text messages where each message is expected to contain (a person's) name -and then responds with another text message that contains a greeting: - -.. includecode:: ../../code/docs/http/javadsl/server/WebSocketCoreExample.java - :include: websocket-handler - -.. note:: - Inactive WebSocket connections will be dropped according to the :ref:`idle-timeout settings `. - In case you need to keep inactive connections alive, you can either tweak your idle-timeout or inject - 'keep-alive' messages regularly. - -Routing support ---------------- - -The routing DSL provides the ``handleWebSocketMessages`` directive to install a WebSocket handler if a request -is a WebSocket request. Otherwise, the directive rejects the request. - -Let's look at how the above example can be rewritten using the high-level routing DSL. - -Instead of writing the request handler manually, the routing behavior of the app is defined by a route that -uses the ``handleWebSocketRequests`` directive in place of the ``WebSocket.handleWebSocketRequestWith``: - -.. includecode:: ../../code/docs/http/javadsl/server/WebSocketRoutingExample.java - :include: websocket-route - -The handling code itself will be the same as with using the low-level API. - -See the `full routing example`_. - -.. _example: @github@/akka-docs/rst/java/code/docs/http/javadsl/server/WebSocketCoreExample.java -.. _full routing example: @github@/akka-docs/rst/java/code/docs/http/javadsl/server/WebSocketCoreExample.java diff --git a/akka-docs/rst/java/stream/stream-io.rst b/akka-docs/rst/java/stream/stream-io.rst index 6eac487fe3..69265c7c99 100644 --- a/akka-docs/rst/java/stream/stream-io.rst +++ b/akka-docs/rst/java/stream/stream-io.rst @@ -9,11 +9,6 @@ While the general approach is very similar to the :ref:`Actor based TCP handling by using Akka Streams you are freed of having to manually react to back-pressure signals, as the library does it transparently for you. -.. note:: - If you are not familiar with Akka Streams basic concepts, like ``Source``, ``Sink`` and ``Flow``, - please refer to the :ref:`Streams section ` of the document. Also higher level APIs like ``bind`` are - used in Akka HTTP too. So you may get some hints from the :ref:`Akka HTTP introduction `. - Streaming TCP ============= diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/HttpClientExampleSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/HttpClientExampleSpec.scala deleted file mode 100644 index c16dc1efc1..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/HttpClientExampleSpec.scala +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl - -import akka.Done -import akka.actor.{ ActorLogging, ActorSystem } -import akka.http.scaladsl.model.HttpEntity.Strict -import akka.http.scaladsl.model.HttpMessage.DiscardedEntity -import akka.stream.{ IOResult, Materializer } -import akka.stream.scaladsl.{ Framing, Sink } -import akka.util.ByteString -import docs.CompileOnlySpec -import org.scalatest.{ Matchers, WordSpec } - -import scala.concurrent.{ ExecutionContextExecutor, Future } - -class HttpClientExampleSpec extends WordSpec with Matchers with CompileOnlySpec { - - "manual-entity-consume-example-1" in compileOnlySpec { - //#manual-entity-consume-example-1 - import java.io.File - import akka.actor.ActorSystem - import akka.stream.ActorMaterializer - import akka.stream.scaladsl.Framing - import akka.stream.scaladsl.FileIO - import akka.http.scaladsl.model._ - - implicit val system = ActorSystem() - implicit val dispatcher = system.dispatcher - implicit val materializer = ActorMaterializer() - - val response: HttpResponse = ??? - - response.entity.dataBytes - .via(Framing.delimiter(ByteString("\n"), maximumFrameLength = 256)) - .map(transformEachLine) - .runWith(FileIO.toPath(new File("/tmp/example.out").toPath)) - - def transformEachLine(line: ByteString): ByteString = ??? - - //#manual-entity-consume-example-1 - } - - "manual-entity-consume-example-2" in compileOnlySpec { - //#manual-entity-consume-example-2 - import java.io.File - import akka.actor.ActorSystem - import akka.stream.ActorMaterializer - import akka.http.scaladsl.model._ - import scala.concurrent.duration._ - - implicit val system = ActorSystem() - implicit val dispatcher = system.dispatcher - implicit val materializer = ActorMaterializer() - - case class ExamplePerson(name: String) - def parse(line: ByteString): ExamplePerson = ??? - - val response: HttpResponse = ??? - - // toStrict to enforce all data be loaded into memory from the connection - val strictEntity: Future[HttpEntity.Strict] = response.entity.toStrict(3.seconds) - - // while API remains the same to consume dataBytes, now they're in memory already: - val transformedData: Future[ExamplePerson] = - strictEntity flatMap { e => - e.dataBytes - .runFold(ByteString.empty) { case (acc, b) => acc ++ b } - .map(parse) - } - - //#manual-entity-consume-example-2 - } - - "manual-entity-discard-example-1" in compileOnlySpec { - //#manual-entity-discard-example-1 - import akka.actor.ActorSystem - import akka.stream.ActorMaterializer - import akka.http.scaladsl.model._ - - implicit val system = ActorSystem() - implicit val dispatcher = system.dispatcher - implicit val materializer = ActorMaterializer() - - val response1: HttpResponse = ??? // obtained from an HTTP call (see examples below) - - val discarded: DiscardedEntity = response1.discardEntityBytes() - discarded.future.onComplete { case done => println("Entity discarded completely!") } - - //#manual-entity-discard-example-1 - } - "manual-entity-discard-example-2" in compileOnlySpec { - import akka.actor.ActorSystem - import akka.stream.ActorMaterializer - import akka.http.scaladsl.model._ - - implicit val system = ActorSystem() - implicit val dispatcher = system.dispatcher - implicit val materializer = ActorMaterializer() - - //#manual-entity-discard-example-2 - val response1: HttpResponse = ??? // obtained from an HTTP call (see examples below) - - val discardingComplete: Future[Done] = response1.entity.dataBytes.runWith(Sink.ignore) - discardingComplete.onComplete { case done => println("Entity discarded completely!") } - //#manual-entity-discard-example-2 - } - - "outgoing-connection-example" in compileOnlySpec { - //#outgoing-connection-example - import akka.actor.ActorSystem - import akka.http.scaladsl.Http - import akka.http.scaladsl.model._ - import akka.stream.ActorMaterializer - import akka.stream.scaladsl._ - - import scala.concurrent.Future - import scala.util.{ Failure, Success } - - object WebClient { - def main(args: Array[String]): Unit = { - implicit val system = ActorSystem() - implicit val materializer = ActorMaterializer() - implicit val executionContext = system.dispatcher - - val connectionFlow: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] = - Http().outgoingConnection("akka.io") - val responseFuture: Future[HttpResponse] = - Source.single(HttpRequest(uri = "/")) - .via(connectionFlow) - .runWith(Sink.head) - - responseFuture.andThen { - case Success(_) => println("request succeded") - case Failure(_) => println("request failed") - }.andThen { - case _ => system.terminate() - } - } - } - //#outgoing-connection-example - } - - "host-level-example" in compileOnlySpec { - //#host-level-example - import akka.http.scaladsl.Http - import akka.http.scaladsl.model._ - import akka.stream.ActorMaterializer - import akka.stream.scaladsl._ - - import scala.concurrent.Future - import scala.util.Try - - implicit val system = ActorSystem() - implicit val materializer = ActorMaterializer() - // construct a pool client flow with context type `Int` - val poolClientFlow = Http().cachedHostConnectionPool[Int]("akka.io") - val responseFuture: Future[(Try[HttpResponse], Int)] = - Source.single(HttpRequest(uri = "/") -> 42) - .via(poolClientFlow) - .runWith(Sink.head) - //#host-level-example - } - - "single-request-example" in compileOnlySpec { - //#single-request-example - import akka.http.scaladsl.Http - import akka.http.scaladsl.model._ - import akka.stream.ActorMaterializer - - import scala.concurrent.Future - import scala.util.{ Failure, Success } - - implicit val system = ActorSystem() - implicit val materializer = ActorMaterializer() - - val responseFuture: Future[HttpResponse] = - Http().singleRequest(HttpRequest(uri = "http://akka.io")) - //#single-request-example - } - - "single-request-in-actor-example" in compileOnlySpec { - //#single-request-in-actor-example - import akka.actor.Actor - import akka.http.scaladsl.Http - import akka.http.scaladsl.model._ - import akka.stream.ActorMaterializer - import akka.stream.ActorMaterializerSettings - - class Myself extends Actor - with ActorLogging { - - import akka.pattern.pipe - import context.dispatcher - - final implicit val materializer: ActorMaterializer = ActorMaterializer(ActorMaterializerSettings(context.system)) - - val http = Http(context.system) - - override def preStart() = { - http.singleRequest(HttpRequest(uri = "http://akka.io")) - .pipeTo(self) - } - - def receive = { - case HttpResponse(StatusCodes.OK, headers, entity, _) => - log.info("Got response, body: " + entity.dataBytes.runFold(ByteString(""))(_ ++ _)) - case resp @ HttpResponse(code, _, _, _) => - log.info("Request failed, response code: " + code) - resp.discardEntityBytes() - } - - } - //#single-request-in-actor-example - } - -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/HttpServerExampleSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/HttpServerExampleSpec.scala deleted file mode 100644 index d91d819b59..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/HttpServerExampleSpec.scala +++ /dev/null @@ -1,682 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl - -import akka.event.LoggingAdapter -import akka.http.scaladsl.model.{ RequestEntity, StatusCodes } -import akka.stream.scaladsl.Sink -import akka.testkit.TestActors -import docs.CompileOnlySpec -import org.scalatest.{ Matchers, WordSpec } - -import scala.language.postfixOps -import scala.concurrent.{ ExecutionContext, Future } - -class HttpServerExampleSpec extends WordSpec with Matchers - with CompileOnlySpec { - - // never actually called - val log: LoggingAdapter = null - - "binding-example" in compileOnlySpec { - import akka.actor.ActorSystem - import akka.http.scaladsl.Http - import akka.stream.ActorMaterializer - import akka.stream.scaladsl._ - - implicit val system = ActorSystem() - implicit val materializer = ActorMaterializer() - implicit val executionContext = system.dispatcher - - val serverSource: Source[Http.IncomingConnection, Future[Http.ServerBinding]] = - Http().bind(interface = "localhost", port = 8080) - val bindingFuture: Future[Http.ServerBinding] = - serverSource.to(Sink.foreach { connection => // foreach materializes the source - println("Accepted new connection from " + connection.remoteAddress) - // ... and then actually handle the connection - }).run() - } - - "binding-failure-high-level-example" in compileOnlySpec { - import akka.actor.ActorSystem - import akka.http.scaladsl.Http - import akka.http.scaladsl.Http.ServerBinding - import akka.http.scaladsl.server.Directives._ - import akka.stream.ActorMaterializer - - import scala.concurrent.Future - - object WebServer { - def main(args: Array[String]) { - implicit val system = ActorSystem() - implicit val materializer = ActorMaterializer() - // needed for the future onFailure in the end - implicit val executionContext = system.dispatcher - - val handler = get { - complete("Hello world!") - } - - // let's say the OS won't allow us to bind to 80. - val (host, port) = ("localhost", 80) - val bindingFuture: Future[ServerBinding] = - Http().bindAndHandle(handler, host, port) - - bindingFuture.onFailure { - case ex: Exception => - log.error(ex, "Failed to bind to {}:{}!", host, port) - } - } - } - } - - // mock values: - val handleConnections = { - import akka.stream.scaladsl.Sink - Sink.ignore.mapMaterializedValue(_ => Future.failed(new Exception(""))) - } - - "binding-failure-handling" in compileOnlySpec { - import akka.actor.ActorSystem - import akka.http.scaladsl.Http - import akka.http.scaladsl.Http.ServerBinding - import akka.stream.ActorMaterializer - - import scala.concurrent.Future - - implicit val system = ActorSystem() - implicit val materializer = ActorMaterializer() - // needed for the future onFailure in the end - implicit val executionContext = system.dispatcher - - // let's say the OS won't allow us to bind to 80. - val (host, port) = ("localhost", 80) - val serverSource = Http().bind(host, port) - - val bindingFuture: Future[ServerBinding] = serverSource - .to(handleConnections) // Sink[Http.IncomingConnection, _] - .run() - - bindingFuture.onFailure { - case ex: Exception => - log.error(ex, "Failed to bind to {}:{}!", host, port) - } - } - - object MyExampleMonitoringActor { - def props = TestActors.echoActorProps - } - - "incoming-connections-source-failure-handling" in compileOnlySpec { - import akka.actor.ActorSystem - import akka.actor.ActorRef - import akka.http.scaladsl.Http - import akka.stream.ActorMaterializer - import akka.stream.scaladsl.Flow - - implicit val system = ActorSystem() - implicit val materializer = ActorMaterializer() - implicit val executionContext = system.dispatcher - - import Http._ - val (host, port) = ("localhost", 8080) - val serverSource = Http().bind(host, port) - - val failureMonitor: ActorRef = system.actorOf(MyExampleMonitoringActor.props) - - val reactToTopLevelFailures = Flow[IncomingConnection] - .watchTermination()((_, termination) => termination.onFailure { - case cause => failureMonitor ! cause - }) - - serverSource - .via(reactToTopLevelFailures) - .to(handleConnections) // Sink[Http.IncomingConnection, _] - .run() - } - - "connection-stream-failure-handling" in compileOnlySpec { - import akka.actor.ActorSystem - import akka.http.scaladsl.Http - import akka.http.scaladsl.model._ - import akka.stream.ActorMaterializer - import akka.stream.scaladsl.Flow - - implicit val system = ActorSystem() - implicit val materializer = ActorMaterializer() - implicit val executionContext = system.dispatcher - - val (host, port) = ("localhost", 8080) - val serverSource = Http().bind(host, port) - - val reactToConnectionFailure = Flow[HttpRequest] - .recover[HttpRequest] { - case ex => - // handle the failure somehow - throw ex - } - - val httpEcho = Flow[HttpRequest] - .via(reactToConnectionFailure) - .map { request => - // simple streaming (!) "echo" response: - HttpResponse(entity = HttpEntity(ContentTypes.`text/plain(UTF-8)`, request.entity.dataBytes)) - } - - serverSource - .runForeach { con => - con.handleWith(httpEcho) - } - } - - "full-server-example" in compileOnlySpec { - import akka.actor.ActorSystem - import akka.http.scaladsl.Http - import akka.http.scaladsl.model.HttpMethods._ - import akka.http.scaladsl.model._ - import akka.stream.ActorMaterializer - import akka.stream.scaladsl.Sink - - implicit val system = ActorSystem() - implicit val materializer = ActorMaterializer() - implicit val executionContext = system.dispatcher - - val serverSource = Http().bind(interface = "localhost", port = 8080) - - val requestHandler: HttpRequest => HttpResponse = { - case HttpRequest(GET, Uri.Path("/"), _, _, _) => - HttpResponse(entity = HttpEntity( - ContentTypes.`text/html(UTF-8)`, - "Hello world!")) - - case HttpRequest(GET, Uri.Path("/ping"), _, _, _) => - HttpResponse(entity = "PONG!") - - case HttpRequest(GET, Uri.Path("/crash"), _, _, _) => - sys.error("BOOM!") - - case r: HttpRequest => - r.discardEntityBytes() // important to drain incoming HTTP Entity stream - HttpResponse(404, entity = "Unknown resource!") - } - - val bindingFuture: Future[Http.ServerBinding] = - serverSource.to(Sink.foreach { connection => - println("Accepted new connection from " + connection.remoteAddress) - - connection handleWithSyncHandler requestHandler - // this is equivalent to - // connection handleWith { Flow[HttpRequest] map requestHandler } - }).run() - } - - "low-level-server-example" in compileOnlySpec { - import akka.actor.ActorSystem - import akka.http.scaladsl.Http - import akka.http.scaladsl.model.HttpMethods._ - import akka.http.scaladsl.model._ - import akka.stream.ActorMaterializer - import scala.io.StdIn - - object WebServer { - - def main(args: Array[String]) { - implicit val system = ActorSystem() - implicit val materializer = ActorMaterializer() - // needed for the future map/flatmap in the end - implicit val executionContext = system.dispatcher - - val requestHandler: HttpRequest => HttpResponse = { - case HttpRequest(GET, Uri.Path("/"), _, _, _) => - HttpResponse(entity = HttpEntity( - ContentTypes.`text/html(UTF-8)`, - "Hello world!")) - - case HttpRequest(GET, Uri.Path("/ping"), _, _, _) => - HttpResponse(entity = "PONG!") - - case HttpRequest(GET, Uri.Path("/crash"), _, _, _) => - sys.error("BOOM!") - - case r: HttpRequest => - r.discardEntityBytes() // important to drain incoming HTTP Entity stream - HttpResponse(404, entity = "Unknown resource!") - } - - val bindingFuture = Http().bindAndHandleSync(requestHandler, "localhost", 8080) - println(s"Server online at http://localhost:8080/\nPress RETURN to stop...") - StdIn.readLine() // let it run until user presses return - bindingFuture - .flatMap(_.unbind()) // trigger unbinding from the port - .onComplete(_ => system.terminate()) // and shutdown when done - - } - } - } - - // format: OFF - - "high-level-server-example" in compileOnlySpec { - import akka.actor.ActorSystem - import akka.http.scaladsl.Http - import akka.http.scaladsl.model.{ContentTypes, HttpEntity} - import akka.http.scaladsl.server.Directives._ - import akka.stream.ActorMaterializer - import scala.io.StdIn - - object WebServer { - def main(args: Array[String]) { - implicit val system = ActorSystem() - implicit val materializer = ActorMaterializer() - // needed for the future flatMap/onComplete in the end - implicit val executionContext = system.dispatcher - - val route = - get { - pathSingleSlash { - complete(HttpEntity(ContentTypes.`text/html(UTF-8)`,"Hello world!")) - } ~ - path("ping") { - complete("PONG!") - } ~ - path("crash") { - sys.error("BOOM!") - } - } - - // `route` will be implicitly converted to `Flow` using `RouteResult.route2HandlerFlow` - val bindingFuture = Http().bindAndHandle(route, "localhost", 8080) - println(s"Server online at http://localhost:8080/\nPress RETURN to stop...") - StdIn.readLine() // let it run until user presses return - bindingFuture - .flatMap(_.unbind()) // trigger unbinding from the port - .onComplete(_ => system.terminate()) // and shutdown when done - } - } - } - - "minimal-routing-example" in compileOnlySpec { - import akka.actor.ActorSystem - import akka.http.scaladsl.Http - import akka.http.scaladsl.model._ - import akka.http.scaladsl.server.Directives._ - import akka.stream.ActorMaterializer - import scala.io.StdIn - - object WebServer { - def main(args: Array[String]) { - - implicit val system = ActorSystem("my-system") - implicit val materializer = ActorMaterializer() - // needed for the future flatMap/onComplete in the end - implicit val executionContext = system.dispatcher - - val route = - path("hello") { - get { - complete(HttpEntity(ContentTypes.`text/html(UTF-8)`, "

Say hello to akka-http

")) - } - } - - val bindingFuture = Http().bindAndHandle(route, "localhost", 8080) - - println(s"Server online at http://localhost:8080/\nPress RETURN to stop...") - StdIn.readLine() // let it run until user presses return - bindingFuture - .flatMap(_.unbind()) // trigger unbinding from the port - .onComplete(_ => system.terminate()) // and shutdown when done - } - } - } - - "long-routing-example" in compileOnlySpec { - //#long-routing-example - import akka.actor.{ActorRef, ActorSystem} - import akka.http.scaladsl.coding.Deflate - import akka.http.scaladsl.marshalling.ToResponseMarshaller - import akka.http.scaladsl.model.StatusCodes.MovedPermanently - import akka.http.scaladsl.server.Directives._ - import akka.http.scaladsl.unmarshalling.FromRequestUnmarshaller - import akka.pattern.ask - import akka.stream.ActorMaterializer - import akka.util.Timeout - - // types used by the API routes - type Money = Double // only for demo purposes, don't try this at home! - type TransactionResult = String - case class User(name: String) - case class Order(email: String, amount: Money) - case class Update(order: Order) - case class OrderItem(i: Int, os: Option[String], s: String) - - // marshalling would usually be derived automatically using libraries - implicit val orderUM: FromRequestUnmarshaller[Order] = ??? - implicit val orderM: ToResponseMarshaller[Order] = ??? - implicit val orderSeqM: ToResponseMarshaller[Seq[Order]] = ??? - implicit val timeout: Timeout = ??? // for actor asks - implicit val ec: ExecutionContext = ??? - implicit val mat: ActorMaterializer = ??? - implicit val sys: ActorSystem = ??? - - // backend entry points - def myAuthenticator: Authenticator[User] = ??? - def retrieveOrdersFromDB: Seq[Order] = ??? - def myDbActor: ActorRef = ??? - def processOrderRequest(id: Int, complete: Order => Unit): Unit = ??? - - val route = { - path("orders") { - authenticateBasic(realm = "admin area", myAuthenticator) { user => - get { - encodeResponseWith(Deflate) { - complete { - // marshal custom object with in-scope marshaller - retrieveOrdersFromDB - } - } - } ~ - post { - // decompress gzipped or deflated requests if required - decodeRequest { - // unmarshal with in-scope unmarshaller - entity(as[Order]) { order => - complete { - // ... write order to DB - "Order received" - } - } - } - } - } - } ~ - // extract URI path element as Int - pathPrefix("order" / IntNumber) { orderId => - pathEnd { - (put | parameter('method ! "put")) { - // form extraction from multipart or www-url-encoded forms - formFields(('email, 'total.as[Money])).as(Order) { order => - complete { - // complete with serialized Future result - (myDbActor ? Update(order)).mapTo[TransactionResult] - } - } - } ~ - get { - // debugging helper - logRequest("GET-ORDER") { - // use in-scope marshaller to create completer function - completeWith(instanceOf[Order]) { completer => - // custom - processOrderRequest(orderId, completer) - } - } - } - } ~ - path("items") { - get { - // parameters to case class extraction - parameters(('size.as[Int], 'color ?, 'dangerous ? "no")) - .as(OrderItem) { orderItem => - // ... route using case class instance created from - // required and optional query parameters - complete("") // hide - } - } - } - } ~ - pathPrefix("documentation") { - // optionally compresses the response with Gzip or Deflate - // if the client accepts compressed responses - encodeResponse { - // serve up static content from a JAR resource - getFromResourceDirectory("docs") - } - } ~ - path("oldApi" / Remaining) { pathRest => - redirect("http://oldapi.example.com/" + pathRest, MovedPermanently) - } - } - } - - "stream random numbers" in compileOnlySpec { - //#stream-random-numbers - import akka.actor.ActorSystem - import akka.stream.scaladsl._ - import akka.util.ByteString - import akka.http.scaladsl.Http - import akka.http.scaladsl.model.{HttpEntity, ContentTypes} - import akka.http.scaladsl.server.Directives._ - import akka.stream.ActorMaterializer - import scala.util.Random - import scala.io.StdIn - - object WebServer { - - def main(args: Array[String]) { - - implicit val system = ActorSystem() - implicit val materializer = ActorMaterializer() - // needed for the future flatMap/onComplete in the end - implicit val executionContext = system.dispatcher - - // streams are re-usable so we can define it here - // and use it for every request - val numbers = Source.fromIterator(() => - Iterator.continually(Random.nextInt())) - - val route = - path("random") { - get { - complete( - HttpEntity( - ContentTypes.`text/plain(UTF-8)`, - // transform each number to a chunk of bytes - numbers.map(n => ByteString(s"$n\n")) - ) - ) - } - } - - val bindingFuture = Http().bindAndHandle(route, "localhost", 8080) - println(s"Server online at http://localhost:8080/\nPress RETURN to stop...") - StdIn.readLine() // let it run until user presses return - bindingFuture - .flatMap(_.unbind()) // trigger unbinding from the port - .onComplete(_ => system.terminate()) // and shutdown when done - } - } - //#stream-random-numbers - } - - - object Auction { - import akka.actor.Props - def props: Props = ??? - } - - "interact with an actor" in compileOnlySpec { - //#actor-interaction - import akka.actor.ActorSystem - import akka.http.scaladsl.Http - import akka.http.scaladsl.model.StatusCodes - import akka.http.scaladsl.server.Directives._ - import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._ - import akka.pattern.ask - import akka.stream.ActorMaterializer - import akka.util.Timeout - import spray.json.DefaultJsonProtocol._ - import scala.concurrent.duration._ - import scala.io.StdIn - - object WebServer { - - case class Bid(userId: String, bid: Int) - case object GetBids - case class Bids(bids: List[Bid]) - - // these are from spray-json - implicit val bidFormat = jsonFormat2(Bid) - implicit val bidsFormat = jsonFormat1(Bids) - - def main(args: Array[String]) { - implicit val system = ActorSystem() - implicit val materializer = ActorMaterializer() - // needed for the future flatMap/onComplete in the end - implicit val executionContext = system.dispatcher - - val auction = system.actorOf(Auction.props, "auction") - - val route = - path("auction") { - put { - parameter("bid".as[Int], "user") { (bid, user) => - // place a bid, fire-and-forget - auction ! Bid(user, bid) - complete((StatusCodes.Accepted, "bid placed")) - } - } - get { - implicit val timeout: Timeout = 5.seconds - - // query the actor for the current auction state - val bids: Future[Bids] = (auction ? GetBids).mapTo[Bids] - complete(bids) - } - } - - val bindingFuture = Http().bindAndHandle(route, "localhost", 8080) - println(s"Server online at http://localhost:8080/\nPress RETURN to stop...") - StdIn.readLine() // let it run until user presses return - bindingFuture - .flatMap(_.unbind()) // trigger unbinding from the port - .onComplete(_ => system.terminate()) // and shutdown when done - - } - } - //#actor-interaction - } - - "consume entity using entity directive" in compileOnlySpec { - //#consume-entity-directive - import akka.actor.ActorSystem - import akka.http.scaladsl.server.Directives._ - import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._ - import akka.stream.ActorMaterializer - import spray.json.DefaultJsonProtocol._ - - implicit val system = ActorSystem() - implicit val materializer = ActorMaterializer() - // needed for the future flatMap/onComplete in the end - implicit val executionContext = system.dispatcher - - final case class Bid(userId: String, bid: Int) - - // these are from spray-json - implicit val bidFormat = jsonFormat2(Bid) - - val route = - path("bid") { - put { - entity(as[Bid]) { bid => - // incoming entity is fully consumed and converted into a Bid - complete("The bid was: " + bid) - } - } - } - //#consume-entity-directive - } - - "consume entity using raw dataBytes to file" in compileOnlySpec { - //#consume-raw-dataBytes - import akka.actor.ActorSystem - import akka.stream.scaladsl.FileIO - import akka.http.scaladsl.server.Directives._ - import akka.stream.ActorMaterializer - import java.io.File - - implicit val system = ActorSystem() - implicit val materializer = ActorMaterializer() - // needed for the future flatMap/onComplete in the end - implicit val executionContext = system.dispatcher - - val route = - (put & path("lines")) { - withoutSizeLimit { - extractDataBytes { bytes => - val finishedWriting = bytes.runWith(FileIO.toPath(new File("/tmp/example.out").toPath)) - - // we only want to respond once the incoming data has been handled: - onComplete(finishedWriting) { ioResult => - complete("Finished writing data: " + ioResult) - } - } - } - } - //#consume-raw-dataBytes - } - - "drain entity using request#discardEntityBytes" in compileOnlySpec { - //#discard-discardEntityBytes - import akka.actor.ActorSystem - import akka.stream.scaladsl.FileIO - import akka.http.scaladsl.server.Directives._ - import akka.stream.ActorMaterializer - import akka.http.scaladsl.model.HttpRequest - - implicit val system = ActorSystem() - implicit val materializer = ActorMaterializer() - // needed for the future flatMap/onComplete in the end - implicit val executionContext = system.dispatcher - - val route = - (put & path("lines")) { - withoutSizeLimit { - extractRequest { r: HttpRequest => - val finishedWriting = r.discardEntityBytes().future - - // we only want to respond once the incoming data has been handled: - onComplete(finishedWriting) { done => - complete("Drained all data from connection... (" + done + ")") - } - } - } - } - //#discard-discardEntityBytes - } - - "discard entity manually" in compileOnlySpec { - //#discard-close-connections - import akka.actor.ActorSystem - import akka.stream.scaladsl.Sink - import akka.http.scaladsl.server.Directives._ - import akka.http.scaladsl.model.headers.Connection - import akka.stream.ActorMaterializer - - implicit val system = ActorSystem() - implicit val materializer = ActorMaterializer() - // needed for the future flatMap/onComplete in the end - implicit val executionContext = system.dispatcher - - val route = - (put & path("lines")) { - withoutSizeLimit { - extractDataBytes { data => - // Closing connections, method 1 (eager): - // we deem this request as illegal, and close the connection right away: - data.runWith(Sink.cancelled) // "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: - respondWithHeader(Connection("close")) - complete(StatusCodes.Forbidden -> "Not allowed!") - } - } - } - //#discard-close-connections - } - - -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/HttpsExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/HttpsExamplesSpec.scala deleted file mode 100644 index 64aeafa5e2..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/HttpsExamplesSpec.scala +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl - -import akka.actor.{ ActorLogging, ActorSystem } -import akka.http.scaladsl.Http -import akka.stream.ActorMaterializer -import akka.util.ByteString -import com.typesafe.sslconfig.akka.AkkaSSLConfig -import docs.CompileOnlySpec -import org.scalatest.{ Matchers, WordSpec } - -class HttpsExamplesSpec extends WordSpec with Matchers with CompileOnlySpec { - - "disable SNI for connection" in compileOnlySpec { - val unsafeHost = "example.com" - //#disable-sni-connection - implicit val system = ActorSystem() - implicit val mat = ActorMaterializer() - - // WARNING: disabling SNI is a very bad idea, please don't unless you have a very good reason to. - val badSslConfig = AkkaSSLConfig().mapSettings(s => s.withLoose(s.loose.withDisableSNI(true))) - val badCtx = Http().createClientHttpsContext(badSslConfig) - Http().outgoingConnectionHttps(unsafeHost, connectionContext = badCtx) - //#disable-sni-connection - } -} \ No newline at end of file diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/MarshalSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/MarshalSpec.scala deleted file mode 100644 index e88dab2925..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/MarshalSpec.scala +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl - -import akka.testkit.AkkaSpec - -class MarshalSpec extends AkkaSpec { - - "use marshal" in { - import scala.concurrent.Await - import scala.concurrent.duration._ - import akka.http.scaladsl.marshalling.Marshal - import akka.http.scaladsl.model._ - - import system.dispatcher // ExecutionContext - - val string = "Yeah" - val entityFuture = Marshal(string).to[MessageEntity] - val entity = Await.result(entityFuture, 1.second) // don't block in non-test code! - entity.contentType shouldEqual ContentTypes.`text/plain(UTF-8)` - - val errorMsg = "Easy, pal!" - val responseFuture = Marshal(420 -> errorMsg).to[HttpResponse] - val response = Await.result(responseFuture, 1.second) // don't block in non-test code! - response.status shouldEqual StatusCodes.EnhanceYourCalm - response.entity.contentType shouldEqual ContentTypes.`text/plain(UTF-8)` - - val request = HttpRequest(headers = List(headers.Accept(MediaTypes.`application/json`))) - val responseText = "Plaintext" - val respFuture = Marshal(responseText).toResponseFor(request) // with content negotiation! - a[Marshal.UnacceptableResponseContentTypeException] should be thrownBy { - Await.result(respFuture, 1.second) // client requested JSON, we only have text/plain! - } - } - -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/ModelSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/ModelSpec.scala deleted file mode 100644 index 63ecd5bd46..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/ModelSpec.scala +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl - -//#import-model -import akka.http.scaladsl.model._ - -//#import-model - -import akka.testkit.AkkaSpec -import akka.util.ByteString -import akka.http.scaladsl.model.headers.BasicHttpCredentials - -class ModelSpec extends AkkaSpec { - "construct request" in { - //#construct-request - import HttpMethods._ - - // construct a simple GET request to `homeUri` - val homeUri = Uri("/abc") - HttpRequest(GET, uri = homeUri) - - // construct simple GET request to "/index" (implicit string to Uri conversion) - HttpRequest(GET, uri = "/index") - - // construct simple POST request containing entity - val data = ByteString("abc") - HttpRequest(POST, uri = "/receive", entity = data) - - // customize every detail of HTTP request - import HttpProtocols._ - import MediaTypes._ - import HttpCharsets._ - val userData = ByteString("abc") - val authorization = headers.Authorization(BasicHttpCredentials("user", "pass")) - HttpRequest( - PUT, - uri = "/user", - entity = HttpEntity(`text/plain` withCharset `UTF-8`, userData), - headers = List(authorization), - protocol = `HTTP/1.0`) - //#construct-request - } - - "construct response" in { - //#construct-response - import StatusCodes._ - - // simple OK response without data created using the integer status code - HttpResponse(200) - - // 404 response created using the named StatusCode constant - HttpResponse(NotFound) - - // 404 response with a body explaining the error - HttpResponse(404, entity = "Unfortunately, the resource couldn't be found.") - - // A redirecting response containing an extra header - val locationHeader = headers.Location("http://example.com/other") - HttpResponse(Found, headers = List(locationHeader)) - - //#construct-response - } - - "deal with headers" in { - //#headers - import akka.http.scaladsl.model.headers._ - - // create a ``Location`` header - val loc = Location("http://example.com/other") - - // create an ``Authorization`` header with HTTP Basic authentication data - val auth = Authorization(BasicHttpCredentials("joe", "josepp")) - - // custom type - case class User(name: String, pass: String) - - // a method that extracts basic HTTP credentials from a request - def credentialsOfRequest(req: HttpRequest): Option[User] = - for { - Authorization(BasicHttpCredentials(user, pass)) <- req.header[Authorization] - } yield User(user, pass) - //#headers - - credentialsOfRequest(HttpRequest(headers = List(auth))) should be(Some(User("joe", "josepp"))) - credentialsOfRequest(HttpRequest()) should be(None) - credentialsOfRequest(HttpRequest(headers = List(Authorization(GenericHttpCredentials("Other", Map.empty[String, String]))))) should be(None) - } -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/SprayJsonCompactMarshalSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/SprayJsonCompactMarshalSpec.scala deleted file mode 100644 index c9d5285252..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/SprayJsonCompactMarshalSpec.scala +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2009-2016 Typesafe Inc. - */ -package docs.http.scaladsl - -import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport -import akka.http.scaladsl.server.Directives -import org.scalatest.{ Matchers, WordSpec } - -class SprayJsonCompactMarshalSpec extends WordSpec with Matchers { - - "spray-json example" in { - //#example - import spray.json._ - - // domain model - final case class CompactPrintedItem(name: String, id: Long) - - trait CompactJsonFormatSupport extends DefaultJsonProtocol with SprayJsonSupport { - implicit val printer = CompactPrinter - implicit val compactPrintedItemFormat = jsonFormat2(CompactPrintedItem) - } - - // use it wherever json (un)marshalling is needed - class MyJsonService extends Directives with CompactJsonFormatSupport { - - // format: OFF - val route = - get { - pathSingleSlash { - complete { - // should complete with spray.json.JsValue = {"name":"akka","id":42} - CompactPrintedItem("akka", 42) // will render as JSON - } - } - } - // format: ON - //# - } - } -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/SprayJsonExampleSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/SprayJsonExampleSpec.scala deleted file mode 100644 index 187290eed0..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/SprayJsonExampleSpec.scala +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl - -import org.scalatest.{ Matchers, WordSpec } - -class SprayJsonExampleSpec extends WordSpec with Matchers { - - def compileOnlySpec(body: => Unit) = () - - "spray-json example" in compileOnlySpec { - //#minimal-spray-json-example - import akka.http.scaladsl.server.Directives - import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport - import spray.json._ - - // domain model - final case class Item(name: String, id: Long) - final case class Order(items: List[Item]) - - // collect your json format instances into a support trait: - trait JsonSupport extends SprayJsonSupport with DefaultJsonProtocol { - implicit val itemFormat = jsonFormat2(Item) - implicit val orderFormat = jsonFormat1(Order) // contains List[Item] - } - - // use it wherever json (un)marshalling is needed - class MyJsonService extends Directives with JsonSupport { - - // format: OFF - val route = - get { - pathSingleSlash { - complete(Item("thing", 42)) // will render as JSON - } - } ~ - post { - entity(as[Order]) { order => // will unmarshal JSON to Order - val itemsCount = order.items.size - val itemNames = order.items.map(_.name).mkString(", ") - complete(s"Ordered $itemsCount items: $itemNames") - } - } - // format: ON - //# - } - } - - "second-spray-json-example" in compileOnlySpec { - //#second-spray-json-example - import akka.actor.ActorSystem - import akka.http.scaladsl.Http - import akka.stream.ActorMaterializer - import akka.Done - import akka.http.scaladsl.server.Route - import akka.http.scaladsl.server.Directives._ - import akka.http.scaladsl.model.StatusCodes - import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._ - import spray.json.DefaultJsonProtocol._ - - import scala.io.StdIn - - import scala.concurrent.Future - - object WebServer { - - // domain model - final case class Item(name: String, id: Long) - final case class Order(items: List[Item]) - - // formats for unmarshalling and marshalling - implicit val itemFormat = jsonFormat2(Item) - implicit val orderFormat = jsonFormat1(Order) - - // (fake) async database query api - def fetchItem(itemId: Long): Future[Option[Item]] = ??? - def saveOrder(order: Order): Future[Done] = ??? - - def main(args: Array[String]) { - - // needed to run the route - implicit val system = ActorSystem() - implicit val materializer = ActorMaterializer() - // needed for the future map/flatmap in the end - implicit val executionContext = system.dispatcher - - val route: Route = - get { - pathPrefix("item" / LongNumber) { id => - // there might be no item for a given id - val maybeItem: Future[Option[Item]] = fetchItem(id) - - onSuccess(maybeItem) { - case Some(item) => complete(item) - case None => complete(StatusCodes.NotFound) - } - } - } ~ - post { - path("create-order") { - entity(as[Order]) { order => - val saved: Future[Done] = saveOrder(order) - onComplete(saved) { done => - complete("order created") - } - } - } - } - - val bindingFuture = Http().bindAndHandle(route, "localhost", 8080) - println(s"Server online at http://localhost:8080/\nPress RETURN to stop...") - StdIn.readLine() // let it run until user presses return - bindingFuture - .flatMap(_.unbind()) // trigger unbinding from the port - .onComplete(_ ⇒ system.terminate()) // and shutdown when done - - } - } - //#second-spray-json-example - } -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/UnmarshalSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/UnmarshalSpec.scala deleted file mode 100644 index a6fb09d50c..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/UnmarshalSpec.scala +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl - -import akka.stream.{ Materializer, ActorMaterializer } -import akka.testkit.AkkaSpec - -class UnmarshalSpec extends AkkaSpec { - - "use unmarshal" in { - import akka.http.scaladsl.unmarshalling.Unmarshal - import system.dispatcher // ExecutionContext - implicit val materializer: Materializer = ActorMaterializer() - - import scala.concurrent.Await - import scala.concurrent.duration._ - - val intFuture = Unmarshal("42").to[Int] - val int = Await.result(intFuture, 1.second) // don't block in non-test code! - int shouldEqual 42 - - val boolFuture = Unmarshal("off").to[Boolean] - val bool = Await.result(boolFuture, 1.second) // don't block in non-test code! - bool shouldBe false - } - -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/WebSocketClientExampleSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/WebSocketClientExampleSpec.scala deleted file mode 100644 index fdc88ff200..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/WebSocketClientExampleSpec.scala +++ /dev/null @@ -1,248 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package docs.http.scaladsl - -import docs.CompileOnlySpec -import org.scalatest.{ Matchers, WordSpec } - -class WebSocketClientExampleSpec extends WordSpec with Matchers with CompileOnlySpec { - - "singleWebSocket-request-example" in compileOnlySpec { - //#single-WebSocket-request - import akka.actor.ActorSystem - import akka.{ Done, NotUsed } - import akka.http.scaladsl.Http - import akka.stream.ActorMaterializer - import akka.stream.scaladsl._ - import akka.http.scaladsl.model._ - import akka.http.scaladsl.model.ws._ - - import scala.concurrent.Future - - object SingleWebSocketRequest { - def main(args: Array[String]) = { - implicit val system = ActorSystem() - implicit val materializer = ActorMaterializer() - import system.dispatcher - - // print each incoming strict text message - val printSink: Sink[Message, Future[Done]] = - Sink.foreach { - case message: TextMessage.Strict => - println(message.text) - } - - val helloSource: Source[Message, NotUsed] = - Source.single(TextMessage("hello world!")) - - // the Future[Done] is the materialized value of Sink.foreach - // and it is completed when the stream completes - val flow: Flow[Message, Message, Future[Done]] = - Flow.fromSinkAndSourceMat(printSink, helloSource)(Keep.left) - - // upgradeResponse is a Future[WebSocketUpgradeResponse] that - // completes or fails when the connection succeeds or fails - // and closed is a Future[Done] representing the stream completion from above - val (upgradeResponse, closed) = - Http().singleWebSocketRequest(WebSocketRequest("ws://echo.websocket.org"), flow) - - val connected = upgradeResponse.map { 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 == StatusCodes.SwitchingProtocols) { - Done - } else { - throw new RuntimeException(s"Connection failed: ${upgrade.response.status}") - } - } - - // in a real application you would not side effect here - // and handle errors more carefully - connected.onComplete(println) - closed.foreach(_ => println("closed")) - } - } - //#single-WebSocket-request - } - - "half-closed-WebSocket-closing-example" in compileOnlySpec { - import akka.actor.ActorSystem - import akka.{ Done, NotUsed } - import akka.http.scaladsl.Http - import akka.stream.ActorMaterializer - import akka.stream.scaladsl._ - import akka.http.scaladsl.model.ws._ - - implicit val system = ActorSystem() - implicit val materializer = ActorMaterializer() - import system.dispatcher - - //#half-closed-WebSocket-closing-example - - // we may expect to be able to to just tail - // the server websocket output like this - val flow: Flow[Message, Message, NotUsed] = - Flow.fromSinkAndSource( - Sink.foreach(println), - Source.empty) - - Http().singleWebSocketRequest( - WebSocketRequest("ws://example.com:8080/some/path"), - flow) - - //#half-closed-WebSocket-closing-example - } - - "half-closed-WebSocket-working-example" in compileOnlySpec { - import akka.actor.ActorSystem - import akka.http.scaladsl.Http - import akka.stream.ActorMaterializer - import akka.stream.scaladsl._ - import akka.http.scaladsl.model.ws._ - - import scala.concurrent.Promise - - implicit val system = ActorSystem() - implicit val materializer = ActorMaterializer() - import system.dispatcher - - //#half-closed-WebSocket-working-example - - // using Source.maybe materializes into a promise - // which will allow us to complete the source later - val flow: Flow[Message, Message, Promise[Option[Message]]] = - Flow.fromSinkAndSourceMat( - Sink.foreach[Message](println), - Source.maybe[Message])(Keep.right) - - val (upgradeResponse, promise) = - Http().singleWebSocketRequest( - WebSocketRequest("ws://example.com:8080/some/path"), - flow) - - // at some later time we want to disconnect - promise.success(None) - //#half-closed-WebSocket-working-example - } - - "half-closed-WebSocket-finite-working-example" in compileOnlySpec { - import akka.actor.ActorSystem - import akka.{ Done, NotUsed } - import akka.http.scaladsl.Http - import akka.stream.ActorMaterializer - import akka.stream.scaladsl._ - import akka.http.scaladsl.model.ws._ - - import scala.concurrent.Promise - - implicit val system = ActorSystem() - implicit val materializer = ActorMaterializer() - import system.dispatcher - - //#half-closed-WebSocket-finite-working-example - - // using emit "one" and "two" and then keep the connection open - val flow: Flow[Message, Message, Promise[Option[Message]]] = - Flow.fromSinkAndSourceMat( - Sink.foreach[Message](println), - Source(List(TextMessage("one"), TextMessage("two"))) - .concatMat(Source.maybe[Message])(Keep.right))(Keep.right) - - val (upgradeResponse, promise) = - Http().singleWebSocketRequest( - WebSocketRequest("ws://example.com:8080/some/path"), - flow) - - // at some later time we want to disconnect - promise.success(None) - //#half-closed-WebSocket-finite-working-example - } - - "authorized-singleWebSocket-request-example" in compileOnlySpec { - import akka.actor.ActorSystem - import akka.NotUsed - import akka.http.scaladsl.Http - import akka.stream.ActorMaterializer - import akka.stream.scaladsl._ - import akka.http.scaladsl.model.headers.{ Authorization, BasicHttpCredentials } - import akka.http.scaladsl.model.ws._ - - implicit val system = ActorSystem() - implicit val materializer = ActorMaterializer() - import collection.immutable.Seq - - val flow: Flow[Message, Message, NotUsed] = ??? - - //#authorized-single-WebSocket-request - val (upgradeResponse, _) = - Http().singleWebSocketRequest( - WebSocketRequest( - "ws://example.com:8080/some/path", - extraHeaders = Seq(Authorization( - BasicHttpCredentials("johan", "correcthorsebatterystaple")))), - flow) - //#authorized-single-WebSocket-request - } - - "WebSocketClient-flow-example" in compileOnlySpec { - //#WebSocket-client-flow - import akka.actor.ActorSystem - import akka.Done - import akka.http.scaladsl.Http - import akka.stream.ActorMaterializer - import akka.stream.scaladsl._ - import akka.http.scaladsl.model._ - import akka.http.scaladsl.model.ws._ - - import scala.concurrent.Future - - object WebSocketClientFlow { - def main(args: Array[String]) = { - implicit val system = ActorSystem() - implicit val materializer = ActorMaterializer() - import system.dispatcher - - // Future[Done] is the materialized value of Sink.foreach, - // emitted when the stream completes - val incoming: Sink[Message, Future[Done]] = - Sink.foreach[Message] { - case message: TextMessage.Strict => - println(message.text) - } - - // send this as a message over the WebSocket - val outgoing = Source.single(TextMessage("hello world!")) - - // flow to use (note: not re-usable!) - val webSocketFlow = Http().webSocketClientFlow(WebSocketRequest("ws://echo.websocket.org")) - - // the materialized value is a tuple with - // upgradeResponse is a Future[WebSocketUpgradeResponse] that - // completes or fails when the connection succeeds or fails - // and closed is a Future[Done] with the stream completion from the incoming sink - val (upgradeResponse, closed) = - outgoing - .viaMat(webSocketFlow)(Keep.right) // keep the materialized Future[WebSocketUpgradeResponse] - .toMat(incoming)(Keep.both) // also keep the Future[Done] - .run() - - // 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 - val connected = upgradeResponse.flatMap { upgrade => - if (upgrade.response.status == StatusCodes.SwitchingProtocols) { - Future.successful(Done) - } else { - throw new RuntimeException(s"Connection failed: ${upgrade.response.status}") - } - } - - // in a real application you would not side effect here - connected.onComplete(println) - closed.foreach(_ => println("closed")) - } - } - //#WebSocket-client-flow - } - -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/BlockingInHttpExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/BlockingInHttpExamplesSpec.scala deleted file mode 100644 index 7c06c9ede2..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/BlockingInHttpExamplesSpec.scala +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package docs.http.scaladsl.server - -import akka.actor.ActorSystem -import akka.http.scaladsl.server.{ Directives, Route } -import docs.CompileOnlySpec -import org.scalatest.WordSpec - -import scala.concurrent.Future - -class BlockingInHttpExamplesSpec extends WordSpec with CompileOnlySpec - with Directives { - - compileOnlySpec { - val system: ActorSystem = ??? - - //#blocking-example-in-default-dispatcher - // BAD (due to blocking in Future, on default dispatcher) - implicit val defaultDispatcher = system.dispatcher - - val routes: Route = post { - complete { - Future { // uses defaultDispatcher - Thread.sleep(5000) // will block on default dispatcher, - System.currentTimeMillis().toString // Starving the routing infrastructure - } - } - } - //# - } - - compileOnlySpec { - val system: ActorSystem = ??? - - //#blocking-example-in-dedicated-dispatcher - // GOOD (the blocking is now isolated onto a dedicated dispatcher): - implicit val blockingDispatcher = system.dispatchers.lookup("my-blocking-dispatcher") - - val routes: Route = post { - complete { - Future { // uses the good "blocking dispatcher" that we configured, - // instead of the default dispatcher- the blocking is isolated. - Thread.sleep(5000) - System.currentTimeMillis().toString - } - } - } - //# - } - -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/CaseClassExtractionExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/CaseClassExtractionExamplesSpec.scala deleted file mode 100644 index 2ef5914ac8..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/CaseClassExtractionExamplesSpec.scala +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl.server - - - -/* -import org.scalatest.Inside -import akka.http.scaladsl.server._ - -class CaseClassExtractionExamplesSpec extends RoutingSpec with Inside { - // FIXME: investigate why it doesn't work without this import - import akka.http.scaladsl.server.directives.ParameterDirectives.ParamMagnet - - // format: OFF - - "example-1" in { - case class Color(red: Int, green: Int, blue: Int) - - val route = - path("color") { - parameters('red.as[Int], 'green.as[Int], 'blue.as[Int]) { (red, green, blue) => - val color = Color(red, green, blue) - // ... route working with the `color` instance - null // hide - } - } - Get("/color?red=1&green=2&blue=3") ~> route ~> check { responseAs[String] shouldEqual "Color(1,2,3)" } // hide - } - - "example-2" in { - case class Color(red: Int, green: Int, blue: Int) - - val route = - path("color") { - parameters('red.as[Int], 'green.as[Int], 'blue.as[Int]).as(Color) { color => - // ... route working with the `color` instance - null // hide - } - } - Get("/color?red=1&green=2&blue=3") ~> route ~> check { responseAs[String] shouldEqual "Color(1,2,3)" } // hide - } - - "example-3" in { - case class Color(name: String, red: Int, green: Int, blue: Int) - - val route = - (path("color" / Segment) & parameters('r.as[Int], 'g.as[Int], 'b.as[Int])) - .as(Color) { color => - // ... route working with the `color` instance - null // hide - } - Get("/color/abc?r=1&g=2&b=3") ~> route ~> check { responseAs[String] shouldEqual "Color(abc,1,2,3)" } // hide - } - - //# example-4 - case class Color(name: String, red: Int, green: Int, blue: Int) { - require(!name.isEmpty, "color name must not be empty") - require(0 <= red && red <= 255, "red color component must be between 0 and 255") - require(0 <= green && green <= 255, "green color component must be between 0 and 255") - require(0 <= blue && blue <= 255, "blue color component must be between 0 and 255") - } - //# - - "example 4 test" in { - val route = - (path("color" / Segment) & - parameters('r.as[Int], 'g.as[Int], 'b.as[Int])).as(Color) { color => - doSomethingWith(color) // route working with the Color instance - } - Get("/color/abc?r=1&g=2&b=3") ~> route ~> check { - responseAs[String] shouldEqual "Color(abc,1,2,3)" - } - Get("/color/abc?r=1&g=2&b=345") ~> route ~> check { - inside(rejection) { - case ValidationRejection("requirement failed: blue color component must be between 0 and 255", _) => - } - } - } - - def doSomethingWith(x: Any) = complete(x.toString) -}*/ diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/DirectiveExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/DirectiveExamplesSpec.scala deleted file mode 100644 index 178d24886c..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/DirectiveExamplesSpec.scala +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl.server - -import akka.http.scaladsl.server._ -import akka.http.scaladsl.testkit.ScalatestRouteTest - -class DirectiveExamplesSpec extends RoutingSpec { - - // format: OFF - - "example-1" in { - val route: Route = - path("order" / IntNumber) { id => - get { - complete { - "Received GET request for order " + id - } - } ~ - put { - complete { - "Received PUT request for order " + id - } - } - } - verify(route) // hide - } - - "example-2" in { - def innerRoute(id: Int): Route = - get { - complete { - "Received GET request for order " + id - } - } ~ - put { - complete { - "Received PUT request for order " + id - } - } - - val route: Route = path("order" / IntNumber) { id => innerRoute(id) } - verify(route) // hide - } - - "example-3" in { - val route = - path("order" / IntNumber) { id => - (get | put) { ctx => - ctx.complete(s"Received ${ctx.request.method.name} request for order $id") - } - } - verify(route) // hide - } - - "example-4" in { - val route = - path("order" / IntNumber) { id => - (get | put) { - extractMethod { m => - complete(s"Received ${m.name} request for order $id") - } - } - } - verify(route) // hide - } - - "example-5" in { - val getOrPut = get | put - val route = - path("order" / IntNumber) { id => - getOrPut { - extractMethod { m => - complete(s"Received ${m.name} request for order $id") - } - } - } - verify(route) // hide - } - - "example-6" in { - val getOrPut = get | put - val route = - (path("order" / IntNumber) & getOrPut & extractMethod) { (id, m) => - complete(s"Received ${m.name} request for order $id") - } - verify(route) // hide - } - - "example-7" in { - val orderGetOrPutWithMethod = - path("order" / IntNumber) & (get | put) & extractMethod - val route = - orderGetOrPutWithMethod { (id, m) => - complete(s"Received ${m.name} request for order $id") - } - verify(route) // hide - } - - def verify(route: Route) = { - Get("/order/42") ~> route ~> check { responseAs[String] shouldEqual "Received GET request for order 42" } - Put("/order/42") ~> route ~> check { responseAs[String] shouldEqual "Received PUT request for order 42" } - Get("/") ~> route ~> check { handled shouldEqual false } - } -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/ExceptionHandlerExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/ExceptionHandlerExamplesSpec.scala deleted file mode 100644 index 22c6a6c41a..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/ExceptionHandlerExamplesSpec.scala +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl.server - -// format: OFF - -object MyExplicitExceptionHandler { - - //#explicit-handler-example - import akka.actor.ActorSystem - import akka.stream.ActorMaterializer - import akka.http.scaladsl.Http - import akka.http.scaladsl.model._ - import akka.http.scaladsl.server._ - import StatusCodes._ - import Directives._ - - val myExceptionHandler = ExceptionHandler { - case _: ArithmeticException => - extractUri { uri => - println(s"Request to $uri could not be handled normally") - complete(HttpResponse(InternalServerError, entity = "Bad numbers, bad result!!!")) - } - } - - object MyApp extends App { - implicit val system = ActorSystem() - implicit val materializer = ActorMaterializer() - - val route: Route = - handleExceptions(myExceptionHandler) { - // ... some route structure - null // hide - } - - Http().bindAndHandle(route, "localhost", 8080) - } - //# -} - -object MyImplicitExceptionHandler { - - //#implicit-handler-example - import akka.actor.ActorSystem - import akka.stream.ActorMaterializer - import akka.http.scaladsl.Http - import akka.http.scaladsl.model._ - import akka.http.scaladsl.server._ - import StatusCodes._ - import Directives._ - - implicit def myExceptionHandler: ExceptionHandler = - ExceptionHandler { - case _: ArithmeticException => - extractUri { uri => - println(s"Request to $uri could not be handled normally") - complete(HttpResponse(InternalServerError, entity = "Bad numbers, bad result!!!")) - } - } - - object MyApp extends App { - implicit val system = ActorSystem() - implicit val materializer = ActorMaterializer() - - val route: Route = - // ... some route structure - null // hide - - Http().bindAndHandle(route, "localhost", 8080) - } - //# -} - -class ExceptionHandlerExamplesSpec extends RoutingSpec { - - "test explicit example" in { - // tests: - Get() ~> handleExceptions(MyExplicitExceptionHandler.myExceptionHandler) { - _.complete((1 / 0).toString) - } ~> check { - responseAs[String] === "Bad numbers, bad result!!!" - } - } - - "test implicit example" in { - import akka.http.scaladsl.server._ - import MyImplicitExceptionHandler.myExceptionHandler - // tests: - Get() ~> Route.seal(ctx => ctx.complete((1 / 0).toString)) ~> check { - responseAs[String] === "Bad numbers, bad result!!!" - } - } -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/FileUploadExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/FileUploadExamplesSpec.scala deleted file mode 100644 index 5bdf363a14..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/FileUploadExamplesSpec.scala +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package docs.http.scaladsl.server - -import java.io.File - -import akka.Done -import akka.actor.ActorRef -import akka.http.scaladsl.model.Multipart.FormData.BodyPart -import akka.stream.scaladsl.Framing -import akka.stream.scaladsl._ -import akka.http.scaladsl.model.Multipart -import akka.util.ByteString - -import scala.concurrent.duration._ -import scala.concurrent.Future - -class FileUploadExamplesSpec extends RoutingSpec { - - case class Video(file: File, title: String, author: String) - object db { - def create(video: Video): Future[Unit] = Future.successful(Unit) - } - - "simple-upload" in { - val uploadVideo = - path("video") { - entity(as[Multipart.FormData]) { formData => - - // collect all parts of the multipart as it arrives into a map - val allPartsF: Future[Map[String, Any]] = formData.parts.mapAsync[(String, Any)](1) { - - case b: BodyPart if b.name == "file" => - // stream into a file as the chunks of it arrives and return a future - // file to where it got stored - val file = File.createTempFile("upload", "tmp") - b.entity.dataBytes.runWith(FileIO.toPath(file.toPath)).map(_ => - (b.name -> file)) - - case b: BodyPart => - // collect form field values - b.toStrict(2.seconds).map(strict => - (b.name -> strict.entity.data.utf8String)) - - }.runFold(Map.empty[String, Any])((map, tuple) => map + tuple) - - val done = allPartsF.map { allParts => - // You would have some better validation/unmarshalling here - db.create(Video( - file = allParts("file").asInstanceOf[File], - title = allParts("title").asInstanceOf[String], - author = allParts("author").asInstanceOf[String])) - } - - // when processing have finished create a response for the user - onSuccess(allPartsF) { allParts => - complete { - "ok!" - } - } - } - } - } - - object MetadataActor { - case class Entry(id: Long, values: Seq[String]) - } - val metadataActor: ActorRef = system.deadLetters - - "stream-csv-upload" in { - val splitLines = Framing.delimiter(ByteString("\n"), 256) - - val csvUploads = - path("metadata" / LongNumber) { id => - entity(as[Multipart.FormData]) { formData => - val done: Future[Done] = formData.parts.mapAsync(1) { - case b: BodyPart if b.filename.exists(_.endsWith(".csv")) => - b.entity.dataBytes - .via(splitLines) - .map(_.utf8String.split(",").toVector) - .runForeach(csv => - metadataActor ! MetadataActor.Entry(id, csv)) - case _ => Future.successful(Done) - }.runWith(Sink.ignore) - - // when processing have finished create a response for the user - onSuccess(done) { _ => - complete { - "ok!" - } - } - } - } - } - -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/FullTestKitExampleSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/FullTestKitExampleSpec.scala deleted file mode 100644 index 40400a453c..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/FullTestKitExampleSpec.scala +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl.server - -// format: OFF - -//# source-quote -import org.scalatest.{ Matchers, WordSpec } -import akka.http.scaladsl.model.StatusCodes -import akka.http.scaladsl.testkit.ScalatestRouteTest -import akka.http.scaladsl.server._ -import Directives._ - -class FullTestKitExampleSpec extends WordSpec with Matchers with ScalatestRouteTest { - - val smallRoute = - get { - pathSingleSlash { - complete { - "Captain on the bridge!" - } - } ~ - path("ping") { - complete("PONG!") - } - } - - "The service" should { - - "return a greeting for GET requests to the root path" in { - // tests: - Get() ~> smallRoute ~> check { - responseAs[String] shouldEqual "Captain on the bridge!" - } - } - - "return a 'PONG!' response for GET requests to /ping" in { - // tests: - Get("/ping") ~> smallRoute ~> check { - responseAs[String] shouldEqual "PONG!" - } - } - - "leave GET requests to other paths unhandled" in { - // tests: - Get("/kermit") ~> smallRoute ~> check { - handled shouldBe false - } - } - - "return a MethodNotAllowed error for PUT requests to the root path" in { - // tests: - Put() ~> Route.seal(smallRoute) ~> check { - status === StatusCodes.MethodNotAllowed - responseAs[String] shouldEqual "HTTP method not allowed, supported methods: GET" - } - } - } -} -//# \ No newline at end of file diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/HttpsServerExampleSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/HttpsServerExampleSpec.scala deleted file mode 100644 index cc15315dda..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/HttpsServerExampleSpec.scala +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl.server - -//#imports -import java.io.InputStream -import java.security.{ SecureRandom, KeyStore } -import javax.net.ssl.{ SSLContext, TrustManagerFactory, KeyManagerFactory } - -import akka.actor.ActorSystem -import akka.http.scaladsl.server.{ RouteResult, Route, Directives } -import akka.http.scaladsl.{ ConnectionContext, HttpsConnectionContext, Http } -import akka.stream.ActorMaterializer -import com.typesafe.sslconfig.akka.AkkaSSLConfig -//# - -import docs.CompileOnlySpec -import org.scalatest.{ Matchers, WordSpec } - -abstract class HttpsServerExampleSpec extends WordSpec with Matchers - with Directives with CompileOnlySpec { - - class HowToObtainSSLConfig { - //#akka-ssl-config - implicit val system = ActorSystem() - val sslConfig = AkkaSSLConfig() - //# - } - - "low level api" in compileOnlySpec { - //#low-level-default - implicit val system = ActorSystem() - implicit val mat = ActorMaterializer() - implicit val dispatcher = system.dispatcher - - // Manual HTTPS configuration - - val password: Array[Char] = ??? // do not store passwords in code, read them from somewhere safe! - - val ks: KeyStore = KeyStore.getInstance("PKCS12") - val keystore: InputStream = getClass.getClassLoader.getResourceAsStream("server.p12") - - require(keystore != null, "Keystore required!") - ks.load(keystore, password) - - val keyManagerFactory: KeyManagerFactory = KeyManagerFactory.getInstance("SunX509") - keyManagerFactory.init(ks, password) - - val tmf: TrustManagerFactory = TrustManagerFactory.getInstance("SunX509") - tmf.init(ks) - - val sslContext: SSLContext = SSLContext.getInstance("TLS") - sslContext.init(keyManagerFactory.getKeyManagers, tmf.getTrustManagers, new SecureRandom) - val https: HttpsConnectionContext = ConnectionContext.https(sslContext) - //# - - //#both-https-and-http - // you can run both HTTP and HTTPS in the same application as follows: - val commonRoutes: Route = get { complete("Hello world!") } - Http().bindAndHandle(commonRoutes, "127.0.0.1", 443, connectionContext = https) - Http().bindAndHandle(commonRoutes, "127.0.0.1", 80) - //# - - //#bind-low-level-context - Http().bind("127.0.0.1", connectionContext = https) - - // or using the high level routing DSL: - val routes: Route = get { complete("Hello world!") } - Http().bindAndHandle(routes, "127.0.0.1", 8080, connectionContext = https) - //# - - //#set-low-level-context-default - // sets default context to HTTPS – all Http() bound servers for this ActorSystem will use HTTPS from now on - Http().setDefaultServerHttpContext(https) - Http().bindAndHandle(routes, "127.0.0.1", 9090, connectionContext = https) - //# - - system.terminate() - } - -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/RejectionHandlerExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/RejectionHandlerExamplesSpec.scala deleted file mode 100644 index 587106986f..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/RejectionHandlerExamplesSpec.scala +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl.server - -// format: OFF - -object MyRejectionHandler { - - //#custom-handler-example - import akka.actor.ActorSystem - import akka.stream.ActorMaterializer - import akka.http.scaladsl.Http - import akka.http.scaladsl.model._ - import akka.http.scaladsl.server._ - import StatusCodes._ - import Directives._ - - implicit def myRejectionHandler = - RejectionHandler.newBuilder() - .handle { case MissingCookieRejection(cookieName) => - complete(HttpResponse(BadRequest, entity = "No cookies, no service!!!")) - } - .handle { case AuthorizationFailedRejection => - complete((Forbidden, "You're out of your depth!")) - } - .handle { case ValidationRejection(msg, _) => - complete((InternalServerError, "That wasn't valid! " + msg)) - } - .handleAll[MethodRejection] { methodRejections => - val names = methodRejections.map(_.supported.name) - complete((MethodNotAllowed, s"Can't do that! Supported: ${names mkString " or "}!")) - } - .handleNotFound { complete((NotFound, "Not here!")) } - .result() - - object MyApp extends App { - implicit val system = ActorSystem() - implicit val materializer = ActorMaterializer() - - val route: Route = - // ... some route structure - null // hide - - Http().bindAndHandle(route, "localhost", 8080) - } - //# -} - -class RejectionHandlerExamplesSpec extends RoutingSpec { - import MyRejectionHandler._ - - "example-1" in { - import akka.http.scaladsl.coding.Gzip - - val route = - path("order") { - get { - complete("Received GET") - } ~ - post { - decodeRequestWith(Gzip) { - complete("Received compressed POST") - } - } - } - } - - "test custom handler example" in { - import akka.http.scaladsl.server._ - val route = Route.seal(reject(MissingCookieRejection("abc"))) - - // tests: - Get() ~> route ~> check { - responseAs[String] === "No cookies, no service!!!" - } - } -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/RoutingSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/RoutingSpec.scala deleted file mode 100644 index a86ba5bac1..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/RoutingSpec.scala +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl.server - -import akka.http.scaladsl.server.Directives -import akka.http.scaladsl.testkit.ScalatestRouteTest -import docs.CompileOnlySpec -import org.scalatest.{ Matchers, WordSpec } - -abstract class RoutingSpec extends WordSpec with Matchers - with Directives with ScalatestRouteTest - with CompileOnlySpec diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/WebSocketExampleSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/WebSocketExampleSpec.scala deleted file mode 100644 index 6317359124..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/WebSocketExampleSpec.scala +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl.server - -import akka.http.scaladsl.model.ws.BinaryMessage -import akka.stream.scaladsl.Sink -import docs.CompileOnlySpec -import org.scalatest.{ Matchers, WordSpec } - -class WebSocketExampleSpec extends WordSpec with Matchers with CompileOnlySpec { - "core-example" in compileOnlySpec { - //#websocket-example-using-core - import akka.actor.ActorSystem - import akka.stream.ActorMaterializer - import akka.stream.scaladsl.{ Source, Flow } - import akka.http.scaladsl.Http - import akka.http.scaladsl.model.ws.UpgradeToWebSocket - import akka.http.scaladsl.model.ws.{ TextMessage, Message } - import akka.http.scaladsl.model.{ HttpResponse, Uri, HttpRequest } - import akka.http.scaladsl.model.HttpMethods._ - - implicit val system = ActorSystem() - implicit val materializer = ActorMaterializer() - - //#websocket-handler - // The Greeter WebSocket Service expects a "name" per message and - // returns a greeting message for that name - val greeterWebSocketService = - Flow[Message] - .mapConcat { - // we match but don't actually consume the text message here, - // rather we simply stream it back as the tail of the response - // this means we might start sending the response even before the - // end of the incoming message has been received - case tm: TextMessage => TextMessage(Source.single("Hello ") ++ tm.textStream) :: Nil - case bm: BinaryMessage => - // ignore binary messages but drain content to avoid the stream being clogged - bm.dataStream.runWith(Sink.ignore) - Nil - } - //#websocket-handler - - //#websocket-request-handling - val requestHandler: HttpRequest => HttpResponse = { - case req @ HttpRequest(GET, Uri.Path("/greeter"), _, _, _) => - req.header[UpgradeToWebSocket] match { - case Some(upgrade) => upgrade.handleMessages(greeterWebSocketService) - case None => HttpResponse(400, entity = "Not a valid websocket request!") - } - case r: HttpRequest => - r.discardEntityBytes() // important to drain incoming HTTP Entity stream - HttpResponse(404, entity = "Unknown resource!") - } - //#websocket-request-handling - - val bindingFuture = - Http().bindAndHandleSync(requestHandler, interface = "localhost", port = 8080) - - println(s"Server online at http://localhost:8080/\nPress RETURN to stop...") - Console.readLine() - - import system.dispatcher // for the future transformations - bindingFuture - .flatMap(_.unbind()) // trigger unbinding from the port - .onComplete(_ => system.terminate()) // and shutdown when done - } - "routing-example" in compileOnlySpec { - import akka.actor.ActorSystem - import akka.stream.ActorMaterializer - import akka.stream.scaladsl.{ Source, Flow } - import akka.http.scaladsl.Http - import akka.http.scaladsl.model.ws.{ TextMessage, Message } - import akka.http.scaladsl.server.Directives - - implicit val system = ActorSystem() - implicit val materializer = ActorMaterializer() - - import Directives._ - - // The Greeter WebSocket Service expects a "name" per message and - // returns a greeting message for that name - val greeterWebSocketService = - Flow[Message] - .collect { - case tm: TextMessage => TextMessage(Source.single("Hello ") ++ tm.textStream) - // ignore binary messages - // TODO #20096 in case a Streamed message comes in, we should runWith(Sink.ignore) its data - } - - //#websocket-routing - val route = - path("greeter") { - get { - handleWebSocketMessages(greeterWebSocketService) - } - } - //#websocket-routing - - val bindingFuture = Http().bindAndHandle(route, "localhost", 8080) - - println(s"Server online at http://localhost:8080/\nPress RETURN to stop...") - Console.readLine() - - import system.dispatcher // for the future transformations - bindingFuture - .flatMap(_.unbind()) // trigger unbinding from the port - .onComplete(_ => system.terminate()) // and shutdown when done - } -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala deleted file mode 100644 index c3ec9b4576..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala +++ /dev/null @@ -1,878 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl.server.directives - -import java.nio.file.Paths - -import akka.actor.ActorSystem -import akka.event.Logging -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers.{ Server, RawHeader } -import akka.http.scaladsl.server.RouteResult.{ Complete, Rejected } -import akka.http.scaladsl.server._ -import akka.http.scaladsl.settings.RoutingSettings -import akka.stream.ActorMaterializer -import akka.stream.scaladsl.{ FileIO, Sink, Source } -import akka.util.ByteString -import docs.http.scaladsl.server.RoutingSpec - -import scala.concurrent.Future -import scala.util.control.NonFatal - -class BasicDirectivesExamplesSpec extends RoutingSpec { - "0extract" in { - //#0extract - val uriLength = extract(_.request.uri.toString.length) - val route = - uriLength { len => - complete(s"The length of the request URI is $len") - } - - // tests: - Get("/abcdef") ~> route ~> check { - responseAs[String] shouldEqual "The length of the request URI is 25" - } - //# - } - "0extractLog" in { - //#0extractLog - val route = - extractLog { log => - log.debug("I'm logging things in much detail..!") - complete("It's amazing!") - } - - // tests: - Get("/abcdef") ~> route ~> check { - responseAs[String] shouldEqual "It's amazing!" - } - //# - } - "withMaterializer-0" in { - //#withMaterializer-0 - val special = ActorMaterializer(namePrefix = Some("special")) - - def sample() = - path("sample") { - extractMaterializer { mat => - complete { - // explicitly use the materializer: - Source.single(s"Materialized by ${mat.##}!") - .runWith(Sink.head)(mat) - } - } - } - - val route = - pathPrefix("special") { - withMaterializer(special) { - sample() // `special` materializer will be used - } - } ~ sample() // default materializer will be used - - // tests: - Get("/sample") ~> route ~> check { - responseAs[String] shouldEqual s"Materialized by ${materializer.##}!" - } - Get("/special/sample") ~> route ~> check { - responseAs[String] shouldEqual s"Materialized by ${special.##}!" - } - //# - } - "extractMaterializer-0" in { - //#extractMaterializer-0 - val route = - path("sample") { - extractMaterializer { materializer => - complete { - // explicitly use the `materializer`: - Source.single(s"Materialized by ${materializer.##}!") - .runWith(Sink.head)(materializer) - } - } - } // default materializer will be used - - // tests: - Get("/sample") ~> route ~> check { - responseAs[String] shouldEqual s"Materialized by ${materializer.##}!" - } - //# - } - "withExecutionContext-0" in compileOnlySpec { - //#withExecutionContext-0 - val special = system.dispatchers.lookup("special") - - def sample() = - path("sample") { - extractExecutionContext { implicit executor => - complete { - Future(s"Run on ${executor.##}!") // uses the `executor` ExecutionContext - } - } - } - - val route = - pathPrefix("special") { - withExecutionContext(special) { - sample() // `special` execution context will be used - } - } ~ sample() // default execution context will be used - - // tests: - Get("/sample") ~> route ~> check { - responseAs[String] shouldEqual s"Run on ${system.dispatcher.##}!" - } - Get("/special/sample") ~> route ~> check { - responseAs[String] shouldEqual s"Run on ${special.##}!" - } - //# - } - "extractExecutionContext-0" in compileOnlySpec { - //#extractExecutionContext-0 - def sample() = - path("sample") { - extractExecutionContext { implicit executor => - complete { - Future(s"Run on ${executor.##}!") // uses the `executor` ExecutionContext - } - } - } - - val route = - pathPrefix("special") { - sample() // default execution context will be used - } - - // tests: - Get("/sample") ~> route ~> check { - responseAs[String] shouldEqual s"Run on ${system.dispatcher.##}!" - } - //# - } - "0withLog" in { - //#0withLog - val special = Logging(system, "SpecialRoutes") - - def sample() = - path("sample") { - extractLog { implicit log => - complete { - val msg = s"Logging using $log!" - log.debug(msg) - msg - } - } - } - - val route = - pathPrefix("special") { - withLog(special) { - sample() // `special` logging adapter will be used - } - } ~ sample() // default logging adapter will be used - - // tests: - Get("/sample") ~> route ~> check { - responseAs[String] shouldEqual s"Logging using ${system.log}!" - } - Get("/special/sample") ~> route ~> check { - responseAs[String] shouldEqual s"Logging using $special!" - } - //# - } - "withSettings-0" in compileOnlySpec { - //#withSettings-0 - val special = RoutingSettings(system).withFileIODispatcher("special-io-dispatcher") - - def sample() = - path("sample") { - complete { - // internally uses the configured fileIODispatcher: - val source = FileIO.fromPath(Paths.get("example.json")) - HttpResponse(entity = HttpEntity(ContentTypes.`application/json`, source)) - } - } - - val route = - get { - pathPrefix("special") { - withSettings(special) { - sample() // `special` file-io-dispatcher will be used to read the file - } - } ~ sample() // default file-io-dispatcher will be used to read the file - } - - // tests: - Post("/special/sample") ~> route ~> check { - responseAs[String] shouldEqual s"{}" - } - Get("/sample") ~> route ~> check { - responseAs[String] shouldEqual "{}" - } - //# - } - "textract" in { - //#textract - val pathAndQuery = textract { ctx => - val uri = ctx.request.uri - (uri.path, uri.query()) - } - val route = - pathAndQuery { (p, query) => - complete(s"The path is $p and the query is $query") - } - - // tests: - Get("/abcdef?ghi=12") ~> route ~> check { - responseAs[String] shouldEqual "The path is /abcdef and the query is ghi=12" - } - //# - } - "tprovide" in { - //#tprovide - def provideStringAndLength(value: String) = tprovide((value, value.length)) - val route = - provideStringAndLength("test") { (value, len) => - complete(s"Value is $value and its length is $len") - } - - // tests: - Get("/") ~> route ~> check { - responseAs[String] shouldEqual "Value is test and its length is 4" - } - //# - } - "0mapResponse" in { - //#0mapResponse - def overwriteResultStatus(response: HttpResponse): HttpResponse = - response.copy(status = StatusCodes.BadGateway) - val route = mapResponse(overwriteResultStatus)(complete("abc")) - - // tests: - Get("/abcdef?ghi=12") ~> route ~> check { - status shouldEqual StatusCodes.BadGateway - } - //# - } - "1mapResponse-advanced-json" in { - //#1mapResponse-advanced - trait ApiRoutes { - protected def system: ActorSystem - - private val log = Logging(system, "ApiRoutes") - - private val NullJsonEntity = HttpEntity(ContentTypes.`application/json`, "{}") - - private def nonSuccessToEmptyJsonEntity(response: HttpResponse): HttpResponse = - response.status match { - case code if code.isSuccess => response - case code => - log.warning("Dropping response entity since response status code was: {}", code) - response.copy(entity = NullJsonEntity) - } - - /** Wrapper for all of our JSON API routes */ - def apiRoute(innerRoutes: => Route): Route = - mapResponse(nonSuccessToEmptyJsonEntity)(innerRoutes) - } - //# - - import StatusCodes._ - val __system = system - val routes = new ApiRoutes { - override protected def system = __system - } - import routes.apiRoute - - //#1mapResponse-advanced - val route: Route = - apiRoute { - get { - complete(InternalServerError) - } - } - - // tests: - Get("/") ~> route ~> check { - responseAs[String] shouldEqual "{}" - } - //# - } - "mapRouteResult" in { - //#mapRouteResult - // this directive is a joke, don't do that :-) - val makeEverythingOk = mapRouteResult { - case Complete(response) => - // "Everything is OK!" - Complete(response.copy(status = 200)) - case r => r - } - - val route = - makeEverythingOk { - // will actually render as 200 OK (!) - complete(StatusCodes.Accepted) - } - - // tests: - Get("/") ~> route ~> check { - status shouldEqual StatusCodes.OK - } - //# - } - "mapRouteResultFuture" in { - //#mapRouteResultFuture - val tryRecoverAddServer = mapRouteResultFuture { fr => - fr recover { - case ex: IllegalArgumentException => - Complete(HttpResponse(StatusCodes.InternalServerError)) - } map { - case Complete(res) => Complete(res.addHeader(Server("MyServer 1.0"))) - case rest => rest - } - } - - val route = - tryRecoverAddServer { - complete("Hello world!") - } - - // tests: - Get("/") ~> route ~> check { - status shouldEqual StatusCodes.OK - header[Server] shouldEqual Some(Server("MyServer 1.0")) - } - //# - } - "mapResponseEntity" in { - //#mapResponseEntity - def prefixEntity(entity: ResponseEntity): ResponseEntity = entity match { - case HttpEntity.Strict(contentType, data) => - HttpEntity.Strict(contentType, ByteString("test") ++ data) - case _ => throw new IllegalStateException("Unexpected entity type") - } - - val prefixWithTest: Directive0 = mapResponseEntity(prefixEntity) - val route = prefixWithTest(complete("abc")) - - // tests: - Get("/") ~> route ~> check { - responseAs[String] shouldEqual "testabc" - } - //# - } - "mapResponseHeaders" in { - //#mapResponseHeaders - // adds all request headers to the response - val echoRequestHeaders = extract(_.request.headers).flatMap(respondWithHeaders) - - val removeIdHeader = mapResponseHeaders(_.filterNot(_.lowercaseName == "id")) - val route = - removeIdHeader { - echoRequestHeaders { - complete("test") - } - } - - // tests: - Get("/") ~> RawHeader("id", "12345") ~> RawHeader("id2", "67890") ~> route ~> check { - header("id") shouldEqual None - header("id2").get.value shouldEqual "67890" - } - //# - } - "mapInnerRoute" in { - //#mapInnerRoute - val completeWithInnerException = - mapInnerRoute { route => ctx => - try { - route(ctx) - } catch { - case NonFatal(e) => ctx.complete(s"Got ${e.getClass.getSimpleName} '${e.getMessage}'") - } - } - - val route = - completeWithInnerException { - complete(throw new IllegalArgumentException("BLIP! BLOP! Everything broke")) - } - - // tests: - Get("/") ~> route ~> check { - responseAs[String] shouldEqual "Got IllegalArgumentException 'BLIP! BLOP! Everything broke'" - } - //# - } - "mapRejections" in { - //#mapRejections - // ignore any rejections and replace them by AuthorizationFailedRejection - val replaceByAuthorizationFailed = mapRejections(_ => List(AuthorizationFailedRejection)) - val route = - replaceByAuthorizationFailed { - path("abc")(complete("abc")) - } - - // tests: - Get("/") ~> route ~> check { - rejection shouldEqual AuthorizationFailedRejection - } - - Get("/abc") ~> route ~> check { - status shouldEqual StatusCodes.OK - } - //# - } - "recoverRejections" in { - //#recoverRejections - val authRejectionsToNothingToSeeHere = recoverRejections { rejections => - if (rejections.exists(_.isInstanceOf[AuthenticationFailedRejection])) - Complete(HttpResponse(entity = "Nothing to see here, move along.")) - else if (rejections == Nil) // see "Empty Rejections" for more details - Complete(HttpResponse(StatusCodes.NotFound, entity = "Literally nothing to see here.")) - else - Rejected(rejections) - } - val neverAuth: Authenticator[String] = creds => None - val alwaysAuth: Authenticator[String] = creds => Some("id") - - val route = - authRejectionsToNothingToSeeHere { - pathPrefix("auth") { - path("never") { - authenticateBasic("my-realm", neverAuth) { user => - complete("Welcome to the bat-cave!") - } - } ~ - path("always") { - authenticateBasic("my-realm", alwaysAuth) { user => - complete("Welcome to the secret place!") - } - } - } - } - - // tests: - Get("/auth/never") ~> route ~> check { - status shouldEqual StatusCodes.OK - responseAs[String] shouldEqual "Nothing to see here, move along." - } - Get("/auth/always") ~> route ~> check { - status shouldEqual StatusCodes.OK - responseAs[String] shouldEqual "Welcome to the secret place!" - } - Get("/auth/does_not_exist") ~> route ~> check { - status shouldEqual StatusCodes.NotFound - responseAs[String] shouldEqual "Literally nothing to see here." - } - //# - } - "recoverRejectionsWith" in { - //#recoverRejectionsWith - val authRejectionsToNothingToSeeHere = recoverRejectionsWith { rejections => - Future { - // imagine checking rejections takes a longer time: - if (rejections.exists(_.isInstanceOf[AuthenticationFailedRejection])) - Complete(HttpResponse(entity = "Nothing to see here, move along.")) - else - Rejected(rejections) - } - } - val neverAuth: Authenticator[String] = creds => None - - val route = - authRejectionsToNothingToSeeHere { - pathPrefix("auth") { - path("never") { - authenticateBasic("my-realm", neverAuth) { user => - complete("Welcome to the bat-cave!") - } - } - } - } - - // tests: - Get("/auth/never") ~> route ~> check { - status shouldEqual StatusCodes.OK - responseAs[String] shouldEqual "Nothing to see here, move along." - } - //# - } - "0mapRequest" in { - //#0mapRequest - def transformToPostRequest(req: HttpRequest): HttpRequest = req.copy(method = HttpMethods.POST) - val route = - mapRequest(transformToPostRequest) { - extractRequest { req => - complete(s"The request method was ${req.method.name}") - } - } - - Get("/") ~> route ~> check { - responseAs[String] shouldEqual "The request method was POST" - } - //# - } - "mapRequestContext" in { - //#mapRequestContext - val replaceRequest = - mapRequestContext(_.withRequest(HttpRequest(HttpMethods.POST))) - - val route = - replaceRequest { - extractRequest { req => - complete(req.method.value) - } - } - - // tests: - Get("/abc/def/ghi") ~> route ~> check { - responseAs[String] shouldEqual "POST" - } - //# - } - "0mapRouteResult" in { - //#0mapRouteResult - val rejectAll = // not particularly useful directive - mapRouteResult { - case _ => Rejected(List(AuthorizationFailedRejection)) - } - val route = - rejectAll { - complete("abc") - } - - // tests: - Get("/") ~> route ~> check { - rejections.nonEmpty shouldEqual true - } - //# - } - "mapRouteResultPF" in { - //#mapRouteResultPF - case object MyCustomRejection extends Rejection - val rejectRejections = // not particularly useful directive - mapRouteResultPF { - case Rejected(_) => Rejected(List(AuthorizationFailedRejection)) - } - val route = - rejectRejections { - reject(MyCustomRejection) - } - - // tests: - Get("/") ~> route ~> check { - rejection shouldEqual AuthorizationFailedRejection - } - //# - } - "mapRouteResultWithPF-0" in { - //#mapRouteResultWithPF-0 - case object MyCustomRejection extends Rejection - val rejectRejections = // not particularly useful directive - mapRouteResultWithPF { - case Rejected(_) => Future(Rejected(List(AuthorizationFailedRejection))) - } - val route = - rejectRejections { - reject(MyCustomRejection) - } - - // tests: - Get("/") ~> route ~> check { - rejection shouldEqual AuthorizationFailedRejection - } - //# - } - "mapRouteResultWith-0" in { - //#mapRouteResultWith-0 - case object MyCustomRejection extends Rejection - val rejectRejections = // not particularly useful directive - mapRouteResultWith { - case Rejected(_) => Future(Rejected(List(AuthorizationFailedRejection))) - case res => Future(res) - } - val route = - rejectRejections { - reject(MyCustomRejection) - } - - // tests: - Get("/") ~> route ~> check { - rejection shouldEqual AuthorizationFailedRejection - } - //# - } - "pass" in { - //#pass - val route = pass(complete("abc")) - - // tests: - Get("/") ~> route ~> check { - responseAs[String] shouldEqual "abc" - } - //# - } - "0provide" in { - //#0provide - def providePrefixedString(value: String): Directive1[String] = provide("prefix:" + value) - val route = - providePrefixedString("test") { value => - complete(value) - } - - // tests: - Get("/") ~> route ~> check { - responseAs[String] shouldEqual "prefix:test" - } - //# - } - "cancelRejections-filter-example" in { - //#cancelRejections-filter-example - def isMethodRejection: Rejection => Boolean = { - case MethodRejection(_) => true - case _ => false - } - - val route = - cancelRejections(isMethodRejection) { - post { - complete("Result") - } - } - - // tests: - Get("/") ~> route ~> check { - rejections shouldEqual Nil - handled shouldEqual false - } - //# - } - "cancelRejection-example" in { - //#cancelRejection-example - val route = - cancelRejection(MethodRejection(HttpMethods.POST)) { - post { - complete("Result") - } - } - - // tests: - Get("/") ~> route ~> check { - rejections shouldEqual Nil - handled shouldEqual false - } - //# - } - "extractRequest-example" in { - //#extractRequest-example - val route = - extractRequest { request => - complete(s"Request method is ${request.method.name} and content-type is ${request.entity.contentType}") - } - - // tests: - Post("/", "text") ~> route ~> check { - responseAs[String] shouldEqual "Request method is POST and content-type is text/plain; charset=UTF-8" - } - Get("/") ~> route ~> check { - responseAs[String] shouldEqual "Request method is GET and content-type is none/none" - } - //# - } - "extractSettings-examples" in { - //#extractSettings-examples - val route = - extractSettings { settings: RoutingSettings => - complete(s"RoutingSettings.renderVanityFooter = ${settings.renderVanityFooter}") - } - - // tests: - Get("/") ~> route ~> check { - responseAs[String] shouldEqual "RoutingSettings.renderVanityFooter = true" - } - //# - } - "mapSettings-examples" in { - //#mapSettings-examples - val tunedSettings = mapSettings { settings => - settings.withFileGetConditional(false) - } - - val route = - tunedSettings { - extractSettings { settings: RoutingSettings => - complete(s"RoutingSettings.fileGetConditional = ${settings.fileGetConditional}") - } - } - - // tests: - Get("/") ~> route ~> check { - responseAs[String] shouldEqual s"RoutingSettings.fileGetConditional = false" - } - //# - } - "extractRequestContext-example" in { - //#extractRequestContext-example - val route = - extractRequestContext { ctx => - ctx.log.debug("Using access to additional context availablethings, like the logger.") - val request = ctx.request - complete(s"Request method is ${request.method.name} and content-type is ${request.entity.contentType}") - } - - // tests: - Post("/", "text") ~> route ~> check { - responseAs[String] shouldEqual "Request method is POST and content-type is text/plain; charset=UTF-8" - } - Get("/") ~> route ~> check { - responseAs[String] shouldEqual "Request method is GET and content-type is none/none" - } - //# - } - "extractUri-example" in { - //#extractUri-example - val route = - extractUri { uri => - complete(s"Full URI: $uri") - } - - // tests: - Get("/") ~> route ~> check { - // tests are executed with the host assumed to be "example.com" - responseAs[String] shouldEqual "Full URI: http://example.com/" - } - Get("/test") ~> route ~> check { - responseAs[String] shouldEqual "Full URI: http://example.com/test" - } - //# - } - "mapUnmatchedPath-example" in { - //#mapUnmatchedPath-example - def ignore456(path: Uri.Path) = path match { - case s @ Uri.Path.Segment(head, tail) if head.startsWith("456") => - val newHead = head.drop(3) - if (newHead.isEmpty) tail - else s.copy(head = head.drop(3)) - case _ => path - } - val ignoring456 = mapUnmatchedPath(ignore456) - - val route = - pathPrefix("123") { - ignoring456 { - path("abc") { - complete("Content") - } - } - } - - // tests: - Get("/123/abc") ~> route ~> check { - responseAs[String] shouldEqual "Content" - } - Get("/123456/abc") ~> route ~> check { - responseAs[String] shouldEqual "Content" - } - //# - } - "extractUnmatchedPath-example" in { - //#extractUnmatchedPath-example - val route = - pathPrefix("abc") { - extractUnmatchedPath { remaining => - complete(s"Unmatched: '$remaining'") - } - } - - // tests: - Get("/abc") ~> route ~> check { - responseAs[String] shouldEqual "Unmatched: ''" - } - Get("/abc/456") ~> route ~> check { - responseAs[String] shouldEqual "Unmatched: '/456'" - } - //# - } - "extractRequestEntity-example" in { - //#extractRequestEntity-example - val route = - extractRequestEntity { entity => - complete(s"Request entity content-type is ${entity.contentType}") - } - - // tests: - val httpEntity = HttpEntity(ContentTypes.`text/plain(UTF-8)`, "req") - Post("/abc", httpEntity) ~> route ~> check { - responseAs[String] shouldEqual "Request entity content-type is text/plain; charset=UTF-8" - } - //# - } - "extractDataBytes-example" in { - //#extractDataBytes-example - val route = - extractDataBytes { data ⇒ - val sum = data.runFold(0) { (acc, i) ⇒ acc + i.utf8String.toInt } - onSuccess(sum) { s ⇒ - complete(HttpResponse(entity = HttpEntity(s.toString))) - } - } - - // tests: - val dataBytes = Source.fromIterator(() ⇒ Iterator.range(1, 10).map(x ⇒ ByteString(x.toString))) - Post("/abc", HttpEntity(ContentTypes.`text/plain(UTF-8)`, data = dataBytes)) ~> route ~> check { - responseAs[String] shouldEqual "45" - } - //# - } - "extractStrictEntity-example" in { - //#extractStrictEntity-example - import scala.concurrent.duration._ - val route = extractStrictEntity(3.seconds) { entity => - complete(entity.data.utf8String) - } - - // tests: - val dataBytes = Source.fromIterator(() ⇒ Iterator.range(1, 10).map(x ⇒ ByteString(x.toString))) - Post("/", HttpEntity(ContentTypes.`text/plain(UTF-8)`, data = dataBytes)) ~> route ~> check { - responseAs[String] shouldEqual "123456789" - } - //# - } - "toStrictEntity-example" in { - //#toStrictEntity-example - import scala.concurrent.duration._ - val route = toStrictEntity(3.seconds) { - extractRequest { req => - req.entity match { - case strict: HttpEntity.Strict => - complete(s"Request entity is strict, data=${strict.data.utf8String}") - case _ => - complete("Ooops, request entity is not strict!") - } - } - } - - // tests: - val dataBytes = Source.fromIterator(() ⇒ Iterator.range(1, 10).map(x ⇒ ByteString(x.toString))) - Post("/", HttpEntity(ContentTypes.`text/plain(UTF-8)`, data = dataBytes)) ~> route ~> check { - responseAs[String] shouldEqual "Request entity is strict, data=123456789" - } - //# - } - - "extractActorSystem-example" in { - //#extractActorSystem-example - val route = extractActorSystem { actorSystem => - complete(s"Actor System extracted, hash=${actorSystem.hashCode()}") - } - - // tests: - Get("/") ~> route ~> check { - responseAs[String] shouldEqual s"Actor System extracted, hash=${system.hashCode()}" - } - //# - } - -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/CodingDirectivesExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/CodingDirectivesExamplesSpec.scala deleted file mode 100644 index e8c9f4197d..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/CodingDirectivesExamplesSpec.scala +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl.server.directives - -import akka.http.scaladsl.coding._ -import docs.http.scaladsl.server.RoutingSpec -import akka.http.scaladsl.model.{ HttpResponse, StatusCodes } -import akka.http.scaladsl.model.headers.{ HttpEncodings, HttpEncoding, `Accept-Encoding`, `Content-Encoding` } -import akka.http.scaladsl.model.headers.HttpEncodings._ -import akka.http.scaladsl.server._ -import akka.util.ByteString -import org.scalatest.matchers.Matcher - -class CodingDirectivesExamplesSpec extends RoutingSpec { - "responseEncodingAccepted" in { - val route = responseEncodingAccepted(gzip) { complete("content") } - - Get("/") ~> route ~> check { - responseAs[String] shouldEqual "content" - } - Get("/") ~> `Accept-Encoding`(deflate) ~> route ~> check { - rejection shouldEqual UnacceptedResponseEncodingRejection(gzip) - } - } - "encodeResponse" in { - val route = encodeResponse { complete("content") } - - // tests: - Get("/") ~> route ~> check { - response should haveContentEncoding(identity) - } - Get("/") ~> `Accept-Encoding`(gzip, deflate) ~> route ~> check { - response should haveContentEncoding(gzip) - } - Get("/") ~> `Accept-Encoding`(deflate) ~> route ~> check { - response should haveContentEncoding(deflate) - } - Get("/") ~> `Accept-Encoding`(identity) ~> route ~> check { - response should haveContentEncoding(identity) - } - } - "encodeResponseWith" in { - val route = encodeResponseWith(Gzip) { complete("content") } - - // tests: - Get("/") ~> route ~> check { - response should haveContentEncoding(gzip) - } - Get("/") ~> `Accept-Encoding`(gzip, deflate) ~> route ~> check { - response should haveContentEncoding(gzip) - } - Get("/") ~> `Accept-Encoding`(deflate) ~> route ~> check { - rejection shouldEqual UnacceptedResponseEncodingRejection(gzip) - } - Get("/") ~> `Accept-Encoding`(identity) ~> route ~> check { - rejection shouldEqual UnacceptedResponseEncodingRejection(gzip) - } - } - - val helloGzipped = compress("Hello", Gzip) - val helloDeflated = compress("Hello", Deflate) - "decodeRequest" in { - val route = - decodeRequest { - entity(as[String]) { content: String => - complete(s"Request content: '$content'") - } - } - - // tests: - Post("/", helloGzipped) ~> `Content-Encoding`(gzip) ~> route ~> check { - responseAs[String] shouldEqual "Request content: 'Hello'" - } - Post("/", helloDeflated) ~> `Content-Encoding`(deflate) ~> route ~> check { - responseAs[String] shouldEqual "Request content: 'Hello'" - } - Post("/", "hello uncompressed") ~> `Content-Encoding`(identity) ~> route ~> check { - responseAs[String] shouldEqual "Request content: 'hello uncompressed'" - } - } - "decodeRequestWith-0" in { - val route = - decodeRequestWith(Gzip) { - entity(as[String]) { content: String => - complete(s"Request content: '$content'") - } - } - - // tests: - Post("/", helloGzipped) ~> `Content-Encoding`(gzip) ~> route ~> check { - responseAs[String] shouldEqual "Request content: 'Hello'" - } - Post("/", helloDeflated) ~> `Content-Encoding`(deflate) ~> route ~> check { - rejection shouldEqual UnsupportedRequestEncodingRejection(gzip) - } - Post("/", "hello") ~> `Content-Encoding`(identity) ~> route ~> check { - rejection shouldEqual UnsupportedRequestEncodingRejection(gzip) - } - } - "decodeRequestWith-1" in { - val route = - decodeRequestWith(Gzip, NoCoding) { - entity(as[String]) { content: String => - complete(s"Request content: '$content'") - } - } - - // tests: - Post("/", helloGzipped) ~> `Content-Encoding`(gzip) ~> route ~> check { - responseAs[String] shouldEqual "Request content: 'Hello'" - } - Post("/", helloDeflated) ~> `Content-Encoding`(deflate) ~> route ~> check { - rejections shouldEqual List(UnsupportedRequestEncodingRejection(gzip), UnsupportedRequestEncodingRejection(identity)) - } - Post("/", "hello uncompressed") ~> `Content-Encoding`(identity) ~> route ~> check { - responseAs[String] shouldEqual "Request content: 'hello uncompressed'" - } - } - - def haveContentEncoding(encoding: HttpEncoding): Matcher[HttpResponse] = - be(encoding) compose { (_: HttpResponse).header[`Content-Encoding`].map(_.encodings.head).getOrElse(HttpEncodings.identity) } - - def compress(input: String, encoder: Encoder): ByteString = encoder.encode(ByteString(input)) -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/CookieDirectivesExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/CookieDirectivesExamplesSpec.scala deleted file mode 100644 index 9e8e6fb414..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/CookieDirectivesExamplesSpec.scala +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl.server.directives - -import akka.http.scaladsl.server._ -import akka.http.scaladsl.model.headers.{ HttpCookie, Cookie, `Set-Cookie` } -import docs.http.scaladsl.server.RoutingSpec -import akka.http.scaladsl.model.DateTime - -class CookieDirectivesExamplesSpec extends RoutingSpec { - "cookie" in { - val route = - cookie("userName") { nameCookie => - complete(s"The logged in user is '${nameCookie.value}'") - } - - // tests: - Get("/") ~> Cookie("userName" -> "paul") ~> route ~> check { - responseAs[String] shouldEqual "The logged in user is 'paul'" - } - // missing cookie - Get("/") ~> route ~> check { - rejection shouldEqual MissingCookieRejection("userName") - } - Get("/") ~> Route.seal(route) ~> check { - responseAs[String] shouldEqual "Request is missing required cookie 'userName'" - } - } - "optionalCookie" in { - val route = - optionalCookie("userName") { - case Some(nameCookie) => complete(s"The logged in user is '${nameCookie.value}'") - case None => complete("No user logged in") - } - - // tests: - Get("/") ~> Cookie("userName" -> "paul") ~> route ~> check { - responseAs[String] shouldEqual "The logged in user is 'paul'" - } - Get("/") ~> route ~> check { - responseAs[String] shouldEqual "No user logged in" - } - } - "deleteCookie" in { - val route = - deleteCookie("userName") { - complete("The user was logged out") - } - - // tests: - Get("/") ~> route ~> check { - responseAs[String] shouldEqual "The user was logged out" - header[`Set-Cookie`] shouldEqual Some(`Set-Cookie`(HttpCookie("userName", value = "deleted", expires = Some(DateTime.MinValue)))) - } - } - "setCookie" in { - val route = - setCookie(HttpCookie("userName", value = "paul")) { - complete("The user was logged in") - } - - // tests: - Get("/") ~> route ~> check { - responseAs[String] shouldEqual "The user was logged in" - header[`Set-Cookie`] shouldEqual Some(`Set-Cookie`(HttpCookie("userName", value = "paul"))) - } - } -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/CustomDirectivesExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/CustomDirectivesExamplesSpec.scala deleted file mode 100644 index e2535aab70..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/CustomDirectivesExamplesSpec.scala +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package docs.http.scaladsl.server.directives - -import akka.http.scaladsl.server.{ Directive1, Directive } -import docs.http.scaladsl.server.RoutingSpec - -class CustomDirectivesExamplesSpec extends RoutingSpec { - - "labeling" in { - val getOrPut = get | put - - // tests: - val route = getOrPut { complete("ok") } - - Get("/") ~> route ~> check { - responseAs[String] shouldEqual "ok" - } - - Put("/") ~> route ~> check { - responseAs[String] shouldEqual "ok" - } - } - - "map-0" in { - val textParam: Directive1[String] = - parameter("text".as[String]) - - val lengthDirective: Directive1[Int] = - textParam.map(text => text.length) - - // tests: - Get("/?text=abcdefg") ~> lengthDirective(x => complete(x.toString)) ~> check { - responseAs[String] === "7" - } - } - - "tmap-1" in { - val twoIntParameters: Directive[(Int, Int)] = - parameters(("a".as[Int], "b".as[Int])) - - val myDirective: Directive1[String] = - twoIntParameters.tmap { - case (a, b) => (a + b).toString - } - - // tests: - Get("/?a=2&b=5") ~> myDirective(x => complete(x)) ~> check { - responseAs[String] === "7" - } - } - - "flatMap-0" in { - val intParameter: Directive1[Int] = parameter("a".as[Int]) - - val myDirective: Directive1[Int] = - intParameter.flatMap { - case a if a > 0 => provide(2 * a) - case _ => reject - } - - // tests: - Get("/?a=21") ~> myDirective(i => complete(i.toString)) ~> check { - responseAs[String] === "42" - } - Get("/?a=-18") ~> myDirective(i => complete(i.toString)) ~> check { - handled === false - } - } - -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/CustomHttpMethodSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/CustomHttpMethodSpec.scala deleted file mode 100644 index 5fe711b713..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/CustomHttpMethodSpec.scala +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl.server.directives - -import akka.http.scaladsl.{ Http, TestUtils } -import akka.http.scaladsl.client.RequestBuilding -import akka.http.scaladsl.model.HttpProtocols._ -import akka.http.scaladsl.model.RequestEntityAcceptance.Expected -import akka.http.scaladsl.model._ -import akka.http.scaladsl.server.Directives -import akka.stream.ActorMaterializer -import akka.testkit.AkkaSpec -import akka.util.ByteString -import org.scalatest.concurrent.ScalaFutures - -import scala.concurrent.duration._ - -class CustomHttpMethodSpec extends AkkaSpec with ScalaFutures - with Directives with RequestBuilding { - - implicit val mat = ActorMaterializer() - - "Http" should { - "allow registering custom method" in { - import system.dispatcher - val (_, host, port) = TestUtils.temporaryServerHostnameAndPort() - - //#application-custom - import akka.http.scaladsl.settings.{ ParserSettings, ServerSettings } - - // define custom media type: - val BOLT = HttpMethod.custom("BOLT", safe = false, - idempotent = true, requestEntityAcceptance = Expected) - - // add custom method to parser settings: - val parserSettings = ParserSettings(system).withCustomMethods(BOLT) - val serverSettings = ServerSettings(system).withParserSettings(parserSettings) - - val routes = extractMethod { method ⇒ - complete(s"This is a ${method.name} method request.") - } - val binding = Http().bindAndHandle(routes, host, port, settings = serverSettings) - //#application-custom - - val request = HttpRequest(BOLT, s"http://$host:$port/", protocol = `HTTP/1.0`) - val response = Http().singleRequest(request).futureValue - - response.status should ===(StatusCodes.OK) - val responseBody = response.toStrict(1.second).futureValue.entity.dataBytes.runFold(ByteString.empty)(_ ++ _).futureValue.utf8String - responseBody should ===("This is a BOLT method request.") - } - } -} - diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/DebuggingDirectivesExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/DebuggingDirectivesExamplesSpec.scala deleted file mode 100644 index 6c32784c76..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/DebuggingDirectivesExamplesSpec.scala +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl.server.directives - -import akka.event.{ LoggingAdapter, Logging } -import akka.event.Logging.LogLevel -import akka.http.scaladsl.model.{ HttpRequest, HttpResponse } -import akka.http.scaladsl.server.RouteResult -import akka.http.scaladsl.server.RouteResult.{ Rejected, Complete } -import akka.http.scaladsl.server.directives.{ DebuggingDirectives, LogEntry, LoggingMagnet } -import docs.http.scaladsl.server.RoutingSpec - -class DebuggingDirectivesExamplesSpec extends RoutingSpec { - "logRequest-0" in { - // different possibilities of using logRequest - - // The first alternatives use an implicitly available LoggingContext for logging - // marks with "get-user", log with debug level, HttpRequest.toString - DebuggingDirectives.logRequest("get-user") - - // marks with "get-user", log with info level, HttpRequest.toString - DebuggingDirectives.logRequest(("get-user", Logging.InfoLevel)) - - // logs just the request method at debug level - def requestMethod(req: HttpRequest): String = req.method.name - DebuggingDirectives.logRequest(requestMethod _) - - // logs just the request method at info level - def requestMethodAsInfo(req: HttpRequest): LogEntry = LogEntry(req.method.name, Logging.InfoLevel) - DebuggingDirectives.logRequest(requestMethodAsInfo _) - - // This one doesn't use the implicit LoggingContext but uses `println` for logging - def printRequestMethod(req: HttpRequest): Unit = println(req.method.name) - val logRequestPrintln = DebuggingDirectives.logRequest(LoggingMagnet(_ => printRequestMethod)) - - // tests: - Get("/") ~> logRequestPrintln(complete("logged")) ~> check { - responseAs[String] shouldEqual "logged" - } - } - "logRequestResult" in { - // different possibilities of using logRequestResponse - - // The first alternatives use an implicitly available LoggingContext for logging - // marks with "get-user", log with debug level, HttpRequest.toString, HttpResponse.toString - DebuggingDirectives.logRequestResult("get-user") - - // marks with "get-user", log with info level, HttpRequest.toString, HttpResponse.toString - DebuggingDirectives.logRequestResult(("get-user", Logging.InfoLevel)) - - // logs just the request method and response status at info level - def requestMethodAndResponseStatusAsInfo(req: HttpRequest): RouteResult => Option[LogEntry] = { - case RouteResult.Complete(res) => Some(LogEntry(req.method.name + ": " + res.status, Logging.InfoLevel)) - case _ => None // no log entries for rejections - } - DebuggingDirectives.logRequestResult(requestMethodAndResponseStatusAsInfo _) - - // This one doesn't use the implicit LoggingContext but uses `println` for logging - def printRequestMethodAndResponseStatus(req: HttpRequest)(res: RouteResult): Unit = - println(requestMethodAndResponseStatusAsInfo(req)(res).map(_.obj.toString).getOrElse("")) - val logRequestResultPrintln = DebuggingDirectives.logRequestResult(LoggingMagnet(_ => printRequestMethodAndResponseStatus)) - - // tests: - Get("/") ~> logRequestResultPrintln(complete("logged")) ~> check { - responseAs[String] shouldEqual "logged" - } - } - "logResult" in { - // different possibilities of using logResponse - - // The first alternatives use an implicitly available LoggingContext for logging - // marks with "get-user", log with debug level, HttpResponse.toString - DebuggingDirectives.logResult("get-user") - - // marks with "get-user", log with info level, HttpResponse.toString - DebuggingDirectives.logResult(("get-user", Logging.InfoLevel)) - - // logs just the response status at debug level - def responseStatus(res: RouteResult): String = res match { - case RouteResult.Complete(x) => x.status.toString - case RouteResult.Rejected(rejections) => "Rejected: " + rejections.mkString(", ") - } - DebuggingDirectives.logResult(responseStatus _) - - // logs just the response status at info level - def responseStatusAsInfo(res: RouteResult): LogEntry = LogEntry(responseStatus(res), Logging.InfoLevel) - DebuggingDirectives.logResult(responseStatusAsInfo _) - - // This one doesn't use the implicit LoggingContext but uses `println` for logging - def printResponseStatus(res: RouteResult): Unit = println(responseStatus(res)) - val logResultPrintln = DebuggingDirectives.logResult(LoggingMagnet(_ => printResponseStatus)) - - // tests: - Get("/") ~> logResultPrintln(complete("logged")) ~> check { - responseAs[String] shouldEqual "logged" - } - } - "logRequestResultWithResponseTime" in { - - def akkaResponseTimeLoggingFunction( - loggingAdapter: LoggingAdapter, - requestTimestamp: Long, - level: LogLevel = Logging.InfoLevel)(req: HttpRequest)(res: Any): Unit = { - val entry = res match { - case Complete(resp) => - val responseTimestamp: Long = System.nanoTime - val elapsedTime: Long = (responseTimestamp - requestTimestamp) / 1000000 - val loggingString = s"""Logged Request:${req.method}:${req.uri}:${resp.status}:${elapsedTime}""" - LogEntry(loggingString, level) - case Rejected(reason) => - LogEntry(s"Rejected Reason: ${reason.mkString(",")}", level) - } - entry.logTo(loggingAdapter) - } - def printResponseTime(log: LoggingAdapter) = { - val requestTimestamp = System.nanoTime - akkaResponseTimeLoggingFunction(log, requestTimestamp)(_) - } - - val logResponseTime = DebuggingDirectives.logRequestResult(LoggingMagnet(printResponseTime(_))) - - Get("/") ~> logResponseTime(complete("logged")) ~> check { - responseAs[String] shouldEqual "logged" - } - } -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/ExecutionDirectivesExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/ExecutionDirectivesExamplesSpec.scala deleted file mode 100644 index 21ec5bf39c..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/ExecutionDirectivesExamplesSpec.scala +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl.server.directives - -import akka.http.scaladsl.model.StatusCodes -import akka.http.scaladsl.server._ -import docs.http.scaladsl.server.RoutingSpec - -class ExecutionDirectivesExamplesSpec extends RoutingSpec { - "handleExceptions" in { - val divByZeroHandler = ExceptionHandler { - case _: ArithmeticException => complete((StatusCodes.BadRequest, "You've got your arithmetic wrong, fool!")) - } - val route = - path("divide" / IntNumber / IntNumber) { (a, b) => - handleExceptions(divByZeroHandler) { - complete(s"The result is ${a / b}") - } - } - - // tests: - Get("/divide/10/5") ~> route ~> check { - responseAs[String] shouldEqual "The result is 2" - } - Get("/divide/10/0") ~> route ~> check { - status shouldEqual StatusCodes.BadRequest - responseAs[String] shouldEqual "You've got your arithmetic wrong, fool!" - } - } - "handleRejections" in { - val totallyMissingHandler = RejectionHandler.newBuilder() - .handleNotFound { complete((StatusCodes.NotFound, "Oh man, what you are looking for is long gone.")) } - .handle { case ValidationRejection(msg, _) => complete((StatusCodes.InternalServerError, msg)) } - .result() - val route = - pathPrefix("handled") { - handleRejections(totallyMissingHandler) { - path("existing")(complete("This path exists")) ~ - path("boom")(reject(new ValidationRejection("This didn't work."))) - } - } - - // tests: - Get("/handled/existing") ~> route ~> check { - responseAs[String] shouldEqual "This path exists" - } - Get("/missing") ~> Route.seal(route) /* applies default handler */ ~> check { - status shouldEqual StatusCodes.NotFound - responseAs[String] shouldEqual "The requested resource could not be found." - } - Get("/handled/missing") ~> route ~> check { - status shouldEqual StatusCodes.NotFound - responseAs[String] shouldEqual "Oh man, what you are looking for is long gone." - } - Get("/handled/boom") ~> route ~> check { - status shouldEqual StatusCodes.InternalServerError - responseAs[String] shouldEqual "This didn't work." - } - } -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/FileAndResourceDirectivesExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/FileAndResourceDirectivesExamplesSpec.scala deleted file mode 100644 index 53bee2bb06..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/FileAndResourceDirectivesExamplesSpec.scala +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl.server.directives - -import akka.http.scaladsl.marshalling.ToEntityMarshaller -import akka.http.scaladsl.model.StatusCodes -import akka.http.scaladsl.server.directives.DirectoryListing -import akka.http.scaladsl.server.directives.FileAndResourceDirectives.DirectoryRenderer -import docs.http.scaladsl.server.RoutingSpec -import docs.http.scaladsl.server.RoutingSpec - -class FileAndResourceDirectivesExamplesSpec extends RoutingSpec { - "getFromFile-examples" in compileOnlySpec { - import akka.http.scaladsl.server.directives._ - import ContentTypeResolver.Default - - val route = - path("logs" / Segment) { name => - getFromFile(s"$name.log") // uses implicit ContentTypeResolver - } - - // tests: - Get("/logs/example") ~> route ~> check { - responseAs[String] shouldEqual "example file contents" - } - } - "getFromResource-examples" in compileOnlySpec { - import akka.http.scaladsl.server.directives._ - import ContentTypeResolver.Default - - val route = - path("logs" / Segment) { name => - getFromResource(s"$name.log") // uses implicit ContentTypeResolver - } - - // tests: - Get("/logs/example") ~> route ~> check { - responseAs[String] shouldEqual "example file contents" - } - } - "listDirectoryContents-examples" in compileOnlySpec { - val route = - path("tmp") { - listDirectoryContents("/tmp") - } ~ - path("custom") { - // implement your custom renderer here - val renderer = new DirectoryRenderer { - override def marshaller(renderVanityFooter: Boolean): ToEntityMarshaller[DirectoryListing] = ??? - } - listDirectoryContents("/tmp")(renderer) - } - - // tests: - Get("/logs/example") ~> route ~> check { - responseAs[String] shouldEqual "example file contents" - } - } - "getFromBrowseableDirectory-examples" in compileOnlySpec { - val route = - path("tmp") { - getFromBrowseableDirectory("/tmp") - } - - // tests: - Get("/tmp") ~> route ~> check { - status shouldEqual StatusCodes.OK - } - } - "getFromBrowseableDirectories-examples" in compileOnlySpec { - val route = - path("tmp") { - getFromBrowseableDirectories("/main", "/backups") - } - - // tests: - Get("/tmp") ~> route ~> check { - status shouldEqual StatusCodes.OK - } - } - "getFromDirectory-examples" in compileOnlySpec { - val route = - pathPrefix("tmp") { - getFromDirectory("/tmp") - } - - // tests: - Get("/tmp/example") ~> route ~> check { - responseAs[String] shouldEqual "example file contents" - } - } - "getFromResourceDirectory-examples" in compileOnlySpec { - val route = - pathPrefix("examples") { - getFromResourceDirectory("/examples") - } - - // tests: - Get("/examples/example-1") ~> route ~> check { - responseAs[String] shouldEqual "example file contents" - } - } - -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/FileUploadDirectivesExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/FileUploadDirectivesExamplesSpec.scala deleted file mode 100644 index 1bad238591..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/FileUploadDirectivesExamplesSpec.scala +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright (C) 2015-2016 Lightbend Inc. - */ -package docs.http.scaladsl.server.directives - -import akka.http.scaladsl.model._ -import akka.http.scaladsl.testkit.RouteTestTimeout -import akka.stream.scaladsl.Framing -import akka.util.ByteString -import docs.http.scaladsl.server.RoutingSpec -import akka.testkit.TestDuration -import scala.concurrent.Future -import scala.concurrent.duration._ - -class FileUploadDirectivesExamplesSpec extends RoutingSpec { - - override def testConfigSource = "akka.actor.default-mailbox.mailbox-type = \"akka.dispatch.UnboundedMailbox\"" - - // test touches disk, so give it some time - implicit val routeTimeout = RouteTestTimeout(3.seconds.dilated) - - "uploadedFile" in { - - val route = - uploadedFile("csv") { - case (metadata, file) => - // do something with the file and file metadata ... - file.delete() - complete(StatusCodes.OK) - } - - // tests: - val multipartForm = - Multipart.FormData( - Multipart.FormData.BodyPart.Strict( - "csv", - HttpEntity(ContentTypes.`text/plain(UTF-8)`, "1,5,7\n11,13,17"), - Map("filename" -> "data.csv"))) - - Post("/", multipartForm) ~> route ~> check { - status shouldEqual StatusCodes.OK - } - - } - - "fileUpload" in { - - // adding integers as a service ;) - val route = - extractRequestContext { ctx => - implicit val materializer = ctx.materializer - implicit val ec = ctx.executionContext - - fileUpload("csv") { - case (metadata, byteSource) => - - val sumF: Future[Int] = - // sum the numbers as they arrive so that we can - // accept any size of file - byteSource.via(Framing.delimiter(ByteString("\n"), 1024)) - .mapConcat(_.utf8String.split(",").toVector) - .map(_.toInt) - .runFold(0) { (acc, n) => acc + n } - - onSuccess(sumF) { sum => complete(s"Sum: $sum") } - } - } - - // tests: - val multipartForm = - Multipart.FormData(Multipart.FormData.BodyPart.Strict( - "csv", - HttpEntity(ContentTypes.`text/plain(UTF-8)`, "2,3,5\n7,11,13,17,23\n29,31,37\n"), - Map("filename" -> "primes.csv"))) - - Post("/", multipartForm) ~> route ~> check { - status shouldEqual StatusCodes.OK - responseAs[String] shouldEqual "Sum: 178" - } - - } - -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/FormFieldDirectivesExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/FormFieldDirectivesExamplesSpec.scala deleted file mode 100644 index d332858135..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/FormFieldDirectivesExamplesSpec.scala +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl.server.directives - -import akka.http.scaladsl.server.Route -import akka.http.scaladsl.model._ -import docs.http.scaladsl.server.RoutingSpec - -class FormFieldDirectivesExamplesSpec extends RoutingSpec { - "formFields" in { - val route = - formFields('color, 'age.as[Int]) { (color, age) => - complete(s"The color is '$color' and the age ten years ago was ${age - 10}") - } - - // tests: - Post("/", FormData("color" -> "blue", "age" -> "68")) ~> route ~> check { - responseAs[String] shouldEqual "The color is 'blue' and the age ten years ago was 58" - } - - Get("/") ~> Route.seal(route) ~> check { - status shouldEqual StatusCodes.BadRequest - responseAs[String] shouldEqual "Request is missing required form field 'color'" - } - } - "formField" in { - val route = - formField('color) { color => - complete(s"The color is '$color'") - } ~ - formField('id.as[Int]) { id => - complete(s"The id is '$id'") - } - - // tests: - Post("/", FormData("color" -> "blue")) ~> route ~> check { - responseAs[String] shouldEqual "The color is 'blue'" - } - - Get("/") ~> Route.seal(route) ~> check { - status shouldEqual StatusCodes.BadRequest - responseAs[String] shouldEqual "Request is missing required form field 'color'" - } - } - "formFieldMap" in { - val route = - formFieldMap { fields => - def formFieldString(formField: (String, String)): String = - s"""${formField._1} = '${formField._2}'""" - complete(s"The form fields are ${fields.map(formFieldString).mkString(", ")}") - } - - // tests: - Post("/", FormData("color" -> "blue", "count" -> "42")) ~> route ~> check { - responseAs[String] shouldEqual "The form fields are color = 'blue', count = '42'" - } - Post("/", FormData("x" -> "1", "x" -> "5")) ~> route ~> check { - responseAs[String] shouldEqual "The form fields are x = '5'" - } - } - "formFieldMultiMap" in { - val route = - formFieldMultiMap { fields => - complete("There are " + - s"form fields ${fields.map(x => x._1 + " -> " + x._2.size).mkString(", ")}") - } - - // tests: - Post("/", FormData("color" -> "blue", "count" -> "42")) ~> route ~> check { - responseAs[String] shouldEqual "There are form fields color -> 1, count -> 1" - } - Post("/", FormData("x" -> "23", "x" -> "4", "x" -> "89")) ~> route ~> check { - responseAs[String] shouldEqual "There are form fields x -> 3" - } - } - "formFieldSeq" in { - val route = - formFieldSeq { fields => - def formFieldString(formField: (String, String)): String = - s"""${formField._1} = '${formField._2}'""" - complete(s"The form fields are ${fields.map(formFieldString).mkString(", ")}") - } - - // tests: - Post("/", FormData("color" -> "blue", "count" -> "42")) ~> route ~> check { - responseAs[String] shouldEqual "The form fields are color = 'blue', count = '42'" - } - Post("/", FormData("x" -> "23", "x" -> "4", "x" -> "89")) ~> route ~> check { - responseAs[String] shouldEqual "The form fields are x = '23', x = '4', x = '89'" - } - } - -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/FutureDirectivesExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/FutureDirectivesExamplesSpec.scala deleted file mode 100644 index cc9d7a38b7..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/FutureDirectivesExamplesSpec.scala +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl.server.directives - -import java.util.concurrent.TimeUnit - -import docs.http.scaladsl.server.RoutingSpec - -import scala.concurrent.Future -import scala.concurrent.duration._ -import scala.util.{ Failure, Success } -import akka.http.scaladsl.server.{ CircuitBreakerOpenRejection, ExceptionHandler, Route } -import akka.util.Timeout -import akka.http.scaladsl.model._ -import StatusCodes._ -import akka.pattern.CircuitBreaker - -// format: OFF - -class FutureDirectivesExamplesSpec extends RoutingSpec { - object TestException extends Throwable - - implicit val myExceptionHandler = - ExceptionHandler { - case TestException => ctx => - ctx.complete((InternalServerError, "Unsuccessful future!")) - } - - implicit val responseTimeout = Timeout(2, TimeUnit.SECONDS) - - "onComplete" in { - def divide(a: Int, b: Int): Future[Int] = Future { - a / b - } - - val route = - path("divide" / IntNumber / IntNumber) { (a, b) => - onComplete(divide(a, b)) { - case Success(value) => complete(s"The result was $value") - case Failure(ex) => complete((InternalServerError, s"An error occurred: ${ex.getMessage}")) - } - } - - // tests: - Get("/divide/10/2") ~> route ~> check { - responseAs[String] shouldEqual "The result was 5" - } - - Get("/divide/10/0") ~> Route.seal(route) ~> check { - status shouldEqual InternalServerError - responseAs[String] shouldEqual "An error occurred: / by zero" - } - } - - "onCompleteWithBreaker" in { - def divide(a: Int, b: Int): Future[Int] = Future { - a / b - } - - val resetTimeout = 1.second - val breaker = new CircuitBreaker(system.scheduler, - maxFailures = 1, - callTimeout = 5.seconds, - resetTimeout - ) - - val route = - path("divide" / IntNumber / IntNumber) { (a, b) => - onCompleteWithBreaker(breaker)(divide(a, b)) { - case Success(value) => complete(s"The result was $value") - case Failure(ex) => complete((InternalServerError, s"An error occurred: ${ex.getMessage}")) - } - } - - // tests: - Get("/divide/10/2") ~> route ~> check { - responseAs[String] shouldEqual "The result was 5" - } - - Get("/divide/10/0") ~> Route.seal(route) ~> check { - status shouldEqual InternalServerError - responseAs[String] shouldEqual "An error occurred: / by zero" - } // opens the circuit breaker - - Get("/divide/10/2") ~> route ~> check { - rejection shouldBe a[CircuitBreakerOpenRejection] - } - - Thread.sleep(resetTimeout.toMillis + 200) - - Get("/divide/10/2") ~> route ~> check { - responseAs[String] shouldEqual "The result was 5" - } - } - - "onSuccess" in { - val route = - path("success") { - onSuccess(Future { "Ok" }) { extraction => - complete(extraction) - } - } ~ - path("failure") { - onSuccess(Future.failed[String](TestException)) { extraction => - complete(extraction) - } - } - - // tests: - Get("/success") ~> route ~> check { - responseAs[String] shouldEqual "Ok" - } - - Get("/failure") ~> Route.seal(route) ~> check { - status shouldEqual InternalServerError - responseAs[String] shouldEqual "Unsuccessful future!" - } - } - - "completeOrRecoverWith" in { - val route = - path("success") { - completeOrRecoverWith(Future { "Ok" }) { extraction => - failWith(extraction) // not executed. - } - } ~ - path("failure") { - completeOrRecoverWith(Future.failed[String](TestException)) { extraction => - failWith(extraction) - } - } - - // tests: - Get("/success") ~> route ~> check { - responseAs[String] shouldEqual "Ok" - } - - Get("/failure") ~> Route.seal(route) ~> check { - status shouldEqual InternalServerError - responseAs[String] shouldEqual "Unsuccessful future!" - } - } -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/HeaderDirectivesExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/HeaderDirectivesExamplesSpec.scala deleted file mode 100644 index e417ca35a4..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/HeaderDirectivesExamplesSpec.scala +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl.server.directives - -import akka.http.scaladsl.model.StatusCodes._ -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers._ -import akka.http.scaladsl.server.{ InvalidOriginRejection, MissingHeaderRejection, Route } -import docs.http.scaladsl.server.RoutingSpec -import org.scalatest.Inside - -class HeaderDirectivesExamplesSpec extends RoutingSpec with Inside { - "headerValueByName-0" in { - val route = - headerValueByName("X-User-Id") { userId => - complete(s"The user is $userId") - } - - // tests: - Get("/") ~> RawHeader("X-User-Id", "Joe42") ~> route ~> check { - responseAs[String] shouldEqual "The user is Joe42" - } - - Get("/") ~> Route.seal(route) ~> check { - status shouldEqual BadRequest - responseAs[String] shouldEqual "Request is missing required HTTP header 'X-User-Id'" - } - } - "headerValue-0" in { - def extractHostPort: HttpHeader => Option[Int] = { - case h: `Host` => Some(h.port) - case x => None - } - - val route = - headerValue(extractHostPort) { port => - complete(s"The port was $port") - } - - // tests: - Get("/") ~> Host("example.com", 5043) ~> route ~> check { - responseAs[String] shouldEqual "The port was 5043" - } - Get("/") ~> Route.seal(route) ~> check { - status shouldEqual NotFound - responseAs[String] shouldEqual "The requested resource could not be found." - } - } - "optionalHeaderValue-0" in { - def extractHostPort: HttpHeader => Option[Int] = { - case h: `Host` => Some(h.port) - case x => None - } - - val route = - optionalHeaderValue(extractHostPort) { - case Some(port) => complete(s"The port was $port") - case None => complete(s"The port was not provided explicitly") - } ~ // can also be written as: - optionalHeaderValue(extractHostPort) { port => - complete { - port match { - case Some(p) => s"The port was $p" - case _ => "The port was not provided explicitly" - } - } - } - - // tests: - Get("/") ~> Host("example.com", 5043) ~> route ~> check { - responseAs[String] shouldEqual "The port was 5043" - } - Get("/") ~> Route.seal(route) ~> check { - responseAs[String] shouldEqual "The port was not provided explicitly" - } - } - "optionalHeaderValueByName-0" in { - val route = - optionalHeaderValueByName("X-User-Id") { - case Some(userId) => complete(s"The user is $userId") - case None => complete(s"No user was provided") - } ~ // can also be written as: - optionalHeaderValueByName("port") { port => - complete { - port match { - case Some(p) => s"The user is $p" - case _ => "No user was provided" - } - } - } - - // tests: - Get("/") ~> RawHeader("X-User-Id", "Joe42") ~> route ~> check { - responseAs[String] shouldEqual "The user is Joe42" - } - Get("/") ~> Route.seal(route) ~> check { - responseAs[String] shouldEqual "No user was provided" - } - } - "headerValuePF-0" in { - def extractHostPort: PartialFunction[HttpHeader, Int] = { - case h: `Host` => h.port - } - - val route = - headerValuePF(extractHostPort) { port => - complete(s"The port was $port") - } - - // tests: - Get("/") ~> Host("example.com", 5043) ~> route ~> check { - responseAs[String] shouldEqual "The port was 5043" - } - Get("/") ~> Route.seal(route) ~> check { - status shouldEqual NotFound - responseAs[String] shouldEqual "The requested resource could not be found." - } - } - "optionalHeaderValuePF-0" in { - def extractHostPort: PartialFunction[HttpHeader, Int] = { - case h: `Host` => h.port - } - - val route = - optionalHeaderValuePF(extractHostPort) { - case Some(port) => complete(s"The port was $port") - case None => complete(s"The port was not provided explicitly") - } ~ // can also be written as: - optionalHeaderValuePF(extractHostPort) { port => - complete { - port match { - case Some(p) => s"The port was $p" - case _ => "The port was not provided explicitly" - } - } - } - - // tests: - Get("/") ~> Host("example.com", 5043) ~> route ~> check { - responseAs[String] shouldEqual "The port was 5043" - } - Get("/") ~> Route.seal(route) ~> check { - responseAs[String] shouldEqual "The port was not provided explicitly" - } - } - "headerValueByType-0" in { - val route = - headerValueByType[Origin]() { origin => - complete(s"The first origin was ${origin.origins.head}") - } - - val originHeader = Origin(HttpOrigin("http://localhost:8080")) - - // tests: - // extract a header if the type is matching - Get("abc") ~> originHeader ~> route ~> check { - responseAs[String] shouldEqual "The first origin was http://localhost:8080" - } - - // reject a request if no header of the given type is present - Get("abc") ~> route ~> check { - inside(rejection) { case MissingHeaderRejection("Origin") => } - } - } - "optionalHeaderValueByType-0" in { - val route = - optionalHeaderValueByType[Origin]() { - case Some(origin) => complete(s"The first origin was ${origin.origins.head}") - case None => complete("No Origin header found.") - } - - val originHeader = Origin(HttpOrigin("http://localhost:8080")) - - // tests: - // extract Some(header) if the type is matching - Get("abc") ~> originHeader ~> route ~> check { - responseAs[String] shouldEqual "The first origin was http://localhost:8080" - } - - // extract None if no header of the given type is present - Get("abc") ~> route ~> check { - responseAs[String] shouldEqual "No Origin header found." - } - } - "checkSameOrigin-0" in { - val correctOrigin = HttpOrigin("http://localhost:8080") - val route = checkSameOrigin(HttpOriginRange(correctOrigin)) { - complete("Result") - } - - // tests: - // handle request with correct origin headers - Get("abc") ~> Origin(correctOrigin) ~> route ~> check { - status shouldEqual StatusCodes.OK - responseAs[String] shouldEqual "Result" - } - - // reject request with missed origin header - Get("abc") ~> route ~> check { - inside(rejection) { - case MissingHeaderRejection(headerName) ⇒ headerName shouldEqual Origin.name - } - } - - // rejects request with invalid origin headers - val invalidHttpOrigin = HttpOrigin("http://invalid.com") - val invalidOriginHeader = Origin(invalidHttpOrigin) - Get("abc") ~> invalidOriginHeader ~> route ~> check { - inside(rejection) { - case InvalidOriginRejection(allowedOrigins) ⇒ - allowedOrigins shouldEqual Seq(correctOrigin) - } - } - Get("abc") ~> invalidOriginHeader ~> Route.seal(route) ~> check { - status shouldEqual StatusCodes.Forbidden - responseAs[String] should include(s"${correctOrigin.value}") - } - } -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/HostDirectivesExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/HostDirectivesExamplesSpec.scala deleted file mode 100644 index 4a6cc91acc..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/HostDirectivesExamplesSpec.scala +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl.server.directives - -import akka.http.scaladsl.model._ -import docs.http.scaladsl.server.RoutingSpec -import headers._ -import StatusCodes._ - -class HostDirectivesExamplesSpec extends RoutingSpec { - - "extractHost" in { - val route = - extractHost { hn => - complete(s"Hostname: $hn") - } - - // tests: - Get() ~> Host("company.com", 9090) ~> route ~> check { - status shouldEqual OK - responseAs[String] shouldEqual "Hostname: company.com" - } - } - - "list-of-hosts" in { - val route = - host("api.company.com", "rest.company.com") { - complete("Ok") - } - - // tests: - Get() ~> Host("rest.company.com") ~> route ~> check { - status shouldEqual OK - responseAs[String] shouldEqual "Ok" - } - - Get() ~> Host("notallowed.company.com") ~> route ~> check { - handled shouldBe false - } - } - - "predicate" in { - val shortOnly: String => Boolean = (hostname) => hostname.length < 10 - - val route = - host(shortOnly) { - complete("Ok") - } - - // tests: - Get() ~> Host("short.com") ~> route ~> check { - status shouldEqual OK - responseAs[String] shouldEqual "Ok" - } - - Get() ~> Host("verylonghostname.com") ~> route ~> check { - handled shouldBe false - } - } - - "using-regex" in { - val route = - host("api|rest".r) { prefix => - complete(s"Extracted prefix: $prefix") - } ~ - host("public.(my|your)company.com".r) { captured => - complete(s"You came through $captured company") - } - - // tests: - Get() ~> Host("api.company.com") ~> route ~> check { - status shouldEqual OK - responseAs[String] shouldEqual "Extracted prefix: api" - } - - Get() ~> Host("public.mycompany.com") ~> route ~> check { - status shouldEqual OK - responseAs[String] shouldEqual "You came through my company" - } - } - - "failing-regex" in { - an[IllegalArgumentException] should be thrownBy { - host("server-([0-9]).company.(com|net|org)".r) { target => - complete("Will never complete :'(") - } - } - } - -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/JsonStreamingExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/JsonStreamingExamplesSpec.scala deleted file mode 100644 index 2f4b2e6766..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/JsonStreamingExamplesSpec.scala +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (C) 2009-2015 Typesafe Inc. - */ - -package docs.http.scaladsl.server.directives - -import akka.NotUsed -import akka.http.scaladsl.common.{ EntityStreamingSupport, JsonEntityStreamingSupport } -import akka.http.scaladsl.marshalling._ -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers.Accept -import akka.http.scaladsl.server.{ UnacceptedResponseContentTypeRejection, UnsupportedRequestContentTypeRejection } -import akka.stream.scaladsl.{ Flow, Source } -import akka.util.ByteString -import docs.http.scaladsl.server.RoutingSpec - -import scala.concurrent.Future - -class JsonStreamingExamplesSpec extends RoutingSpec { - - //#models - case class Tweet(uid: Int, txt: String) - case class Measurement(id: String, value: Int) - //# - - val tweets = List( - Tweet(1, "#Akka rocks!"), - Tweet(2, "Streaming is so hot right now!"), - Tweet(3, "You cannot enter the same river twice.")) - def getTweets = Source(tweets) - - //#formats - object MyJsonProtocol - extends akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport - with spray.json.DefaultJsonProtocol { - - implicit val tweetFormat = jsonFormat2(Tweet.apply) - implicit val measurementFormat = jsonFormat2(Measurement.apply) - } - //# - - "spray-json-response-streaming" in { - // [1] import "my protocol", for marshalling Tweet objects: - import MyJsonProtocol._ - - // [2] pick a Source rendering support trait: - // Note that the default support renders the Source as JSON Array - implicit val jsonStreamingSupport: JsonEntityStreamingSupport = EntityStreamingSupport.json() - - val route = - path("tweets") { - // [3] simply complete a request with a source of tweets: - val tweets: Source[Tweet, NotUsed] = getTweets - complete(tweets) - } - - // tests ------------------------------------------------------------ - val AcceptJson = Accept(MediaRange(MediaTypes.`application/json`)) - val AcceptXml = Accept(MediaRange(MediaTypes.`text/xml`)) - - Get("/tweets").withHeaders(AcceptJson) ~> route ~> check { - responseAs[String] shouldEqual - """[""" + - """{"uid":1,"txt":"#Akka rocks!"},""" + - """{"uid":2,"txt":"Streaming is so hot right now!"},""" + - """{"uid":3,"txt":"You cannot enter the same river twice."}""" + - """]""" - } - - // endpoint can only marshal Json, so it will *reject* requests for application/xml: - Get("/tweets").withHeaders(AcceptXml) ~> route ~> check { - handled should ===(false) - rejection should ===(UnacceptedResponseContentTypeRejection(Set(ContentTypes.`application/json`))) - } - } - - "line-by-line-json-response-streaming" in { - import MyJsonProtocol._ - - // Configure the EntityStreamingSupport to render the elements as: - // {"example":42} - // {"example":43} - // ... - // {"example":1000} - val start = ByteString.empty - val sep = ByteString("\n") - val end = ByteString.empty - - implicit val jsonStreamingSupport = EntityStreamingSupport.json() - .withFramingRenderer(Flow[ByteString].intersperse(start, sep, end)) - - val route = - path("tweets") { - // [3] simply complete a request with a source of tweets: - val tweets: Source[Tweet, NotUsed] = getTweets - complete(tweets) - } - - // tests ------------------------------------------------------------ - val AcceptJson = Accept(MediaRange(MediaTypes.`application/json`)) - - Get("/tweets").withHeaders(AcceptJson) ~> route ~> check { - responseAs[String] shouldEqual - """{"uid":1,"txt":"#Akka rocks!"}""" + "\n" + - """{"uid":2,"txt":"Streaming is so hot right now!"}""" + "\n" + - """{"uid":3,"txt":"You cannot enter the same river twice."}""" - } - } - - "csv-example" in { - // [1] provide a marshaller to ByteString - implicit val tweetAsCsv = Marshaller.strict[Tweet, ByteString] { t => - Marshalling.WithFixedContentType(ContentTypes.`text/csv(UTF-8)`, () => { - val txt = t.txt.replaceAll(",", ".") - val uid = t.uid - ByteString(List(uid, txt).mkString(",")) - }) - } - - // [2] enable csv streaming: - implicit val csvStreaming = EntityStreamingSupport.csv() - - val route = - path("tweets") { - val tweets: Source[Tweet, NotUsed] = getTweets - complete(tweets) - } - - // tests ------------------------------------------------------------ - val AcceptCsv = Accept(MediaRange(MediaTypes.`text/csv`)) - - Get("/tweets").withHeaders(AcceptCsv) ~> route ~> check { - responseAs[String] shouldEqual - "1,#Akka rocks!" + "\n" + - "2,Streaming is so hot right now!" + "\n" + - "3,You cannot enter the same river twice." - } - } - - "response-streaming-modes" in { - - { - //#async-rendering - import MyJsonProtocol._ - implicit val jsonStreamingSupport: JsonEntityStreamingSupport = - EntityStreamingSupport.json() - .withParallelMarshalling(parallelism = 8, unordered = false) - - path("tweets") { - val tweets: Source[Tweet, NotUsed] = getTweets - complete(tweets) - } - //# - } - - { - - //#async-unordered-rendering - import MyJsonProtocol._ - implicit val jsonStreamingSupport: JsonEntityStreamingSupport = - EntityStreamingSupport.json() - .withParallelMarshalling(parallelism = 8, unordered = true) - - path("tweets" / "unordered") { - val tweets: Source[Tweet, NotUsed] = getTweets - complete(tweets) - } - //# - } - } - - "spray-json-request-streaming" in { - // [1] import "my protocol", for unmarshalling Measurement objects: - import MyJsonProtocol._ - - // [2] enable Json Streaming - implicit val jsonStreamingSupport = EntityStreamingSupport.json() - - // prepare your persisting logic here - val persistMetrics = Flow[Measurement] - - val route = - path("metrics") { - // [3] extract Source[Measurement, _] - entity(asSourceOf[Measurement]) { measurements => - // alternative syntax: - // entity(as[Source[Measurement, NotUsed]]) { measurements => - val measurementsSubmitted: Future[Int] = - measurements - .via(persistMetrics) - .runFold(0) { (cnt, _) => cnt + 1 } - - complete { - measurementsSubmitted.map(n => Map("msg" -> s"""Total metrics received: $n""")) - } - } - } - - // tests ------------------------------------------------------------ - // uploading an array or newline separated values works out of the box - val data = HttpEntity( - ContentTypes.`application/json`, - """ - |{"id":"temp","value":32} - |{"id":"temp","value":31} - | - """.stripMargin) - - Post("/metrics", entity = data) ~> route ~> check { - status should ===(StatusCodes.OK) - responseAs[String] should ===("""{"msg":"Total metrics received: 2"}""") - } - - // the FramingWithContentType will reject any content type that it does not understand: - val xmlData = HttpEntity( - ContentTypes.`text/xml(UTF-8)`, - """| - |""".stripMargin) - - Post("/metrics", entity = xmlData) ~> route ~> check { - handled should ===(false) - rejection should ===(UnsupportedRequestContentTypeRejection(Set(ContentTypes.`application/json`))) - } - } - -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/MarshallingDirectivesExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/MarshallingDirectivesExamplesSpec.scala deleted file mode 100644 index 8f7c855dc9..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/MarshallingDirectivesExamplesSpec.scala +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl.server.directives - -import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport -import docs.http.scaladsl.server.RoutingSpec -import akka.http.scaladsl.model.MediaTypes.`application/json` -import akka.http.scaladsl.model._ -import spray.json.{ JsValue, DefaultJsonProtocol } - -//# person-case-class -case class Person(name: String, favoriteNumber: Int) - -//# person-json-support -object PersonJsonSupport extends DefaultJsonProtocol with SprayJsonSupport { - implicit val PortofolioFormats = jsonFormat2(Person) -} -//# - -class MarshallingDirectivesExamplesSpec extends RoutingSpec { - - "example-entity-with-json" in { - import PersonJsonSupport._ - - val route = post { - entity(as[Person]) { person => - complete(s"Person: ${person.name} - favorite number: ${person.favoriteNumber}") - } - } - - // tests: - Post("/", HttpEntity(`application/json`, """{ "name": "Jane", "favoriteNumber" : 42 }""")) ~> - route ~> check { - responseAs[String] shouldEqual "Person: Jane - favorite number: 42" - } - } - - "example-entity-with-raw-json" in { - import PersonJsonSupport._ - - val route = post { - entity(as[JsValue]) { json => - complete(s"Person: ${json.asJsObject.fields("name")} - favorite number: ${json.asJsObject.fields("favoriteNumber")}") - } - } - - // tests: - Post("/", HttpEntity(`application/json`, """{ "name": "Jane", "favoriteNumber" : 42 }""")) ~> - route ~> check { - responseAs[String] shouldEqual """Person: "Jane" - favorite number: 42""" - } - } - - "example-completeWith-with-json" in { - import PersonJsonSupport._ - - val findPerson = (f: Person => Unit) => { - - //... some processing logic... - - //complete the request - f(Person("Jane", 42)) - } - - val route = get { - completeWith(instanceOf[Person]) { completionFunction => findPerson(completionFunction) } - } - - // tests: - Get("/") ~> route ~> check { - mediaType shouldEqual `application/json` - responseAs[String] should include(""""name":"Jane"""") - responseAs[String] should include(""""favoriteNumber":42""") - } - } - - "example-handleWith-with-json" in { - import PersonJsonSupport._ - - val updatePerson = (person: Person) => { - - //... some processing logic... - - //return the person - person - } - - val route = post { - handleWith(updatePerson) - } - - // tests: - Post("/", HttpEntity(`application/json`, """{ "name": "Jane", "favoriteNumber" : 42 }""")) ~> - route ~> check { - mediaType shouldEqual `application/json` - responseAs[String] should include(""""name":"Jane"""") - responseAs[String] should include(""""favoriteNumber":42""") - } - } -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/MethodDirectivesExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/MethodDirectivesExamplesSpec.scala deleted file mode 100644 index 4c6e18dec3..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/MethodDirectivesExamplesSpec.scala +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl.server.directives - -import akka.http.scaladsl.model._ -import akka.http.scaladsl.server.Route -import docs.http.scaladsl.server.RoutingSpec - -class MethodDirectivesExamplesSpec extends RoutingSpec { - - "delete-method" in { - val route = delete { complete("This is a DELETE request.") } - - // tests: - Delete("/") ~> route ~> check { - responseAs[String] shouldEqual "This is a DELETE request." - } - } - - "get-method" in { - val route = get { complete("This is a GET request.") } - - // tests: - Get("/") ~> route ~> check { - responseAs[String] shouldEqual "This is a GET request." - } - } - - "head-method" in { - val route = head { complete("This is a HEAD request.") } - - // tests: - Head("/") ~> route ~> check { - responseAs[String] shouldEqual "This is a HEAD request." - } - } - - "options-method" in { - val route = options { complete("This is an OPTIONS request.") } - - // tests: - Options("/") ~> route ~> check { - responseAs[String] shouldEqual "This is an OPTIONS request." - } - } - - "patch-method" in { - val route = patch { complete("This is a PATCH request.") } - - // tests: - Patch("/", "patch content") ~> route ~> check { - responseAs[String] shouldEqual "This is a PATCH request." - } - } - - "post-method" in { - val route = post { complete("This is a POST request.") } - - // tests: - Post("/", "post content") ~> route ~> check { - responseAs[String] shouldEqual "This is a POST request." - } - } - - "put-method" in { - val route = put { complete("This is a PUT request.") } - - // tests: - Put("/", "put content") ~> route ~> check { - responseAs[String] shouldEqual "This is a PUT request." - } - } - - "method-example" in { - val route = method(HttpMethods.PUT) { complete("This is a PUT request.") } - - // tests: - Put("/", "put content") ~> route ~> check { - responseAs[String] shouldEqual "This is a PUT request." - } - - Get("/") ~> Route.seal(route) ~> check { - status shouldEqual StatusCodes.MethodNotAllowed - responseAs[String] shouldEqual "HTTP method not allowed, supported methods: PUT" - } - } - - "extractMethod-example" in { - val route = - get { - complete("This is a GET request.") - } ~ - extractMethod { method => - complete(s"This ${method.name} request, clearly is not a GET!") - } - - // tests: - Get("/") ~> route ~> check { - responseAs[String] shouldEqual "This is a GET request." - } - - Put("/") ~> route ~> check { - responseAs[String] shouldEqual "This PUT request, clearly is not a GET!" - } - Head("/") ~> route ~> check { - responseAs[String] shouldEqual "This HEAD request, clearly is not a GET!" - } - } - - "overrideMethodWithParameter-0" in { - val route = - overrideMethodWithParameter("method") { - get { - complete("This looks like a GET request.") - } ~ - post { - complete("This looks like a POST request.") - } - } - - // tests: - Get("/?method=POST") ~> route ~> check { - responseAs[String] shouldEqual "This looks like a POST request." - } - Post("/?method=get") ~> route ~> check { - responseAs[String] shouldEqual "This looks like a GET request." - } - - Get("/?method=hallo") ~> route ~> check { - status shouldEqual StatusCodes.NotImplemented - } - } - -} \ No newline at end of file diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/MiscDirectivesExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/MiscDirectivesExamplesSpec.scala deleted file mode 100644 index 49ca6be86b..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/MiscDirectivesExamplesSpec.scala +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl.server.directives - -import akka.http.scaladsl.model._ -import akka.http.scaladsl.server._ -import headers._ -import docs.http.scaladsl.server.RoutingSpec -import java.net.InetAddress - -class MiscDirectivesExamplesSpec extends RoutingSpec { - - "extractClientIP-example" in { - val route = extractClientIP { ip => - complete("Client's ip is " + ip.toOption.map(_.getHostAddress).getOrElse("unknown")) - } - - // tests: - Get("/").withHeaders(`Remote-Address`(RemoteAddress(InetAddress.getByName("192.168.3.12")))) ~> route ~> check { - responseAs[String] shouldEqual "Client's ip is 192.168.3.12" - } - } - - "rejectEmptyResponse-example" in { - val route = rejectEmptyResponse { - path("even" / IntNumber) { i => - complete { - // returns Some(evenNumberDescription) or None - Option(i).filter(_ % 2 == 0).map { num => - s"Number $num is even." - } - } - } - } - - // tests: - Get("/even/23") ~> Route.seal(route) ~> check { - status shouldEqual StatusCodes.NotFound - } - Get("/even/28") ~> route ~> check { - responseAs[String] shouldEqual "Number 28 is even." - } - } - - "requestEntityEmptyPresent-example" in { - val route = - requestEntityEmpty { - complete("request entity empty") - } ~ - requestEntityPresent { - complete("request entity present") - } - - // tests: - Post("/", "text") ~> Route.seal(route) ~> check { - responseAs[String] shouldEqual "request entity present" - } - Post("/") ~> route ~> check { - responseAs[String] shouldEqual "request entity empty" - } - } - - "selectPreferredLanguage-example" in { - val request = Get() ~> `Accept-Language`( - Language("en-US"), - Language("en") withQValue 0.7f, - LanguageRange.`*` withQValue 0.1f, - Language("de") withQValue 0.5f) - - request ~> { - selectPreferredLanguage("en", "en-US") { lang => - complete(lang.toString) - } - } ~> check { responseAs[String] shouldEqual "en-US" } - - request ~> { - selectPreferredLanguage("de-DE", "hu") { lang => - complete(lang.toString) - } - } ~> check { responseAs[String] shouldEqual "de-DE" } - } - - "validate-example" in { - val route = - extractUri { uri => - validate(uri.path.toString.size < 5, s"Path too long: '${uri.path.toString}'") { - complete(s"Full URI: $uri") - } - } - - // tests: - Get("/234") ~> route ~> check { - responseAs[String] shouldEqual "Full URI: http://example.com/234" - } - Get("/abcdefghijkl") ~> route ~> check { - rejection shouldEqual ValidationRejection("Path too long: '/abcdefghijkl'", None) - } - } - - "withSizeLimit-example" in { - val route = withSizeLimit(500) { - entity(as[String]) { _ ⇒ - complete(HttpResponse()) - } - } - - // tests: - def entityOfSize(size: Int) = - HttpEntity(ContentTypes.`text/plain(UTF-8)`, "0" * size) - - Post("/abc", entityOfSize(500)) ~> route ~> check { - status shouldEqual StatusCodes.OK - } - - Post("/abc", entityOfSize(501)) ~> Route.seal(route) ~> check { - status shouldEqual StatusCodes.BadRequest - } - - } - - "withSizeLimit-execution-moment-example" in { - val route = withSizeLimit(500) { - complete(HttpResponse()) - } - - // tests: - def entityOfSize(size: Int) = - HttpEntity(ContentTypes.`text/plain(UTF-8)`, "0" * size) - - Post("/abc", entityOfSize(500)) ~> route ~> check { - status shouldEqual StatusCodes.OK - } - - Post("/abc", entityOfSize(501)) ~> route ~> check { - status shouldEqual StatusCodes.OK - } - } - - "withSizeLimit-nested-example" in { - val route = - withSizeLimit(500) { - withSizeLimit(800) { - entity(as[String]) { _ ⇒ - complete(HttpResponse()) - } - } - } - - // tests: - def entityOfSize(size: Int) = - HttpEntity(ContentTypes.`text/plain(UTF-8)`, "0" * size) - Post("/abc", entityOfSize(800)) ~> route ~> check { - status shouldEqual StatusCodes.OK - } - - Post("/abc", entityOfSize(801)) ~> Route.seal(route) ~> check { - status shouldEqual StatusCodes.BadRequest - } - } - - "withoutSizeLimit-example" in { - val route = - withoutSizeLimit { - entity(as[String]) { _ ⇒ - complete(HttpResponse()) - } - } - - // tests: - def entityOfSize(size: Int) = - HttpEntity(ContentTypes.`text/plain(UTF-8)`, "0" * size) - - // will work even if you have configured akka.http.parsing.max-content-length = 500 - Post("/abc", entityOfSize(501)) ~> route ~> check { - status shouldEqual StatusCodes.OK - } - } - -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/ParameterDirectivesExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/ParameterDirectivesExamplesSpec.scala deleted file mode 100755 index aefcff5ebc..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/ParameterDirectivesExamplesSpec.scala +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl.server.directives - -import akka.http.scaladsl.model._ -import akka.http.scaladsl.server.Route -import akka.http.scaladsl.unmarshalling.PredefinedFromStringUnmarshallers -import docs.http.scaladsl.server.RoutingSpec - -class ParameterDirectivesExamplesSpec extends RoutingSpec with PredefinedFromStringUnmarshallers { - "example-1" in { - val route = - parameter('color) { color => - complete(s"The color is '$color'") - } - - // tests: - Get("/?color=blue") ~> route ~> check { - responseAs[String] shouldEqual "The color is 'blue'" - } - - Get("/") ~> Route.seal(route) ~> check { - status shouldEqual StatusCodes.NotFound - responseAs[String] shouldEqual "Request is missing required query parameter 'color'" - } - } - "required-1" in { - val route = - parameters('color, 'backgroundColor) { (color, backgroundColor) => - complete(s"The color is '$color' and the background is '$backgroundColor'") - } - - // tests: - Get("/?color=blue&backgroundColor=red") ~> route ~> check { - responseAs[String] shouldEqual "The color is 'blue' and the background is 'red'" - } - Get("/?color=blue") ~> Route.seal(route) ~> check { - status shouldEqual StatusCodes.NotFound - responseAs[String] shouldEqual "Request is missing required query parameter 'backgroundColor'" - } - } - "optional" in { - val route = - parameters('color, 'backgroundColor.?) { (color, backgroundColor) => - val backgroundStr = backgroundColor.getOrElse("") - complete(s"The color is '$color' and the background is '$backgroundStr'") - } - - // tests: - Get("/?color=blue&backgroundColor=red") ~> route ~> check { - responseAs[String] shouldEqual "The color is 'blue' and the background is 'red'" - } - Get("/?color=blue") ~> route ~> check { - responseAs[String] shouldEqual "The color is 'blue' and the background is ''" - } - } - "optional-with-default" in { - val route = - parameters('color, 'backgroundColor ? "white") { (color, backgroundColor) => - complete(s"The color is '$color' and the background is '$backgroundColor'") - } - - // tests: - Get("/?color=blue&backgroundColor=red") ~> route ~> check { - responseAs[String] shouldEqual "The color is 'blue' and the background is 'red'" - } - Get("/?color=blue") ~> route ~> check { - responseAs[String] shouldEqual "The color is 'blue' and the background is 'white'" - } - } - "required-value" in { - val route = - parameters('color, 'action ! "true") { (color) => - complete(s"The color is '$color'.") - } - - // tests: - Get("/?color=blue&action=true") ~> route ~> check { - responseAs[String] shouldEqual "The color is 'blue'." - } - - Get("/?color=blue&action=false") ~> Route.seal(route) ~> check { - status shouldEqual StatusCodes.NotFound - responseAs[String] shouldEqual "The requested resource could not be found." - } - } - "mapped-value" in { - val route = - parameters('color, 'count.as[Int]) { (color, count) => - complete(s"The color is '$color' and you have $count of it.") - } - - // tests: - Get("/?color=blue&count=42") ~> route ~> check { - responseAs[String] shouldEqual "The color is 'blue' and you have 42 of it." - } - - Get("/?color=blue&count=blub") ~> Route.seal(route) ~> check { - status shouldEqual StatusCodes.BadRequest - responseAs[String] shouldEqual "The query parameter 'count' was malformed:\n'blub' is not a valid 32-bit signed integer value" - } - } - "repeated" in { - val route = - parameters('color, 'city.*) { (color, cities) => - cities.toList match { - case Nil => complete(s"The color is '$color' and there are no cities.") - case city :: Nil => complete(s"The color is '$color' and the city is $city.") - case multiple => complete(s"The color is '$color' and the cities are ${multiple.mkString(", ")}.") - } - } - - // tests: - Get("/?color=blue") ~> route ~> check { - responseAs[String] === "The color is 'blue' and there are no cities." - } - - Get("/?color=blue&city=Chicago") ~> Route.seal(route) ~> check { - responseAs[String] === "The color is 'blue' and the city is Chicago." - } - - Get("/?color=blue&city=Chicago&city=Boston") ~> Route.seal(route) ~> check { - responseAs[String] === "The color is 'blue' and the cities are Chicago, Boston." - } - } - "mapped-repeated" in { - val route = - parameters('color, 'distance.as[Int].*) { (color, cities) => - cities.toList match { - case Nil => complete(s"The color is '$color' and there are no distances.") - case distance :: Nil => complete(s"The color is '$color' and the distance is $distance.") - case multiple => complete(s"The color is '$color' and the distances are ${multiple.mkString(", ")}.") - } - } - - // tests: - Get("/?color=blue") ~> route ~> check { - responseAs[String] === "The color is 'blue' and there are no distances." - } - - Get("/?color=blue&distance=5") ~> Route.seal(route) ~> check { - responseAs[String] === "The color is 'blue' and the distance is 5." - } - - Get("/?color=blue&distance=5&distance=14") ~> Route.seal(route) ~> check { - responseAs[String] === "The color is 'blue' and the distances are 5, 14." - } - } - "parameterMap" in { - val route = - parameterMap { params => - def paramString(param: (String, String)): String = s"""${param._1} = '${param._2}'""" - complete(s"The parameters are ${params.map(paramString).mkString(", ")}") - } - - // tests: - Get("/?color=blue&count=42") ~> route ~> check { - responseAs[String] shouldEqual "The parameters are color = 'blue', count = '42'" - } - Get("/?x=1&x=2") ~> route ~> check { - responseAs[String] shouldEqual "The parameters are x = '2'" - } - } - "parameterMultiMap" in { - val route = - parameterMultiMap { params => - complete(s"There are parameters ${params.map(x => x._1 + " -> " + x._2.size).mkString(", ")}") - } - - // tests: - Get("/?color=blue&count=42") ~> route ~> check { - responseAs[String] shouldEqual "There are parameters color -> 1, count -> 1" - } - Get("/?x=23&x=42") ~> route ~> check { - responseAs[String] shouldEqual "There are parameters x -> 2" - } - } - "parameterSeq" in { - val route = - parameterSeq { params => - def paramString(param: (String, String)): String = s"""${param._1} = '${param._2}'""" - complete(s"The parameters are ${params.map(paramString).mkString(", ")}") - } - - // tests: - Get("/?color=blue&count=42") ~> route ~> check { - responseAs[String] shouldEqual "The parameters are color = 'blue', count = '42'" - } - Get("/?x=1&x=2") ~> route ~> check { - responseAs[String] shouldEqual "The parameters are x = '1', x = '2'" - } - } - "csv" in { - val route = - parameter("names".as(CsvSeq[String])) { names => - complete(s"The parameters are ${names.mkString(", ")}") - } - - // tests: - Get("/?names=") ~> route ~> check { - responseAs[String] shouldEqual "The parameters are " - } - Get("/?names=Caplin") ~> route ~> check { - responseAs[String] shouldEqual "The parameters are Caplin" - } - Get("/?names=Caplin,John") ~> route ~> check { - responseAs[String] shouldEqual "The parameters are Caplin, John" - } - } -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/PathDirectivesExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/PathDirectivesExamplesSpec.scala deleted file mode 100644 index 9a482c8d1f..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/PathDirectivesExamplesSpec.scala +++ /dev/null @@ -1,379 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl.server.directives - -import akka.http.scaladsl.server._ -import docs.http.scaladsl.server.RoutingSpec - -class PathDirectivesExamplesSpec extends RoutingSpec { - - //# path-matcher - val matcher: PathMatcher1[Option[Int]] = - "foo" / "bar" / "X" ~ IntNumber.? / ("edit" | "create") - //# - - //# path-dsl - // matches /foo/ - path("foo"./) - - // matches e.g. /foo/123 and extracts "123" as a String - path("foo" / """\d+""".r) - - // matches e.g. /foo/bar123 and extracts "123" as a String - path("foo" / """bar(\d+)""".r) - - // similar to `path(Segments)` - path(Segment.repeat(10, separator = Slash)) - - // matches e.g. /i42 or /hCAFE and extracts an Int - path("i" ~ IntNumber | "h" ~ HexIntNumber) - - // identical to path("foo" ~ (PathEnd | Slash)) - path("foo" ~ Slash.?) - - // matches /red or /green or /blue and extracts 1, 2 or 3 respectively - path(Map("red" -> 1, "green" -> 2, "blue" -> 3)) - - // matches anything starting with "/foo" except for /foobar - pathPrefix("foo" ~ !"bar") - //# - - //# pathPrefixTest-, rawPathPrefix-, rawPathPrefixTest-, pathSuffix-, pathSuffixTest- - val completeWithUnmatchedPath = - extractUnmatchedPath { p => - complete(p.toString) - } - - //# - - "path-example" in { - val route = - path("foo") { - complete("/foo") - } ~ - path("foo" / "bar") { - complete("/foo/bar") - } ~ - pathPrefix("ball") { - pathEnd { - complete("/ball") - } ~ - path(IntNumber) { int => - complete(if (int % 2 == 0) "even ball" else "odd ball") - } - } - - // tests: - Get("/") ~> route ~> check { - handled shouldEqual false - } - - Get("/foo") ~> route ~> check { - responseAs[String] shouldEqual "/foo" - } - - Get("/foo/bar") ~> route ~> check { - responseAs[String] shouldEqual "/foo/bar" - } - - Get("/ball/1337") ~> route ~> check { - responseAs[String] shouldEqual "odd ball" - } - } - - "pathEnd-" in { - val route = - pathPrefix("foo") { - pathEnd { - complete("/foo") - } ~ - path("bar") { - complete("/foo/bar") - } - } - - // tests: - Get("/foo") ~> route ~> check { - responseAs[String] shouldEqual "/foo" - } - - Get("/foo/") ~> route ~> check { - handled shouldEqual false - } - - Get("/foo/bar") ~> route ~> check { - responseAs[String] shouldEqual "/foo/bar" - } - } - - "pathEndOrSingleSlash-" in { - val route = - pathPrefix("foo") { - pathEndOrSingleSlash { - complete("/foo") - } ~ - path("bar") { - complete("/foo/bar") - } - } - - // tests: - Get("/foo") ~> route ~> check { - responseAs[String] shouldEqual "/foo" - } - - Get("/foo/") ~> route ~> check { - responseAs[String] shouldEqual "/foo" - } - - Get("/foo/bar") ~> route ~> check { - responseAs[String] shouldEqual "/foo/bar" - } - } - - "pathPrefix-" in { - val route = - pathPrefix("ball") { - pathEnd { - complete("/ball") - } ~ - path(IntNumber) { int => - complete(if (int % 2 == 0) "even ball" else "odd ball") - } - } - - // tests: - Get("/") ~> route ~> check { - handled shouldEqual false - } - - Get("/ball") ~> route ~> check { - responseAs[String] shouldEqual "/ball" - } - - Get("/ball/1337") ~> route ~> check { - responseAs[String] shouldEqual "odd ball" - } - } - - "pathPrefixTest-" in { - val route = - pathPrefixTest("foo" | "bar") { - pathPrefix("foo") { completeWithUnmatchedPath } ~ - pathPrefix("bar") { completeWithUnmatchedPath } - } - - // tests: - Get("/foo/doo") ~> route ~> check { - responseAs[String] shouldEqual "/doo" - } - - Get("/bar/yes") ~> route ~> check { - responseAs[String] shouldEqual "/yes" - } - } - - "pathSingleSlash-" in { - val route = - pathSingleSlash { - complete("root") - } ~ - pathPrefix("ball") { - pathSingleSlash { - complete("/ball/") - } ~ - path(IntNumber) { int => - complete(if (int % 2 == 0) "even ball" else "odd ball") - } - } - - // tests: - Get("/") ~> route ~> check { - responseAs[String] shouldEqual "root" - } - - Get("/ball") ~> route ~> check { - handled shouldEqual false - } - - Get("/ball/") ~> route ~> check { - responseAs[String] shouldEqual "/ball/" - } - - Get("/ball/1337") ~> route ~> check { - responseAs[String] shouldEqual "odd ball" - } - } - - "pathSuffix-" in { - val route = - pathPrefix("start") { - pathSuffix("end") { - completeWithUnmatchedPath - } ~ - pathSuffix("foo" / "bar" ~ "baz") { - completeWithUnmatchedPath - } - } - - // tests: - Get("/start/middle/end") ~> route ~> check { - responseAs[String] shouldEqual "/middle/" - } - - Get("/start/something/barbaz/foo") ~> route ~> check { - responseAs[String] shouldEqual "/something/" - } - } - - "pathSuffixTest-" in { - val route = - pathSuffixTest(Slash) { - complete("slashed") - } ~ - complete("unslashed") - - // tests: - Get("/foo/") ~> route ~> check { - responseAs[String] shouldEqual "slashed" - } - Get("/foo") ~> route ~> check { - responseAs[String] shouldEqual "unslashed" - } - } - - "rawPathPrefix-" in { - val route = - pathPrefix("foo") { - rawPathPrefix("bar") { completeWithUnmatchedPath } ~ - rawPathPrefix("doo") { completeWithUnmatchedPath } - } - - // tests: - Get("/foobar/baz") ~> route ~> check { - responseAs[String] shouldEqual "/baz" - } - - Get("/foodoo/baz") ~> route ~> check { - responseAs[String] shouldEqual "/baz" - } - } - - "rawPathPrefixTest-" in { - val route = - pathPrefix("foo") { - rawPathPrefixTest("bar") { - completeWithUnmatchedPath - } - } - - // tests: - Get("/foobar") ~> route ~> check { - responseAs[String] shouldEqual "bar" - } - - Get("/foobaz") ~> route ~> check { - handled shouldEqual false - } - } - - "redirectToTrailingSlashIfMissing-0" in { - import akka.http.scaladsl.model.StatusCodes - - val route = - redirectToTrailingSlashIfMissing(StatusCodes.MovedPermanently) { - path("foo"./) { - // We require the explicit trailing slash in the path - complete("OK") - } ~ - path("bad-1") { - // MISTAKE! - // Missing `/` in path, causes this path to never match, - // because it is inside a `redirectToTrailingSlashIfMissing` - ??? - } ~ - path("bad-2/") { - // MISTAKE! - // / should be explicit as path element separator and not *in* the path element - // So it should be: "bad-1" / - ??? - } - } - - // tests: - // Redirected: - Get("/foo") ~> route ~> check { - status shouldEqual StatusCodes.MovedPermanently - - // results in nice human readable message, - // in case the redirect can't be followed automatically: - responseAs[String] shouldEqual { - "This and all future requests should be directed to " + - "this URI." - } - } - - // Properly handled: - Get("/foo/") ~> route ~> check { - status shouldEqual StatusCodes.OK - responseAs[String] shouldEqual "OK" - } - - // MISTAKE! will never match - reason explained in routes - Get("/bad-1/") ~> route ~> check { - handled shouldEqual false - } - - // MISTAKE! will never match - reason explained in routes - Get("/bad-2/") ~> route ~> check { - handled shouldEqual false - } - } - - "redirectToNoTrailingSlashIfPresent-0" in { - import akka.http.scaladsl.model.StatusCodes - - val route = - redirectToNoTrailingSlashIfPresent(StatusCodes.MovedPermanently) { - path("foo") { - // We require the explicit trailing slash in the path - complete("OK") - } ~ - path("bad"./) { - // 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. - ??? - } - } - - // tests: - // Redirected: - Get("/foo/") ~> route ~> check { - status shouldEqual StatusCodes.MovedPermanently - - // results in nice human readable message, - // in case the redirect can't be followed automatically: - responseAs[String] shouldEqual { - "This and all future requests should be directed to " + - "this URI." - } - } - - // Properly handled: - Get("/foo") ~> route ~> check { - status shouldEqual StatusCodes.OK - responseAs[String] shouldEqual "OK" - } - - // MISTAKE! will never match - reason explained in routes - Get("/bad") ~> route ~> check { - handled shouldEqual false - } - } -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/RangeDirectivesExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/RangeDirectivesExamplesSpec.scala deleted file mode 100644 index 9922e36240..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/RangeDirectivesExamplesSpec.scala +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl.server.directives - -import akka.http.scaladsl.model._ -import com.typesafe.config.{ ConfigFactory, Config } -import akka.util.ByteString - -import headers._ -import scala.concurrent.Await -import scala.concurrent.duration._ -import docs.http.scaladsl.server.RoutingSpec - -class RangeDirectivesExamplesSpec extends RoutingSpec { - - override def testConfig: Config = - ConfigFactory.parseString("akka.http.routing.range-coalescing-threshold=2").withFallback(super.testConfig) - - "withRangeSupport" in { - val route = - withRangeSupport { - complete("ABCDEFGH") - } - - Get() ~> addHeader(Range(ByteRange(3, 4))) ~> route ~> check { - headers should contain(`Content-Range`(ContentRange(3, 4, 8))) - status shouldEqual StatusCodes.PartialContent - responseAs[String] shouldEqual "DE" - } - - // we set "akka.http.routing.range-coalescing-threshold = 2" - // above to make sure we get two BodyParts - Get() ~> addHeader(Range(ByteRange(0, 1), ByteRange(1, 2), ByteRange(6, 7))) ~> route ~> check { - headers.collectFirst { case `Content-Range`(_, _) => true } shouldBe None - val responseF = responseAs[Multipart.ByteRanges].parts - .runFold[List[Multipart.ByteRanges.BodyPart]](Nil)((acc, curr) => curr :: acc) - - val response = Await.result(responseF, 3.seconds).reverse - - response should have length 2 - - val part1 = response(0) - part1.contentRange === ContentRange(0, 2, 8) - part1.entity should matchPattern { - case HttpEntity.Strict(_, bytes) if bytes.utf8String == "ABC" => - } - - val part2 = response(1) - part2.contentRange === ContentRange(6, 7, 8) - part2.entity should matchPattern { - case HttpEntity.Strict(_, bytes) if bytes.utf8String == "GH" => - } - } - } - -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/RespondWithDirectivesExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/RespondWithDirectivesExamplesSpec.scala deleted file mode 100644 index 0b1c538321..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/RespondWithDirectivesExamplesSpec.scala +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl.server.directives - -import akka.http.scaladsl.model.headers._ -import docs.http.scaladsl.server.RoutingSpec - -class RespondWithDirectivesExamplesSpec extends RoutingSpec { - - "respondWithHeader-0" in { - val route = - path("foo") { - respondWithHeader(RawHeader("Funky-Muppet", "gonzo")) { - complete("beep") - } - } - - // tests: - Get("/foo") ~> route ~> check { - header("Funky-Muppet") shouldEqual Some(RawHeader("Funky-Muppet", "gonzo")) - responseAs[String] shouldEqual "beep" - } - } - - "respondWithDefaultHeader-0" in { - // custom headers - val blippy = RawHeader("X-Fish-Name", "Blippy") - val elTonno = RawHeader("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 - val route = - respondWithDefaultHeader(blippy) { // blippy - respondWithHeader(elTonno) { // / el tonno - path("el-tonno") { // | / - complete("¡Ay blippy!") // | |- el tonno - } ~ // | | - path("los-tonnos") { // | | - complete("¡Ay ay blippy!") // | |- el tonno - } // | | - } ~ // | x - complete("Blip!") // |- blippy - } // x - // format: ON - - // tests: - Get("/") ~> route ~> check { - header("X-Fish-Name") shouldEqual Some(RawHeader("X-Fish-Name", "Blippy")) - responseAs[String] shouldEqual "Blip!" - } - - Get("/el-tonno") ~> route ~> check { - header("X-Fish-Name") shouldEqual Some(RawHeader("X-Fish-Name", "El Tonno")) - responseAs[String] shouldEqual "¡Ay blippy!" - } - - Get("/los-tonnos") ~> route ~> check { - header("X-Fish-Name") shouldEqual Some(RawHeader("X-Fish-Name", "El Tonno")) - responseAs[String] shouldEqual "¡Ay ay blippy!" - } - } - // format: ON - - "respondWithHeaders-0" in { - val route = - path("foo") { - respondWithHeaders(RawHeader("Funky-Muppet", "gonzo"), Origin(HttpOrigin("http://akka.io"))) { - complete("beep") - } - } - - // tests: - Get("/foo") ~> route ~> check { - header("Funky-Muppet") shouldEqual Some(RawHeader("Funky-Muppet", "gonzo")) - header[Origin] shouldEqual Some(Origin(HttpOrigin("http://akka.io"))) - responseAs[String] shouldEqual "beep" - } - } - -} \ No newline at end of file diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/RouteDirectivesExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/RouteDirectivesExamplesSpec.scala deleted file mode 100644 index df10fd21c5..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/RouteDirectivesExamplesSpec.scala +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl.server.directives - -import akka.http.scaladsl.model.ContentTypes._ -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers.`Content-Type` -import akka.http.scaladsl.server.{ Route, ValidationRejection } -import akka.testkit.EventFilter -import docs.http.scaladsl.server.RoutingSpec - -class RouteDirectivesExamplesSpec extends RoutingSpec { - - "complete-examples" in { - val route = - path("a") { - complete(HttpResponse(entity = "foo")) - } ~ - path("b") { - complete(StatusCodes.OK) - } ~ - path("c") { - complete(StatusCodes.Created -> "bar") - } ~ - path("d") { - complete(201 -> "bar") - } ~ - path("e") { - complete(StatusCodes.Created, List(`Content-Type`(`text/plain(UTF-8)`)), "bar") - } ~ - path("f") { - complete(201, List(`Content-Type`(`text/plain(UTF-8)`)), "bar") - } ~ - (path("g") & complete("baz")) // `&` also works with `complete` as the 2nd argument - - // tests: - Get("/a") ~> route ~> check { - status shouldEqual StatusCodes.OK - responseAs[String] shouldEqual "foo" - } - - Get("/b") ~> route ~> check { - status shouldEqual StatusCodes.OK - responseAs[String] shouldEqual "OK" - } - - Get("/c") ~> route ~> check { - status shouldEqual StatusCodes.Created - responseAs[String] shouldEqual "bar" - } - - Get("/d") ~> route ~> check { - status shouldEqual StatusCodes.Created - responseAs[String] shouldEqual "bar" - } - - Get("/e") ~> route ~> check { - status shouldEqual StatusCodes.Created - header[`Content-Type`] shouldEqual Some(`Content-Type`(`text/plain(UTF-8)`)) - responseAs[String] shouldEqual "bar" - } - - Get("/f") ~> route ~> check { - status shouldEqual StatusCodes.Created - header[`Content-Type`] shouldEqual Some(`Content-Type`(`text/plain(UTF-8)`)) - responseAs[String] shouldEqual "bar" - } - - Get("/g") ~> route ~> check { - status shouldEqual StatusCodes.OK - responseAs[String] shouldEqual "baz" - } - } - - "reject-examples" in { - val route = - path("a") { - reject // don't handle here, continue on - } ~ - path("a") { - complete("foo") - } ~ - path("b") { - // trigger a ValidationRejection explicitly - // rather than through the `validate` directive - reject(ValidationRejection("Restricted!")) - } - - // tests: - Get("/a") ~> route ~> check { - responseAs[String] shouldEqual "foo" - } - - Get("/b") ~> route ~> check { - rejection shouldEqual ValidationRejection("Restricted!") - } - } - - "redirect-examples" in { - val route = - pathPrefix("foo") { - pathSingleSlash { - complete("yes") - } ~ - pathEnd { - redirect("/foo/", StatusCodes.PermanentRedirect) - } - } - - // tests: - Get("/foo/") ~> route ~> check { - responseAs[String] shouldEqual "yes" - } - - Get("/foo") ~> route ~> check { - status shouldEqual StatusCodes.PermanentRedirect - responseAs[String] shouldEqual """The request, and all future requests should be repeated using this URI.""" - } - } - - "failwith-examples" in EventFilter[RuntimeException](start = "Error during processing of request", occurrences = 1).intercept { - val route = - path("foo") { - failWith(new RuntimeException("Oops.")) - } - - // tests: - Get("/foo") ~> Route.seal(route) ~> check { - status shouldEqual StatusCodes.InternalServerError - responseAs[String] shouldEqual "There was an internal server error." - } - } - -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/SchemeDirectivesExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/SchemeDirectivesExamplesSpec.scala deleted file mode 100644 index 9d495a1b96..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/SchemeDirectivesExamplesSpec.scala +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl.server.directives -import docs.http.scaladsl.server.RoutingSpec - -class SchemeDirectivesExamplesSpec extends RoutingSpec { - "example-1" in { - val route = - extractScheme { scheme => - complete(s"The scheme is '${scheme}'") - } - - // tests: - Get("https://www.example.com/") ~> route ~> check { - responseAs[String] shouldEqual "The scheme is 'https'" - } - } - - "example-2" in { - import akka.http.scaladsl.model._ - import akka.http.scaladsl.model.headers.Location - import StatusCodes.MovedPermanently - - val route = - scheme("http") { - extract(_.request.uri) { uri => - redirect(uri.copy(scheme = "https"), MovedPermanently) - } - } ~ - scheme("https") { - complete(s"Safe and secure!") - } - - // tests: - Get("http://www.example.com/hello") ~> route ~> check { - status shouldEqual MovedPermanently - header[Location] shouldEqual Some(Location(Uri("https://www.example.com/hello"))) - } - - Get("https://www.example.com/hello") ~> route ~> check { - responseAs[String] shouldEqual "Safe and secure!" - } - } -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/SecurityDirectivesExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/SecurityDirectivesExamplesSpec.scala deleted file mode 100644 index 4cc8d7ee77..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/SecurityDirectivesExamplesSpec.scala +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl.server.directives - -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers._ -import akka.http.scaladsl.server.Route -import akka.http.scaladsl.server.directives.Credentials - -import scala.concurrent.Future -import docs.http.scaladsl.server.RoutingSpec - -class SecurityDirectivesExamplesSpec extends RoutingSpec { - - "authenticateBasic-0" in { - def myUserPassAuthenticator(credentials: Credentials): Option[String] = - credentials match { - case p @ Credentials.Provided(id) if p.verify("p4ssw0rd") => Some(id) - case _ => None - } - - val route = - Route.seal { - path("secured") { - authenticateBasic(realm = "secure site", myUserPassAuthenticator) { userName => - complete(s"The user is '$userName'") - } - } - } - - // tests: - Get("/secured") ~> route ~> check { - status shouldEqual StatusCodes.Unauthorized - responseAs[String] shouldEqual "The resource requires authentication, which was not supplied with the request" - header[`WWW-Authenticate`].get.challenges.head shouldEqual HttpChallenge("Basic", Some("secure site")) - } - - val validCredentials = BasicHttpCredentials("John", "p4ssw0rd") - Get("/secured") ~> addCredentials(validCredentials) ~> // adds Authorization header - route ~> check { - responseAs[String] shouldEqual "The user is 'John'" - } - - val invalidCredentials = BasicHttpCredentials("Peter", "pan") - Get("/secured") ~> - addCredentials(invalidCredentials) ~> // adds Authorization header - route ~> check { - status shouldEqual StatusCodes.Unauthorized - responseAs[String] shouldEqual "The supplied authentication is invalid" - header[`WWW-Authenticate`].get.challenges.head shouldEqual HttpChallenge("Basic", Some("secure site")) - } - } - "authenticateBasicPF-0" in { - val myUserPassAuthenticator: AuthenticatorPF[String] = { - case p @ Credentials.Provided(id) if p.verify("p4ssw0rd") => id - case p @ Credentials.Provided(id) if p.verify("p4ssw0rd-special") => s"$id-admin" - } - - val route = - Route.seal { - path("secured") { - authenticateBasicPF(realm = "secure site", myUserPassAuthenticator) { userName => - complete(s"The user is '$userName'") - } - } - } - - // tests: - Get("/secured") ~> route ~> check { - status shouldEqual StatusCodes.Unauthorized - responseAs[String] shouldEqual "The resource requires authentication, which was not supplied with the request" - header[`WWW-Authenticate`].get.challenges.head shouldEqual HttpChallenge("Basic", Some("secure site")) - } - - val validCredentials = BasicHttpCredentials("John", "p4ssw0rd") - Get("/secured") ~> addCredentials(validCredentials) ~> // adds Authorization header - route ~> check { - responseAs[String] shouldEqual "The user is 'John'" - } - - val validAdminCredentials = BasicHttpCredentials("John", "p4ssw0rd-special") - Get("/secured") ~> addCredentials(validAdminCredentials) ~> // adds Authorization header - route ~> check { - responseAs[String] shouldEqual "The user is 'John-admin'" - } - - val invalidCredentials = BasicHttpCredentials("Peter", "pan") - Get("/secured") ~> - addCredentials(invalidCredentials) ~> // adds Authorization header - route ~> check { - status shouldEqual StatusCodes.Unauthorized - responseAs[String] shouldEqual "The supplied authentication is invalid" - header[`WWW-Authenticate`].get.challenges.head shouldEqual HttpChallenge("Basic", Some("secure site")) - } - } - "authenticateBasicPFAsync-0" in { - case class User(id: String) - def fetchUser(id: String): Future[User] = { - // some fancy logic to obtain a User - Future.successful(User(id)) - } - - val myUserPassAuthenticator: AsyncAuthenticatorPF[User] = { - case p @ Credentials.Provided(id) if p.verify("p4ssw0rd") => - fetchUser(id) - } - - val route = - Route.seal { - path("secured") { - authenticateBasicPFAsync(realm = "secure site", myUserPassAuthenticator) { user => - complete(s"The user is '${user.id}'") - } - } - } - - // tests: - Get("/secured") ~> route ~> check { - status shouldEqual StatusCodes.Unauthorized - responseAs[String] shouldEqual "The resource requires authentication, which was not supplied with the request" - header[`WWW-Authenticate`].get.challenges.head shouldEqual HttpChallenge("Basic", Some("secure site")) - } - - val validCredentials = BasicHttpCredentials("John", "p4ssw0rd") - Get("/secured") ~> addCredentials(validCredentials) ~> // adds Authorization header - route ~> check { - responseAs[String] shouldEqual "The user is 'John'" - } - - val invalidCredentials = BasicHttpCredentials("Peter", "pan") - Get("/secured") ~> - addCredentials(invalidCredentials) ~> // adds Authorization header - route ~> check { - status shouldEqual StatusCodes.Unauthorized - responseAs[String] shouldEqual "The supplied authentication is invalid" - header[`WWW-Authenticate`].get.challenges.head shouldEqual HttpChallenge("Basic", Some("secure site")) - } - } - "authenticateBasicAsync-0" in { - def myUserPassAuthenticator(credentials: Credentials): Future[Option[String]] = - credentials match { - case p @ Credentials.Provided(id) => - Future { - // potentially - if (p.verify("p4ssw0rd")) Some(id) - else None - } - case _ => Future.successful(None) - } - - val route = - Route.seal { - path("secured") { - authenticateBasicAsync(realm = "secure site", myUserPassAuthenticator) { userName => - complete(s"The user is '$userName'") - } - } - } - - // tests: - Get("/secured") ~> route ~> check { - status shouldEqual StatusCodes.Unauthorized - responseAs[String] shouldEqual "The resource requires authentication, which was not supplied with the request" - header[`WWW-Authenticate`].get.challenges.head shouldEqual HttpChallenge("Basic", Some("secure site")) - } - - val validCredentials = BasicHttpCredentials("John", "p4ssw0rd") - Get("/secured") ~> addCredentials(validCredentials) ~> // adds Authorization header - route ~> check { - responseAs[String] shouldEqual "The user is 'John'" - } - - val invalidCredentials = BasicHttpCredentials("Peter", "pan") - Get("/secured") ~> - addCredentials(invalidCredentials) ~> // adds Authorization header - route ~> check { - status shouldEqual StatusCodes.Unauthorized - responseAs[String] shouldEqual "The supplied authentication is invalid" - header[`WWW-Authenticate`].get.challenges.head shouldEqual HttpChallenge("Basic", Some("secure site")) - } - } - "authenticateOrRejectWithChallenge-0" in { - val challenge = HttpChallenge("MyAuth", Some("MyRealm")) - - // your custom authentication logic: - def auth(creds: HttpCredentials): Boolean = true - - def myUserPassAuthenticator(credentials: Option[HttpCredentials]): Future[AuthenticationResult[String]] = - Future { - credentials match { - case Some(creds) if auth(creds) => Right("some-user-name-from-creds") - case _ => Left(challenge) - } - } - - val route = - Route.seal { - path("secured") { - authenticateOrRejectWithChallenge(myUserPassAuthenticator _) { userName => - complete("Authenticated!") - } - } - } - - // tests: - Get("/secured") ~> route ~> check { - status shouldEqual StatusCodes.Unauthorized - responseAs[String] shouldEqual "The resource requires authentication, which was not supplied with the request" - header[`WWW-Authenticate`].get.challenges.head shouldEqual HttpChallenge("MyAuth", Some("MyRealm")) - } - - val validCredentials = BasicHttpCredentials("John", "p4ssw0rd") - Get("/secured") ~> addCredentials(validCredentials) ~> // adds Authorization header - route ~> check { - status shouldEqual StatusCodes.OK - responseAs[String] shouldEqual "Authenticated!" - } - } - - "0authorize-0" in { - case class User(name: String) - - // authenticate the user: - def myUserPassAuthenticator(credentials: Credentials): Option[User] = - credentials match { - case Credentials.Provided(id) => Some(User(id)) - case _ => None - } - - // check if user is authorized to perform admin actions: - val admins = Set("Peter") - def hasAdminPermissions(user: User): Boolean = - admins.contains(user.name) - - val route = - Route.seal { - authenticateBasic(realm = "secure site", myUserPassAuthenticator) { user => - path("peters-lair") { - authorize(hasAdminPermissions(user)) { - complete(s"'${user.name}' visited Peter's lair") - } - } - } - } - - // tests: - val johnsCred = BasicHttpCredentials("John", "p4ssw0rd") - Get("/peters-lair") ~> addCredentials(johnsCred) ~> // adds Authorization header - route ~> check { - status shouldEqual StatusCodes.Forbidden - responseAs[String] shouldEqual "The supplied authentication is not authorized to access this resource" - } - - val petersCred = BasicHttpCredentials("Peter", "pan") - Get("/peters-lair") ~> addCredentials(petersCred) ~> // adds Authorization header - route ~> check { - responseAs[String] shouldEqual "'Peter' visited Peter's lair" - } - } - - "0authorizeAsync" in { - case class User(name: String) - - // authenticate the user: - def myUserPassAuthenticator(credentials: Credentials): Option[User] = - credentials match { - case Credentials.Provided(id) => Some(User(id)) - case _ => None - } - - // check if user is authorized to perform admin actions, - // this could potentially be a long operation so it would return a Future - val admins = Set("Peter") - def hasAdminPermissions(user: User): Future[Boolean] = - Future.successful(admins.contains(user.name)) - - val route = - Route.seal { - authenticateBasic(realm = "secure site", myUserPassAuthenticator) { user => - path("peters-lair") { - authorizeAsync(_ => hasAdminPermissions(user)) { - complete(s"'${user.name}' visited Peter's lair") - } - } - } - } - - // tests: - val johnsCred = BasicHttpCredentials("John", "p4ssw0rd") - Get("/peters-lair") ~> addCredentials(johnsCred) ~> // adds Authorization header - route ~> check { - status shouldEqual StatusCodes.Forbidden - responseAs[String] shouldEqual "The supplied authentication is not authorized to access this resource" - } - - val petersCred = BasicHttpCredentials("Peter", "pan") - Get("/peters-lair") ~> addCredentials(petersCred) ~> // adds Authorization header - route ~> check { - responseAs[String] shouldEqual "'Peter' visited Peter's lair" - } - } - - "0extractCredentials" in { - val route = - extractCredentials { creds => - complete { - creds match { - case Some(c) => "Credentials: " + c - case _ => "No credentials" - } - } - } - - // tests: - val johnsCred = BasicHttpCredentials("John", "p4ssw0rd") - Get("/") ~> addCredentials(johnsCred) ~> // adds Authorization header - route ~> check { - responseAs[String] shouldEqual "Credentials: Basic Sm9objpwNHNzdzByZA==" - } - - Get("/") ~> route ~> check { - responseAs[String] shouldEqual "No credentials" - } - } -} - diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/TimeoutDirectivesExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/TimeoutDirectivesExamplesSpec.scala deleted file mode 100644 index 191215dac7..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/TimeoutDirectivesExamplesSpec.scala +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl.server.directives - -import akka.http.scaladsl.model.{ HttpResponse, StatusCodes } -import akka.http.scaladsl.server.Route -import docs.CompileOnlySpec -import akka.http.scaladsl.{ Http, TestUtils } -import akka.http.scaladsl.server.Directives._ -import akka.stream.ActorMaterializer -import akka.http.scaladsl.model.HttpEntity._ -import akka.http.scaladsl.model._ -import com.typesafe.config.{ Config, ConfigFactory } -import org.scalatest.concurrent.ScalaFutures -import scala.concurrent.duration._ -import scala.concurrent.{ Future, Promise } -import akka.testkit.AkkaSpec - -private[this] object TimeoutDirectivesInfiniteTimeoutTestConfig { - val testConf: Config = ConfigFactory.parseString(""" - akka.loggers = ["akka.testkit.TestEventListener"] - akka.loglevel = ERROR - akka.stdout-loglevel = ERROR - windows-connection-abort-workaround-enabled = auto - akka.log-dead-letters = OFF - akka.http.server.request-timeout = infinite""") -} - -class TimeoutDirectivesExamplesSpec extends AkkaSpec(TimeoutDirectivesInfiniteTimeoutTestConfig.testConf) - with ScalaFutures with CompileOnlySpec { - //#testSetup - import system.dispatcher - implicit val materializer = ActorMaterializer() - - def slowFuture(): Future[String] = Promise[String].future // TODO: move to Future.never in Scala 2.12 - - def runRoute(route: Route, routePath: String): HttpResponse = { - val (_, hostname, port) = TestUtils.temporaryServerHostnameAndPort() - val binding = Http().bindAndHandle(route, hostname, port) - - val response = Http().singleRequest(HttpRequest(uri = s"http://$hostname:$port/$routePath")).futureValue - - binding.flatMap(_.unbind()).futureValue - - response - } - - //# - - // demonstrates that timeout is correctly set despite infinite initial value of akka.http.server.request-timeout - "Request Timeout" should { - "be configurable in routing layer despite infinite initial value of request-timeout" in { - //#withRequestTimeout-plain - val route = - path("timeout") { - withRequestTimeout(1.seconds) { // modifies the global akka.http.server.request-timeout for this request - val response: Future[String] = slowFuture() // very slow - complete(response) - } - } - - // check - runRoute(route, "timeout").status should ===(StatusCodes.ServiceUnavailable) // the timeout response - //# - } - "without timeout" in compileOnlySpec { - //#withoutRequestTimeout-1 - val route = - path("timeout") { - withoutRequestTimeout { - val response: Future[String] = slowFuture() // very slow - complete(response) - } - } - - // no check as there is no time-out, the future would time out failing the test - //# - } - - "allow mapping the response while setting the timeout" in { - //#withRequestTimeout-with-handler - val timeoutResponse = HttpResponse( - StatusCodes.EnhanceYourCalm, - entity = "Unable to serve response within time limit, please enhance your calm.") - - val route = - path("timeout") { - // updates timeout and handler at - withRequestTimeout(1.milli, request => timeoutResponse) { - val response: Future[String] = slowFuture() // very slow - complete(response) - } - } - - // check - runRoute(route, "timeout").status should ===(StatusCodes.EnhanceYourCalm) // the timeout response - //# - } - - // make it compile only to avoid flaking in slow builds - "allow mapping the response" in compileOnlySpec { - //#withRequestTimeoutResponse - val timeoutResponse = HttpResponse( - StatusCodes.EnhanceYourCalm, - entity = "Unable to serve response within time limit, please enhance your calm.") - - val route = - path("timeout") { - withRequestTimeout(100.milli) { // racy! for a very short timeout like 1.milli you can still get 503 - withRequestTimeoutResponse(request => timeoutResponse) { - val response: Future[String] = slowFuture() // very slow - complete(response) - } - } - } - - // check - runRoute(route, "timeout").status should ===(StatusCodes.EnhanceYourCalm) // the timeout response - //# - } - } - -} - -private[this] object TimeoutDirectivesFiniteTimeoutTestConfig { - val testConf: Config = ConfigFactory.parseString(""" - akka.loggers = ["akka.testkit.TestEventListener"] - akka.loglevel = ERROR - akka.stdout-loglevel = ERROR - windows-connection-abort-workaround-enabled = auto - akka.log-dead-letters = OFF - akka.http.server.request-timeout = 1000s""") -} - -class TimeoutDirectivesFiniteTimeoutExamplesSpec extends AkkaSpec(TimeoutDirectivesFiniteTimeoutTestConfig.testConf) - with ScalaFutures with CompileOnlySpec { - import system.dispatcher - implicit val materializer = ActorMaterializer() - - def slowFuture(): Future[String] = Promise[String].future // TODO: move to Future.never in Scala 2.12 - - def runRoute(route: Route, routePath: String): HttpResponse = { - val (_, hostname, port) = TestUtils.temporaryServerHostnameAndPort() - val binding = Http().bindAndHandle(route, hostname, port) - - val response = Http().singleRequest(HttpRequest(uri = s"http://$hostname:$port/$routePath")).futureValue - - binding.flatMap(_.unbind()).futureValue - - response - } - - // demonstrates that timeout is correctly modified for finite initial values of akka.http.server.request-timeout - "Request Timeout" should { - "be configurable in routing layer for finite initial value of request-timeout" in { - val route = - path("timeout") { - withRequestTimeout(1.seconds) { // modifies the global akka.http.server.request-timeout for this request - val response: Future[String] = slowFuture() // very slow - complete(response) - } - } - runRoute(route, "timeout").status should ===(StatusCodes.ServiceUnavailable) // the timeout response - } - } - -} diff --git a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/WebSocketDirectivesExamplesSpec.scala b/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/WebSocketDirectivesExamplesSpec.scala deleted file mode 100644 index 3f8aabdc54..0000000000 --- a/akka-docs/rst/scala/code/docs/http/scaladsl/server/directives/WebSocketDirectivesExamplesSpec.scala +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.scaladsl.server.directives - -import scala.concurrent.duration._ - -import akka.util.ByteString - -import akka.stream.OverflowStrategy -import akka.stream.scaladsl.{ Sink, Source, Flow } - -import docs.http.scaladsl.server.RoutingSpec -import akka.http.scaladsl.model.ws.{ TextMessage, Message, BinaryMessage } -import akka.http.scaladsl.testkit.WSProbe - -class WebSocketDirectivesExamplesSpec extends RoutingSpec { - "greeter-service" in { - def greeter: Flow[Message, Message, Any] = - Flow[Message].mapConcat { - case tm: TextMessage => - TextMessage(Source.single("Hello ") ++ tm.textStream ++ Source.single("!")) :: Nil - case bm: BinaryMessage => - // ignore binary messages but drain content to avoid the stream being clogged - bm.dataStream.runWith(Sink.ignore) - Nil - } - val websocketRoute = - path("greeter") { - handleWebSocketMessages(greeter) - } - - // tests: - // create a testing probe representing the client-side - val wsClient = WSProbe() - - // WS creates a WebSocket request for testing - WS("/greeter", wsClient.flow) ~> websocketRoute ~> - check { - // check response for WS Upgrade headers - isWebSocketUpgrade shouldEqual true - - // manually run a WS conversation - wsClient.sendMessage("Peter") - wsClient.expectMessage("Hello Peter!") - - wsClient.sendMessage(BinaryMessage(ByteString("abcdef"))) - wsClient.expectNoMessage(100.millis) - - wsClient.sendMessage("John") - wsClient.expectMessage("Hello John!") - - wsClient.sendCompletion() - wsClient.expectCompletion() - } - } - - "handle-multiple-protocols" in { - def greeterService: Flow[Message, Message, Any] = - Flow[Message].mapConcat { - case tm: TextMessage => - TextMessage(Source.single("Hello ") ++ tm.textStream ++ Source.single("!")) :: Nil - case bm: BinaryMessage => - // ignore binary messages but drain content to avoid the stream being clogged - bm.dataStream.runWith(Sink.ignore) - Nil - } - - def echoService: Flow[Message, Message, Any] = - Flow[Message] - // needed because a noop flow hasn't any buffer that would start processing in tests - .buffer(1, OverflowStrategy.backpressure) - - def websocketMultipleProtocolRoute = - path("services") { - handleWebSocketMessagesForProtocol(greeterService, "greeter") ~ - handleWebSocketMessagesForProtocol(echoService, "echo") - } - - // tests: - val wsClient = WSProbe() - - // WS creates a WebSocket request for testing - WS("/services", wsClient.flow, List("other", "echo")) ~> - websocketMultipleProtocolRoute ~> - check { - expectWebSocketUpgradeWithProtocol { protocol => - protocol shouldEqual "echo" - - wsClient.sendMessage("Peter") - wsClient.expectMessage("Peter") - - wsClient.sendMessage(BinaryMessage(ByteString("abcdef"))) - wsClient.expectMessage(ByteString("abcdef")) - - wsClient.sendMessage("John") - wsClient.expectMessage("John") - - wsClient.sendCompletion() - wsClient.expectCompletion() - } - } - } -} diff --git a/akka-docs/rst/scala/http/DispatcherBehaviourOnBadCode.png b/akka-docs/rst/scala/http/DispatcherBehaviourOnBadCode.png deleted file mode 100644 index 8cfa3b8a8c..0000000000 Binary files a/akka-docs/rst/scala/http/DispatcherBehaviourOnBadCode.png and /dev/null differ diff --git a/akka-docs/rst/scala/http/DispatcherBehaviourOnGoodCode.png b/akka-docs/rst/scala/http/DispatcherBehaviourOnGoodCode.png deleted file mode 100644 index c764b2f737..0000000000 Binary files a/akka-docs/rst/scala/http/DispatcherBehaviourOnGoodCode.png and /dev/null differ diff --git a/akka-docs/rst/scala/http/DispatcherBehaviourProperBlocking.png b/akka-docs/rst/scala/http/DispatcherBehaviourProperBlocking.png deleted file mode 100644 index cdcd1f8ad5..0000000000 Binary files a/akka-docs/rst/scala/http/DispatcherBehaviourProperBlocking.png and /dev/null differ diff --git a/akka-docs/rst/scala/http/client-side/client-https-support.rst b/akka-docs/rst/scala/http/client-side/client-https-support.rst deleted file mode 100644 index 40c72f62a0..0000000000 --- a/akka-docs/rst/scala/http/client-side/client-https-support.rst +++ /dev/null @@ -1,134 +0,0 @@ -.. _clientSideHTTPS: - -Client-Side HTTPS Support -========================= - -Akka HTTP supports TLS encryption on the client-side as well as on the :ref:`server-side `. - -.. 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/scaladsl/ConnectionContext.scala - :include: https-context-creation - -In addition to the ``outgoingConnection``, ``newHostConnectionPool`` and ``cachedHostConnectionPool`` methods the -`akka.http.scaladsl.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().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 TLS–for 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.scaladsl.Http: @github@/akka-http-core/src/main/scala/akka/http/scaladsl/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`_ etc. - - 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/scaladsl/HttpsExamplesSpec.scala - :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 -.. _trusting a certificate: http://typesafehub.github.io/ssl-config/WSQuickStart.html -.. _configuring the trusted cipher suites: http://typesafehub.github.io/ssl-config/CipherSuites.html \ No newline at end of file diff --git a/akka-docs/rst/scala/http/client-side/connection-level.rst b/akka-docs/rst/scala/http/client-side/connection-level.rst deleted file mode 100644 index 8f3e1d0468..0000000000 --- a/akka-docs/rst/scala/http/client-side/connection-level.rst +++ /dev/null @@ -1,97 +0,0 @@ -.. _connection-level-api: - -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. - -.. note:: - It is recommended to first read the :ref:`implications-of-streaming-http-entities` section, - as it explains the underlying full-stack streaming concepts, which may be unexpected when coming - from a background with non-"streaming first" HTTP Clients. - -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().outgoingConnection(...)`` method. Here is an example: - -.. includecode:: ../../code/docs/http/scaladsl/HttpClientExampleSpec.scala - :include: outgoing-connection-example - -Apart from the host name and port the ``Http().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 --------- - -Currently Akka HTTP doesn't implement client-side request timeout checking itself as this functionality can be regarded -as a more general purpose streaming infrastructure feature. - -It should be noted that Akka Streams provide various timeout functionality so any API that uses streams can benefit -from the stream stages such as ``idleTimeout``, ``backpressureTimeout``, ``completionTimeout``, ``initialTimeout`` -and ``throttle``. To learn more about these refer to their documentation in Akka Streams (and Scala Doc). - -For more details about timeout support in Akka HTTP in general refer to :ref:`http-timeouts-scala`. - - -.. _http-client-layer: - -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 ``BidiStage`` that is defined like this: - -.. includecode2:: /../../akka-http-core/src/main/scala/akka/http/scaladsl/Http.scala - :snippet: client-layer - -You create an instance of ``Http.ClientLayer`` by calling one of the two overloads of the ``Http().clientLayer`` method, -which also allows for varying degrees of configuration. diff --git a/akka-docs/rst/scala/http/client-side/host-level.rst b/akka-docs/rst/scala/http/client-side/host-level.rst deleted file mode 100644 index 629af2b26f..0000000000 --- a/akka-docs/rst/scala/http/client-side/host-level.rst +++ /dev/null @@ -1,160 +0,0 @@ -.. _host-level-api: - -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). - -.. note:: - It is recommended to first read the :ref:`implications-of-streaming-http-entities` section, - as it explains the underlying full-stack streaming concepts, which may be unexpected when coming - from a background with non-"streaming first" HTTP Clients. - -Requesting a Host Connection Pool ---------------------------------- - -The best way to get a hold of a connection pool to a given target endpoint is the ``Http().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().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` 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().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: - -Using a Host Connection Pool ----------------------------- - -The "pool client flow" returned by ``Http().cachedHostConnectionPool(...)`` has the following type:: - - Flow[(HttpRequest, T), (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` 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().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 ``Future[Unit]`` 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().shutdownAllConnectionPools()``. This call too produces a ``Future[Unit]`` 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().shutdownAllConnectionPools()`` method, and only once its Future completes, shutting down the actor system. - - -Example -------- - -.. includecode:: ../../code/docs/http/scaladsl/HttpClientExampleSpec.scala - :include: host-level-example diff --git a/akka-docs/rst/scala/http/client-side/index.rst b/akka-docs/rst/scala/http/client-side/index.rst deleted file mode 100644 index 0a55263911..0000000000 --- a/akka-docs/rst/scala/http/client-side/index.rst +++ /dev/null @@ -1,35 +0,0 @@ -.. _http-client-side: - -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. - -It is recommended to first read the :ref:`implications-of-streaming-http-entities` section, -as it explains the underlying full-stack streaming concepts, which may be unexpected when coming -from a background with non-"streaming first" HTTP Clients. - -Depending on your application's specific needs you can choose from three different API levels: - -:ref:`connection-level-api` - for full-control over when HTTP connections are opened/closed and how requests are scheduled across them - -:ref:`host-level-api` - for letting Akka HTTP manage a connection-pool to *one specific* host/port endpoint - -:ref:`request-level-api` - 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 diff --git a/akka-docs/rst/scala/http/client-side/request-level.rst b/akka-docs/rst/scala/http/client-side/request-level.rst deleted file mode 100644 index 0e2893284b..0000000000 --- a/akka-docs/rst/scala/http/client-side/request-level.rst +++ /dev/null @@ -1,83 +0,0 @@ -.. _request-level-api: - -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` 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:: - It is recommended to first read the :ref:`implications-of-streaming-http-entities` section, - as it explains the underlying full-stack streaming concepts, which may be unexpected when coming - from a background with non-"streaming first" HTTP Clients. - -.. 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` 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`, so the -:ref:`using-a-host-connection-pool` 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 ``Future[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. - -Using the Future-Based API in Actors -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -When using the ``Future`` 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 Future's callbacks -(such as ``map``, ``onComplete``, ...) and instead you should use the ``pipeTo`` pattern to pipe the result back -to the Actor as a message. - -.. includecode:: ../../code/docs/http/scaladsl/HttpClientExampleSpec.scala - :include: single-request-in-actor-example - -Example -------- - -.. includecode:: ../../code/docs/http/scaladsl/HttpClientExampleSpec.scala - :include: single-request-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()`` 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 diff --git a/akka-docs/rst/scala/http/client-side/websocket-support.rst b/akka-docs/rst/scala/http/client-side/websocket-support.rst deleted file mode 100644 index f432d40a70..0000000000 --- a/akka-docs/rst/scala/http/client-side/websocket-support.rst +++ /dev/null @@ -1,123 +0,0 @@ -.. _client-side-websocket-support: - -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` 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 has two subtypes :class:`Strict` or :class:`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 - -For such streamed messages :class:`BinaryMessage.Streamed` and :class:`TextMessage.Streamed` will be used. 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 the -``Future[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/scaladsl/WebSocketClientExampleSpec.scala - :include: single-WebSocket-request - - -The websocket request may also include additional headers, like in this example, HTTP Basic Auth: - -.. includecode:: ../../code/docs/http/scaladsl/WebSocketClientExampleSpec.scala - :include: authorized-single-WebSocket-request - - -webSocketClientFlow -------------------- -``webSocketClientFlow`` takes a request, and returns a ``Flow[Message, Message, Future[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/scaladsl/WebSocketClientExampleSpec.scala - :include: WebSocket-client-flow - - -webSocketClientLayer --------------------- -Just like the :ref:`http-client-layer` 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, Future[WebSocketUpgradeResponse]]``. - - -.. _half-closed-client-websockets: - -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/scaladsl/WebSocketClientExampleSpec.scala - :include: half-closed-WebSocket-closing-example - -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/scaladsl/WebSocketClientExampleSpec.scala - :include: half-closed-WebSocket-working-example - -This will keep the outgoing source from completing, but without emitting any elements until the ``Promise`` 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/scaladsl/WebSocketClientExampleSpec.scala - :include: half-closed-WebSocket-finite-working-example - -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)`` -=========================================== ================================================================================ \ No newline at end of file diff --git a/akka-docs/rst/scala/http/common/de-coding.rst b/akka-docs/rst/scala/http/common/de-coding.rst deleted file mode 100644 index 022c19c907..0000000000 --- a/akka-docs/rst/scala/http/common/de-coding.rst +++ /dev/null @@ -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 ` 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 - diff --git a/akka-docs/rst/scala/http/common/http-model.rst b/akka-docs/rst/scala/http/common/http-model.rst deleted file mode 100644 index 4894f8d8f9..0000000000 --- a/akka-docs/rst/scala/http/common/http-model.rst +++ /dev/null @@ -1,385 +0,0 @@ -.. _http-model-scala: - -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/scaladsl/ModelSpec.scala - :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 live in the ``HttpMethods`` object. -- Defined ``HttpCharset`` instances live in the ``HttpCharsets`` object. -- Defined ``HttpEncoding`` instances live in the ``HttpEncodings`` object. -- Defined ``HttpProtocol`` instances live in the ``HttpProtocols`` object. -- Defined ``MediaType`` instances live in the ``MediaTypes`` object. -- Defined ``StatusCode`` instances live in the ``StatusCodes`` object. - -HttpRequest ------------ - -``HttpRequest`` and ``HttpResponse`` are the basic case 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/scaladsl/ModelSpec.scala - :include: construct-request - -All parameters of ``HttpRequest.apply`` have default values set, so ``headers`` for example don't need to be specified -if there are none. Many of the parameters types (like ``HttpEntity`` and ``Uri``) define implicit conversions -for common use cases to simplify the creation of request and response instances. - -HttpResponse ------------- - -An ``HttpResponse`` consists of - - - a status code - - a seq of headers - - an entity (body data) - - a protocol - -Here are some examples how to construct an ``HttpResponse``: - -.. includecode:: ../../code/docs/http/scaladsl/ModelSpec.scala - :include: construct-response - -In addition to the simple ``HttpEntity`` constructors 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. - - -.. _HttpEntity-scala: - -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: - -HttpEntity.Strict - 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``. - - -HttpEntity.Default - The general, unchunked HTTP/1.1 message entity. - It has a known length and presents its data as a ``Source[ByteString]`` which can be only materialized once. - It is an error if the provided source doesn't produce exactly as many bytes as specified. - The distinction of ``Strict`` and ``Default`` is an API-only one. One the wire, both kinds of entities look the same. - - -HttpEntity.Chunked - 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[HttpEntity.ChunkStreamPart]``. - A ``ChunkStreamPart`` is either a non-empty ``Chunk`` or a ``LastChunk`` containing optional trailer headers. - The stream consists of zero or more ``Chunked`` parts and can be terminated by an optional ``LastChunk`` part. - - -HttpEntity.CloseDelimited - An unchunked entity of unknown length that is implicitly delimited by closing the connection (``Connection: close``). - The content data are 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 ``HttpEntity.Chunked`` instead! - - -HttpEntity.IndefiniteLength - A streaming entity of unspecified length for use in a ``Multipart.BodyPart``. - -__ http://tools.ietf.org/html/rfc7230#section-4.1 - -Entity types ``Strict``, ``Default``, and ``Chunked`` are a subtype of ``HttpEntity.Regular`` which allows to use them -for requests and responses. In contrast, ``HttpEntity.CloseDelimited`` can only be used for responses. - -Streaming entity types (i.e. all but ``Strict``) cannot be shared or serialized. To create a strict, sharable copy of an -entity or message use ``HttpEntity.toStrict`` or ``HttpMessage.toStrict`` which returns a ``Future`` of the object with -the body data collected into a ``ByteString``. - -The ``HttpEntity`` companion object contains several helper constructors to create entities from common types easily. - -You can pattern match over the subtypes of ``HttpEntity`` 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.dataBytes`` is -provided which returns a ``Source[ByteString, Any]`` that allows access to the data of an entity regardless of its -concrete subtype. - -.. note:: - - When to use which subtype? - - Use ``Strict`` if the amount of data is "small" and already available in memory (e.g. as a ``String`` or ``ByteString``) - - Use ``Default`` if the data is generated by a streaming data source and the size of the data is known - - Use ``Chunked`` for an entity of unknown length - - Use ``CloseDelimited`` for a response as a legacy alternative to ``Chunked`` if the client doesn't support - chunked transfer encoding. Otherwise use ``Chunked``! - - In a ``Multipart.Bodypart`` use ``IndefiniteLength`` for content of unknown length. - -.. caution:: - - When you receive a non-strict message from a connection then additional data are only read from the network when you - request them 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! - - -Limiting message entity length -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -All message entities that Akka HTTP reads from the network automatically get a length verification check attached to -them. This check makes sure that the total entity size is less than or equal to the configured -``max-content-length`` [#]_, which is an important defense against certain Denial-of-Service attacks. -However, a single global limit for all requests (or responses) is often too inflexible for applications that need to -allow large limits for *some* requests (or responses) but want to clamp down on all messages not belonging into that -group. - -In order to give you maximum flexibility in defining entity size limits according to your needs the ``HttpEntity`` -features a ``withSizeLimit`` method, which lets you adjust the globally configured maximum size for this particular -entity, be it to increase or decrease any previously set value. -This means that your application will receive all requests (or responses) from the HTTP layer, even the ones whose -``Content-Length`` exceeds the configured limit (because you might want to increase the limit yourself). -Only when the actual data stream ``Source`` contained in the entity is materialized will the boundary checks be -actually applied. In case the length verification fails the respective stream will be terminated with an -:class:`EntityStreamSizeException` either directly at materialization time (if the ``Content-Length`` is known) or whenever more -data bytes than allowed have been read. - -When called on ``Strict`` entities the ``withSizeLimit`` method will return the entity itself if the length is within -the bound, otherwise a ``Default`` entity with a single element data stream. This allows for potential refinement of the -entity size limit at a later point (before materialization of the data stream). - -By default all message entities produced by the HTTP layer automatically carry the limit that is defined in the -application's ``max-content-length`` config setting. If the entity is transformed in a way that changes the -content-length and then another limit is applied then this new limit will be evaluated against the new -content-length. If the entity is transformed in a way that changes the content-length and no new limit is applied -then the previous limit will be applied against the previous content-length. -Generally this behavior should be in line with your expectations. - -.. [#] `akka.http.parsing.max-content-length` (applying to server- as well as client-side), - `akka.http.server.parsing.max-content-length` (server-side only), - `akka.http.client.parsing.max-content-length` (client-side only) or - `akka.http.host-connection-pool.client.parsing.max-content-length` (only host-connection-pools) - - -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 `HttpEntity.Default` and `HttpEntity.Chunked` -to be used for HEAD responses with an empty data stream. - -Also, when a HEAD response has an `HttpEntity.CloseDelimited` 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-scala: - -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/scaladsl/ModelSpec.scala - :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 via the ``HttpEntity.Chunked`` entity. - As such chunked messages that do not have another deeper nested transfer encoding will not have a ``Transfer-Encoding`` - header in their ``headers`` sequence. - 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-scala`. 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. - -Strict-Transport-Security - HTTP Strict Transport Security (HSTS) is a web security policy mechanism which is communicated by the - ``Strict-Transport-Security`` header. The most important security vulnerability that HSTS can fix is SSL-stripping - man-in-the-middle attacks. The SSL-stripping attact works by transparently converting a secure HTTPS connection into a - plain HTTP connection. The user can see that the connection is insecure, but crucially there is no way of knowing - whether the connection should be secure. HSTS addresses this problem by informing the browser that connections to the - site should always use TLS/SSL. See also `RFC 6797`_. - -.. _RFC 6797: http://tools.ietf.org/html/rfc6797 - -__ @github@/akka-http-core/src/test/scala/akka/http/impl/engine/rendering/ResponseRendererSpec.scala#L422 - -.. _custom-headers-scala: - -Custom Headers --------------- - -Sometimes you may need to model a custom header type which is not part of HTTP and still be able to use it -as convienient as is possible with the built-in types. - -Because of the number of ways one may interact with headers (i.e. try to match a ``CustomHeader`` against a ``RawHeader`` -or the other way around etc), a helper trait for custom Header types and their companions classes are provided by Akka HTTP. -Thanks to extending :class:`ModeledCustomHeader` instead of the plain ``CustomHeader`` such header can be matched - -.. includecode:: ../../../../../akka-http-tests/src/test/scala/akka/http/scaladsl/server/ModeledCustomHeaderSpec.scala - :include: modeled-api-key-custom-header - -Which allows the this CustomHeader to be used in the following scenarios: - -.. includecode:: ../../../../../akka-http-tests/src/test/scala/akka/http/scaladsl/server/ModeledCustomHeaderSpec.scala - :include: matching-examples - -Including usage within the header directives like in the following :ref:`-headerValuePF-` example: - -.. includecode:: ../../../../../akka-http-tests/src/test/scala/akka/http/scaladsl/server/ModeledCustomHeaderSpec.scala - :include: matching-in-routes - -One can also directly extend :class:`CustomHeader` which requires less boilerplate, however that has the downside of -matching against :class:`RawHeader` instances not working out-of-the-box, thus limiting its usefulnes in the routing layer -of Akka HTTP. For only rendering such header however it would be enough. - -.. note:: - When defining custom headers, prefer to extend :class:`ModeledCustomHeader` instead of :class:`CustomHeader` directly - as it will automatically make your header abide all the expected pattern matching semantics one is accustomed to - when using built-in types (such as matching a custom header against a ``RawHeader`` as is often the case in routing - layers of Akka HTTP applications). - -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().outgoingConnection`` or the Host Connection Pool APIs ``Http().singleRequest`` or ``Http().superPool``, - usually need the same settings, however the ``server`` most likely has a very different set of settings. - -.. _registeringCustomMediaTypes: - -Registering Custom Media Types ------------------------------- - -Akka HTTP `predefines`_ most commonly encountered media types and emits them in their well-typed form while parsing http messages. -Sometimes you may want to define a custom media type and inform the parser infrastructure about how to handle these custom -media types, e.g. that ``application/custom`` is to be treated as ``NonBinary`` with ``WithFixedCharset``. To achieve this you -need to register the custom media type in the server's settings by configuring ``ParserSettings`` like this: - -.. includecode:: ../../../../../akka-http-tests/src/test/scala/akka/http/scaladsl/CustomMediaTypesSpec.scala - :include: application-custom - -You may also want to read about MediaType `Registration trees`_, in order to register your vendor specific media types -in the right style / place. - -.. _Registration trees: https://en.wikipedia.org/wiki/Media_type#Registration_trees -.. _predefines: https://github.com/akka/akka/blob/master/akka-http-core/src/main/scala/akka/http/scaladsl/model/MediaType.scala#L297 - -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. \ No newline at end of file diff --git a/akka-docs/rst/scala/http/common/index.rst b/akka-docs/rst/scala/http/common/index.rst deleted file mode 100644 index 2409341280..0000000000 --- a/akka-docs/rst/scala/http/common/index.rst +++ /dev/null @@ -1,22 +0,0 @@ -.. _http-scala-common-scala: - -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 `, :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 - xml-support - timeouts diff --git a/akka-docs/rst/scala/http/common/json-support.rst b/akka-docs/rst/scala/http/common/json-support.rst deleted file mode 100644 index 835a26617d..0000000000 --- a/akka-docs/rst/scala/http/common/json-support.rst +++ /dev/null @@ -1,50 +0,0 @@ -.. _akka-http-spray-json: - -JSON Support -============ - -Akka HTTP's :ref:`marshalling ` and :ref:`unmarshalling ` -infrastructure makes it rather easy to seamlessly support specific wire representations of your data objects, like JSON, -XML or even binary encodings. - -For JSON Akka HTTP currently provides support for `spray-json`_ right out of the box through it's -``akka-http-spray-json`` module. - -Other JSON libraries are 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 - -spray-json Support ------------------- - -The SprayJsonSupport_ trait provides a ``FromEntityUnmarshaller[T]`` and ``ToEntityMarshaller[T]`` for every type ``T`` -that an implicit ``spray.json.RootJsonReader`` and/or ``spray.json.RootJsonWriter`` (respectively) is available for. - -This is how you enable automatic support for (un)marshalling from and to JSON with `spray-json`_: - -1. Add a library dependency onto ``"com.typesafe.akka" %% "akka-http-spray-json-experimental" % "@version@"``. - -2. ``import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._`` or mix in the - ``akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport`` trait. - -3. Provide a ``RootJsonFormat[T]`` for your type and bring it into scope. - Check out the `spray-json`_ documentation for more info on how to do this. - -Once you have done this (un)marshalling between JSON and your type ``T`` should work nicely and transparently. - -.. includecode2:: ../../code/docs/http/scaladsl/SprayJsonExampleSpec.scala - :snippet: minimal-spray-json-example - -4. By default, spray-json marshals your types to pretty printed json by implicit conversion using PrettyPrinter, as defined in - ``implicit def sprayJsonMarshallerConverter[T](writer: RootJsonWriter[T])(implicit printer: JsonPrinter = PrettyPrinter): ToEntityMarshaller[T]``. - Alternately to marshal your types to compact printed json, bring a ``CompactPrinter`` in scope to perform implicit conversion. - -.. includecode:: ../../code/docs/http/scaladsl/SprayJsonCompactMarshalSpec.scala - :include: example - -To learn more about how spray-json works please refer to its `documentation `_. - - -.. _spray-json: https://github.com/spray/spray-json -.. _SprayJsonSupport: @github@/akka-http-marshallers-scala/akka-http-spray-json/src/main/scala/akka/http/scaladsl/marshallers/sprayjson/SprayJsonSupport.scala diff --git a/akka-docs/rst/scala/http/common/marshalling.rst b/akka-docs/rst/scala/http/common/marshalling.rst deleted file mode 100644 index c97867d637..0000000000 --- a/akka-docs/rst/scala/http/common/marshalling.rst +++ /dev/null @@ -1,164 +0,0 @@ -.. _http-marshalling-scala: - -Marshalling -=========== - -"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 `. - -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: - -.. includecode2:: ../../code/docs/http/scaladsl/MarshalSpec.scala - :snippet: use marshal diff --git a/akka-docs/rst/scala/http/common/timeouts.rst b/akka-docs/rst/scala/http/common/timeouts.rst deleted file mode 100644 index dea4158a9f..0000000000 --- a/akka-docs/rst/scala/http/common/timeouts.rst +++ /dev/null @@ -1,78 +0,0 @@ -.. _http-timeouts-scala: - -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-scala: - -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-scala: - -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. diff --git a/akka-docs/rst/scala/http/common/unmarshalling.rst b/akka-docs/rst/scala/http/common/unmarshalling.rst deleted file mode 100644 index be1e772e84..0000000000 --- a/akka-docs/rst/scala/http/common/unmarshalling.rst +++ /dev/null @@ -1,119 +0,0 @@ -.. _http-unmarshalling-scala: - -Unmarshalling -============= - -"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 => Future[B]`` and as such quite a bit simpler -than its :ref:`marshalling ` 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: - -.. 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 `. - -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: - -.. includecode2:: ../../code/docs/http/scaladsl/UnmarshalSpec.scala - :snippet: use unmarshal - diff --git a/akka-docs/rst/scala/http/common/xml-support.rst b/akka-docs/rst/scala/http/common/xml-support.rst deleted file mode 100644 index 82672a1cc8..0000000000 --- a/akka-docs/rst/scala/http/common/xml-support.rst +++ /dev/null @@ -1,31 +0,0 @@ -.. _akka-http-xml-marshalling: - -XML Support -=========== - -Akka HTTP's :ref:`marshalling ` and :ref:`unmarshalling ` -infrastructure makes it rather easy to seamlessly support specific wire representations of your data objects, like JSON, -XML or even binary encodings. - -For XML Akka HTTP currently provides support for `Scala XML`_ right out of the box through it's -``akka-http-xml`` module. - - -Scala XML Support ------------------ - -The ScalaXmlSupport_ trait provides a ``FromEntityUnmarshaller[NodeSeq]`` and ``ToEntityMarshaller[NodeSeq]`` that -you can use directly or build upon. - -This is how you enable support for (un)marshalling from and to JSON with `Scala XML`_ ``NodeSeq``: - -1. Add a library dependency onto ``"com.typesafe.akka" %% "akka-http-xml-experimental" % "1.x"``. - -2. ``import akka.http.scaladsl.marshallers.xml.ScalaXmlSupport._`` or mix in the - ``akka.http.scaladsl.marshallers.xml.ScalaXmlSupport`` trait. - -Once you have done this (un)marshalling between XML and ``NodeSeq`` instances should work nicely and transparently. - - -.. _Scala XML: https://github.com/scala/scala-xml -.. _ScalaXmlSupport: @github@/akka-http-marshallers-scala/akka-http-xml/src/main/scala/akka/http/scaladsl/marshallers/xml/ScalaXmlSupport.scala diff --git a/akka-docs/rst/scala/http/configuration.rst b/akka-docs/rst/scala/http/configuration.rst deleted file mode 100644 index 8760c0cc54..0000000000 --- a/akka-docs/rst/scala/http/configuration.rst +++ /dev/null @@ -1,28 +0,0 @@ -.. _akka-http-configuration: - -Configuration -============= - -Just like any other Akka module Akka HTTP is configured via `Typesafe Config`_. -Usually this means that you provide an ``application.conf`` which contains all the application-specific settings that -differ from the default ones provided by the reference configuration files from the individual Akka modules. - -These are the relevant default configuration values for the Akka HTTP modules. - -akka-http-core -~~~~~~~~~~~~~~ - -.. literalinclude:: ../../../../akka-http-core/src/main/resources/reference.conf - :language: none - - -akka-http -~~~~~~~~~ - -.. literalinclude:: ../../../../akka-http/src/main/resources/reference.conf - :language: none - - -The other Akka HTTP modules do not offer any configuration via `Typesafe Config`_. - -.. _Typesafe Config: https://github.com/typesafehub/config \ No newline at end of file diff --git a/akka-docs/rst/scala/http/handling-blocking-operations-in-akka-http-routes.rst b/akka-docs/rst/scala/http/handling-blocking-operations-in-akka-http-routes.rst deleted file mode 100644 index e264edd487..0000000000 --- a/akka-docs/rst/scala/http/handling-blocking-operations-in-akka-http-routes.rst +++ /dev/null @@ -1,120 +0,0 @@ -.. _handling-blocking-in-http-routes-scala: - -Handling blocking operations in Akka HTTP -========================================= -Sometimes it is difficult to avoid performing the blocking operations and there -are good chances that the blocking is done inside a Future execute, which may -lead to problems. It is important to handle the blocking operations correctly. - -Problem -------- -Using ``context.dispatcher`` as the dispatcher on which the blocking Future -executes, can be a problem. The same dispatcher is used by the routing -infrastructure to actually handle the incoming requests. - -If all of the available threads are blocked, the routing infrastructure will end up *starving*. -Therefore, routing infrastructure should not be blocked. Instead, a dedicated dispatcher -for blocking operations should be used. - -.. note:: - Blocking APIs should also be avoided if possible. Try to find or build Reactive APIs, - such that blocking is minimised, or moved over to dedicated dispatchers. - - Often when integrating with existing libraries or systems it is not possible to - avoid blocking APIs, then following solution explains how to handle blocking - operations properly. - - Note that the same hints apply to managing blocking operations anywhere in Akka, - including in Actors etc. - -In the below thread state diagrams the colours have the following meaning: - -* Turquoise - Sleeping state -* Orange - Waiting state -* Green - Runnable state - -The thread information was recorded using the YourKit profiler, however any good JVM profiler -has this feature (including the free and bundled with the Oracle JDK VisualVM as well as Oracle Flight Recorder). - -Problem example: blocking the default dispatcher -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. includecode2:: ../code/docs/http/scaladsl/server/BlockingInHttpExamplesSpec.scala - :snippet: blocking-example-in-default-dispatcher - -Here the app is exposed to load of continous GET requests and large number -of akka.actor.default-dispatcher threads are handling requests. The orange -portion of the thread shows that they are idle. Idle threads are fine, -they're ready to accept new work. However large amounts of Turquoise (sleeping) threads are very bad! - -.. image:: DispatcherBehaviourOnBadCode.png - -After some time, the app is exposed to the load of requesting POST requests, -which will block these threads. For example "``default-akka.default-dispatcher2,3,4``" -are going into the blocking state, after being idle before. It can be observed -that the number of new threads increase, "``default-akka.actor.default-dispatcher 18,19,20,...``" -however they go to sleep state immediately, thus wasting the -resources. - -The number of such new threads depend on the default dispatcher configuration, -but likely will not exceed 50. Since many POST requests are done, the entire -thread pool is starved. The blocking operations dominate such that the routing -infra has no thread available to handle the other requests. - -In essence, the ``Thread.sleep`` has dominated all threads and caused anything -executing on the default dispatcher to starve for resources (including any Actors -that you have not configured an explicit dispatcher for (sic!)). - -Solution: Dedicated dispatcher for blocking operations ------------------------------------------------------- - -In ``application.conf``, the dispatcher dedicated for blocking behaviour should -be configured as follows:: - - my-blocking-dispatcher { - type = Dispatcher - executor = "thread-pool-executor" - thread-pool-executor { - // or in Akka 2.4.2+ - fixed-pool-size = 16 - } - throughput = 100 - } - -There are many dispatcher options available which can be found in :ref:`dispatchers-scala`. - -Here ``thread-pool-executor`` is used, which has a hard limit of threads, it can -keep available for blocking operations. The size settings depend on the app -functionality and the number of cores the server has. - -Whenever blocking has to be done, use the above configured dispatcher -instead of the default one: - -.. includecode2:: ../code/docs/http/scaladsl/server/BlockingInHttpExamplesSpec.scala - :snippet: blocking-example-in-dedicated-dispatcher - -This forces the app to use the same load, initially normal requests and then -the blocking requests. The thread pool behaviour is shown in the figrue. - -.. image:: DispatcherBehaviourOnGoodCode.png - -Initially, the normal requests are easily handled by default dispatcher, the -green lines, which represents the actual execution. - -When blocking operations are issued, the ``my-blocking-dispatcher`` -starts up to the number of configured threads. It handles sleeping. After -certain period of nothing happening to the threads, it shuts them down. - -If another bunch of operations have to be done, the pool will start new -threads that will take care of putting them into sleep state, but the -threads are not wasted. - -In this case, the throughput of the normal GET requests are not impacted -they were still served on the default dispatcher. - -This is the recommended way of dealing with any kind of blocking in reactive -applications. It is referred as "bulkheading" or "isolating" the bad behaving -parts of an app. In this case, bad behaviour of blocking operations. - -There is good documentation availabe in Akka docs section, -`Blocking needs careful management `_. diff --git a/akka-docs/rst/scala/http/implications-of-streaming-http-entity.rst b/akka-docs/rst/scala/http/implications-of-streaming-http-entity.rst deleted file mode 100644 index 5d236a9019..0000000000 --- a/akka-docs/rst/scala/http/implications-of-streaming-http-entity.rst +++ /dev/null @@ -1,130 +0,0 @@ -.. _implications-of-streaming-http-entities: - -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 layers–from 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/scaladsl/HttpClientExampleSpec.scala - :include: 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)`` method which can be used to -eagerly consume the entity and make it available in memory: - -.. includecode:: ../code/docs/http/scaladsl/HttpClientExampleSpec.scala - :include: 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/scaladsl/HttpClientExampleSpec.scala - :include: manual-entity-discard-example-1 - -Or the equivalent low-level code achieving the same result: - -.. includecode:: ../code/docs/http/scaladsl/HttpClientExampleSpec.scala - :include: 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(as[String])`` 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-` directive: - -.. includecode:: ../code/docs/http/scaladsl/HttpServerExampleSpec.scala - :include: 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 ``Future[IoResult]`` once all the data has been written into the file: - -.. includecode:: ../code/docs/http/scaladsl/HttpServerExampleSpec.scala - :include: 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/scaladsl/HttpServerExampleSpec.scala - :include: discard-discardEntityBytes - -A related concept is *cancelling* the incoming ``entity.dataBytes`` 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.dataBytes`` 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/scaladsl/HttpServerExampleSpec.scala - :include: discard-close-connections - -Closing connections is also explained in depth in the :ref:`http-closing-connection-low-level` 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 `_ - as well as `issue #18540 `_ ; as always, contributions are very welcome! - diff --git a/akka-docs/rst/scala/http/index.rst b/akka-docs/rst/scala/http/index.rst index 570f59eb3e..42c0745c15 100644 --- a/akka-docs/rst/scala/http/index.rst +++ b/akka-docs/rst/scala/http/index.rst @@ -1,20 +1,5 @@ -.. _http-scala: +Akka HTTP Documentation (Scala) moved! +====================================== -Akka HTTP -========= - -.. toctree:: - :maxdepth: 2 - - introduction - configuration - common/index - implications-of-streaming-http-entity - low-level-server-side-api - routing-dsl/index - client-side/index - server-side-https-support - handling-blocking-operations-in-akka-http-routes - migration-from-spray - migration-from-old-http-javadsl - migration-guide-2.4.x-experimental +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/ `_. diff --git a/akka-docs/rst/scala/http/introduction.rst b/akka-docs/rst/scala/http/introduction.rst deleted file mode 100644 index 3c1db00013..0000000000 --- a/akka-docs/rst/scala/http/introduction.rst +++ /dev/null @@ -1,156 +0,0 @@ -.. _http-introduction-scala: - -Introduction -============ - -The Akka HTTP modules implement a full server- and client-side HTTP stack on top of *akka-actor* and *akka-stream*. It's -not a web-framework but rather a more general toolkit for providing and consuming HTTP-based services. While interaction -with a browser is of course also in scope it is not the primary focus of Akka HTTP. - -Akka HTTP follows a rather open design and many times offers several different API levels for "doing the same thing". -You get to pick the API level of abstraction that is most suitable for your application. -This means that, if you have trouble achieving something using a high-level API, there's a good chance that you can get -it done with a low-level API, which offers more flexibility but might require you to write more application code. - -Philosophy ----------- - -Akka HTTP has been driven with a clear focus on providing tools for building integration layers rather than application cores. As such it regards itself as a suite of libraries rather than a framework. - -A framework, as we’d like to think of the term, gives you a “frame”, in which you build your application. It comes with a lot of decisions already pre-made and provides a foundation including support structures that lets you get started and deliver results quickly. In a way a framework is like a skeleton onto which you put the “flesh” of your application in order to have it come alive. As such frameworks work best if you choose them before you start application development and try to stick to the frameworks “way of doing things” as you go along. - -For example, if you are building a browser-facing web application it makes sense to choose a web framework and build your application on top of it because the “core” of the application is the interaction of a browser with your code on the web-server. The framework makers have chosen one “proven” way of designing such applications and let you “fill in the blanks” of a more or less flexible “application-template”. Being able to rely on best-practice architecture like this can be a great asset for getting things done quickly. - -However, if your application is not primarily a web application because its core is not browser-interaction but some specialized maybe complex business service and you are merely trying to connect it to the world via a REST/HTTP interface a web-framework might not be what you need. In this case the application architecture should be dictated by what makes sense for the core not the interface layer. Also, you probably won’t benefit from the possibly existing browser-specific framework components like view templating, asset management, JavaScript- and CSS generation/manipulation/minification, localization support, AJAX support, etc. - -Akka HTTP was designed specifically as “not-a-framework”, not because we don’t like frameworks, but for use cases where a framework is not the right choice. Akka HTTP is made for building integration layers based on HTTP and as such tries to “stay on the sidelines”. Therefore you normally don’t build your application “on top of” Akka HTTP, but you build your application on top of whatever makes sense and use Akka HTTP merely for the HTTP integration needs. - -Using Akka HTTP ---------------- -Akka HTTP is provided in a separate jar file, to use it make sure to include the following dependency:: - - "com.typesafe.akka" %% "akka-http-experimental" % "@version@" @crossString@ - -Mind that ``akka-http`` comes in two modules: ``akka-http-experimental`` and ``akka-http-core``. Because ``akka-http-experimental`` -depends on ``akka-http-core`` you don't need to bring the latter explicitly. Still you may need to this in case you rely -solely on low-level API. - - -Routing DSL for HTTP servers ----------------------------- -The high-level, routing API of Akka HTTP provides a DSL to describe HTTP "routes" and how they should be handled. -Each route is composed of one or more level of ``Directive`` s that narrows down to handling one specific type of -request. - -For example one route might start with matching the ``path`` of the request, only matching if it is "/hello", then -narrowing it down to only handle HTTP ``get`` requests and then ``complete`` those with a string literal, which -will be sent back as a HTTP OK with the string as response body. - -Transforming request and response bodies between over-the-wire formats and objects to be used in your application is -done separately from the route declarations, in marshallers, which are pulled in implicitly using the "magnet" pattern. -This means that you can ``complete`` a request with any kind of object a as long as there is an implicit marshaller -available in scope. - -Default marshallers are provided for simple objects like String or ByteString, and you can define your own for example -for JSON. An additional module provides JSON serialization using the spray-json library (see :ref:`akka-http-spray-json` -for details). - -The ``Route`` created using the Route DSL is then "bound" to a port to start serving HTTP requests: - -.. includecode2:: ../code/docs/http/scaladsl/HttpServerExampleSpec.scala - :snippet: minimal-routing-example - -A common use case is to reply to a request using a model object having the marshaller transform it into JSON. In -this case shown by two separate routes. The first route queries an asynchronous database and marshalls the -``Future[Option[Item]]`` result into a JSON response. The second unmarshalls an ``Order`` from the incoming request -saves it to the database and replies with an OK when done. - -.. includecode2:: ../code/docs/http/scaladsl/SprayJsonExampleSpec.scala - :snippet: second-spray-json-example - -The logic for the marshalling and unmarshalling JSON in this example is provided by the "spray-json" library -(details on how to use that here: :ref:`akka-http-spray-json`). - -One of the strengths of Akka HTTP is that streaming data is at its heart meaning that both request and response bodies -can be streamed through the server achieving constant memory usage even for very large requests or responses. Streaming -responses will be backpressured by the remote client so that the server will not push data faster than the client can -handle, streaming requests means that the server decides how fast the remote client can push the data of the request -body. - -Example that streams random numbers as long as the client accepts them: - -.. includecode:: ../code/docs/http/scaladsl/HttpServerExampleSpec.scala - :include: stream-random-numbers - -Connecting to this service with a slow HTTP client would backpressure so that the next random number is produced on -demand with constant memory usage on the server. This can be seen using curl and limiting the rate -``curl --limit-rate 50b 127.0.0.1:8080/random`` - - -Akka HTTP routes easily interacts with actors. In this example one route allows for placing bids in a fire-and-forget -style while the second route contains a request-response interaction with an actor. The resulting response is rendered -as json and returned when the response arrives from the actor. - -.. includecode:: ../code/docs/http/scaladsl/HttpServerExampleSpec.scala - :include: actor-interaction - -Again the logic for the marshalling and unmarshalling JSON in this example is provided by the "spray-json" library -(details on how to use that here: :ref:`akka-http-spray-json`) - - -Read more about the details of the high level APIs in the section :ref:`http-high-level-server-side-api`. - -Low-level HTTP server APIs --------------------------- -The low-level Akka HTTP server APIs allows for handling connections or individual requests by accepting -``HttpRequest`` s and answering them by producing ``HttpResponse`` s. This is provided by the ``akka-http-core`` module. -APIs for handling such request-responses as function calls and as a ``Flow[HttpRequest, HttpResponse, _]`` are available. - -.. includecode2:: ../code/docs/http/scaladsl/HttpServerExampleSpec.scala - :snippet: low-level-server-example - -Read more details about the low level APIs in the section :ref:`http-low-level-server-side-api`. - - -HTTP client API ---------------- -The client APIs provide methods for calling a HTTP server using the same ``HttpRequest`` and ``HttpResponse`` abstractions -that Akka HTTP server uses but adds the concept of connection pools to allow multiple requests to the same server to be -handled more performantly by re-using TCP connections to the server. - -Example simple request: - -.. includecode:: ../code/docs/http/scaladsl/HttpClientExampleSpec.scala - :include: single-request-example - - -Read more about the details of the client APIs in the section :ref:`http-client-side`. - - - -The modules that make up Akka HTTP ----------------------------------- -Akka HTTP is structured into several modules: - -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, this is the recommended way to write HTTP servers - with Akka HTTP. Details can be found in the section :ref:`http-high-level-server-side-api` - -akka-http-core - A complete, mostly low-level, server- and client-side implementation of HTTP (incl. WebSockets) - Details can be found in sections :ref:`http-low-level-server-side-api` and :ref:`http-client-side` - -akka-http-testkit - A test harness and set of utilities for verifying server-side service implementations - -akka-http-spray-json - Predefined glue-code for (de)serializing custom types from/to JSON with spray-json_ - Details can be found here: :ref:`akka-http-spray-json` - -akka-http-xml - Predefined glue-code for (de)serializing custom types from/to XML with scala-xml_ - Details can be found here: :ref:`akka-http-xml-marshalling` - -.. _spray-json: https://github.com/spray/spray-json -.. _scala-xml: https://github.com/scala/scala-xml diff --git a/akka-docs/rst/scala/http/low-level-server-side-api.rst b/akka-docs/rst/scala/http/low-level-server-side-api.rst deleted file mode 100644 index 30bf2b1620..0000000000 --- a/akka-docs/rst/scala/http/low-level-server-side-api.rst +++ /dev/null @@ -1,256 +0,0 @@ -.. _http-low-level-server-side-api: - -Low-Level Server-Side API -========================= - -Apart from the :ref:`HTTP Client ` Akka HTTP also provides an embedded, -`Reactive-Streams`_-based, fully asynchronous HTTP/1.1 server implemented on top of :ref:`Akka Stream `. - -It sports the following features: - -- Full support for `HTTP persistent connections`_ -- Full support for `HTTP pipelining`_ -- Full support for asynchronous HTTP streaming including "chunked" transfer encoding accessible through an idiomatic API -- Optional SSL/TLS encryption -- WebSocket support - -.. _HTTP persistent connections: http://en.wikipedia.org/wiki/HTTP_persistent_connection -.. _HTTP pipelining: http://en.wikipedia.org/wiki/HTTP_pipelining -.. _Reactive-Streams: http://www.reactive-streams.org/ - - -The server-side components of Akka HTTP are split into two layers: - -1. The basic low-level server implementation in the ``akka-http-core`` module -2. Higher-level functionality in the ``akka-http`` module - -The low-level server (1) is scoped with a clear focus on the essential functionality of an HTTP/1.1 server: - -- Connection management -- Parsing and rendering of messages and headers -- Timeout management (for requests and connections) -- Response ordering (for transparent pipelining support) - -All non-core features of typical HTTP servers (like request routing, file serving, compression, etc.) are left to -the higher layers, they are not implemented by the ``akka-http-core``-level server itself. -Apart from general focus this design keeps the server core small and light-weight as well as easy to understand and -maintain. - -Depending on your needs you can either use the low-level API directly or rely on the high-level -:ref:`Routing DSL ` which can make the definition of more complex service logic much -easier. - -.. note:: - It is recommended to read the :ref:`implications-of-streaming-http-entities` section, - as it explains the underlying full-stack streaming concepts, which may be unexpected when coming - from a background with non-"streaming first" HTTP Servers. - -Streams and HTTP ----------------- - -The Akka HTTP server is implemented on top of :ref:`Akka Stream ` and makes heavy use of it - in its -implementation as well as on all levels of its API. - -On the connection level Akka HTTP offers basically the same kind of interface as :ref:`Akka Stream IO `: -A socket binding is represented as a stream of incoming connections. The application pulls connections from this stream -source and, for each of them, provides a ``Flow[HttpRequest, HttpResponse, _]`` to "translate" requests into responses. - -Apart from regarding a socket bound on the server-side as a ``Source[IncomingConnection]`` and each connection as a -``Source[HttpRequest]`` with a ``Sink[HttpResponse]`` the stream abstraction is also present inside a single HTTP -message: The entities of HTTP requests and responses are generally modeled as a ``Source[ByteString]``. See also -the :ref:`http-model-scala` for more information on how HTTP messages are represented in Akka HTTP. - - -Starting and Stopping ---------------------- - -On the most basic level an Akka HTTP server is bound by invoking the ``bind`` method of the `akka.http.scaladsl.Http`_ -extension: - -.. includecode2:: ../code/docs/http/scaladsl/HttpServerExampleSpec.scala - :snippet: binding-example - -Arguments to the ``Http().bind`` method specify the interface and port to bind to and register interest in handling -incoming HTTP connections. Additionally, the method also allows for the definition of socket options as well as a larger -number of settings for configuring the server according to your needs. - -The result of the ``bind`` method is a ``Source[Http.IncomingConnection]`` which must be drained by the application in -order to accept incoming connections. -The actual binding is not performed before this source is materialized as part of a processing pipeline. In -case the bind fails (e.g. because the port is already busy) the materialized stream will immediately be terminated with -a respective exception. -The binding is released (i.e. the underlying socket unbound) when the subscriber of the incoming -connection source has cancelled its subscription. Alternatively one can use the ``unbind()`` method of the -``Http.ServerBinding`` instance that is created as part of the connection source's materialization process. -The ``Http.ServerBinding`` also provides a way to get a hold of the actual local address of the bound socket, which is -useful for example when binding to port zero (and thus letting the OS pick an available port). - -.. _akka.http.scaladsl.Http: @github@/akka-http-core/src/main/scala/akka/http/scaladsl/Http.scala - - -Request-Response Cycle ----------------------- - -When a new connection has been accepted it will be published as an ``Http.IncomingConnection`` which consists -of the remote address and methods to provide a ``Flow[HttpRequest, HttpResponse, _]`` to handle requests coming in over -this connection. - -Requests are handled by calling one of the ``handleWithXXX`` methods with a handler, which can either be - - - a ``Flow[HttpRequest, HttpResponse, _]`` for ``handleWith``, - - a function ``HttpRequest => HttpResponse`` for ``handleWithSyncHandler``, - - a function ``HttpRequest => Future[HttpResponse]`` for ``handleWithAsyncHandler``. - -Here is a complete example: - -.. includecode2:: ../code/docs/http/scaladsl/HttpServerExampleSpec.scala - :snippet: full-server-example - -In this example, a request is handled by transforming the request stream with a function ``HttpRequest => HttpResponse`` -using ``handleWithSyncHandler`` (or equivalently, Akka Stream's ``map`` operator). Depending on the use case many -other ways of providing a request handler are conceivable using Akka Stream's combinators. - -If the application provides a ``Flow`` it is also the responsibility of the application to generate exactly one response -for every request and that the ordering of responses matches the ordering of the associated requests (which is relevant -if HTTP pipelining is enabled where processing of multiple incoming requests may overlap). When relying on -``handleWithSyncHandler`` or ``handleWithAsyncHandler``, or the ``map`` or ``mapAsync`` stream operators, this -requirement will be automatically fulfilled. - - -Streaming Request/Response Entities -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Streaming of HTTP message entities is supported through subclasses of ``HttpEntity``. The application needs to be able -to deal with streamed entities when receiving a request as well as, in many cases, when constructing responses. -See :ref:`HttpEntity-scala` for a description of the alternatives. - -If you rely on the :ref:`http-marshalling-scala` and/or :ref:`http-unmarshalling-scala` facilities provided by -Akka HTTP then the conversion of custom types to and from streamed entities can be quite convenient. - -.. _http-closing-connection-low-level: - -Closing a connection -~~~~~~~~~~~~~~~~~~~~ - -The HTTP connection will be closed when the handling ``Flow`` cancels its upstream subscription or the peer closes the -connection. An often times more convenient alternative is to explicitly add a ``Connection: close`` header to an -``HttpResponse``. This response will then be the last one on the connection and the server will actively close the -connection when it has been sent out. - -Connection will also be closed if request entity has been 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 entity should be -explicitly drained by attaching it to ``Sink.ignore``. - -Configuring Server-side HTTPS ------------------------------ - -For detailed documentation about configuring and using HTTPS on the server-side refer to :ref:`serverSideHTTPS-scala`. - -.. _http-server-layer-scala: - -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 server-side the stand-alone HTTP layer forms a ``BidiFlow`` that is defined like this: - -.. includecode2:: /../../akka-http-core/src/main/scala/akka/http/scaladsl/Http.scala - :snippet: server-layer - -You create an instance of ``Http.ServerLayer`` by calling one of the two overloads of the ``Http().serverLayer`` method, -which also allows for varying degrees of configuration. - -Controlling server parallelism ------------------------------- - -Request handling can be parallelized on two axes, by handling several connections in parallel and by -relying on HTTP pipelining to send several requests on one connection without waiting for a response first. In both -cases the client controls the number of ongoing requests. To prevent being overloaded by too many requests, Akka HTTP -can limit the number of requests it handles in parallel. - -To limit the number of simultaneously open connections, use the ``akka.http.server.max-connections`` setting. This setting -applies to all of ``Http.bindAndHandle*`` methods. If you use ``Http.bind``, incoming connections are represented by -a ``Source[IncomingConnection, ...]``. Use Akka Stream's combinators to apply backpressure to control the flow of -incoming connections, e.g. by using ``throttle`` or ``mapAsync``. - -HTTP pipelining is generally discouraged (and `disabled by most browsers `_) but -is nevertheless fully supported in Akka HTTP. The limit is applied on two levels. First, there's the -``akka.http.server.pipeline-limit`` config setting which prevents that more than the given number of outstanding requests -is ever given to the user-supplied handler-flow. On the other hand, the handler flow itself can apply any kind of throttling -itself. If you use one of the ``Http.bindAndHandleSync`` or ``Http.bindAndHandleAsync`` -entry-points, you can specify the ``parallelism`` argument (default = 1, i.e. pipelining disabled) to control the -number of concurrent requests per connection. If you use ``Http.bindAndHandle`` or ``Http.bind``, the user-supplied handler -flow has full control over how many request it accepts simultaneously by applying backpressure. In this case, you can -e.g. use Akka Stream's ``mapAsync`` combinator with a given parallelism to limit the number of concurrently handled requests. -Effectively, the more constraining one of these two measures, config setting and manual flow shaping, will determine -how parallel requests on one connection are handled. - -.. _handling-http-server-failures-low-level-scala: - -Handling HTTP Server failures in the Low-Level API --------------------------------------------------- - -There are various situations when failure may occur while initialising or running an Akka HTTP server. -Akka by default will log all these failures, however sometimes one may want to react to failures in addition to them -just being logged, for example by shutting down the actor system, or notifying some external monitoring end-point explicitly. - -There are multiple things that can fail when creating and materializing an HTTP Server (similarily, the same applied to -a plain streaming ``Tcp()`` server). The types of failures that can happen on different layers of the stack, starting -from being unable to start the server, and ending with failing to unmarshal an HttpRequest, examples of failures include -(from outer-most, to inner-most): - -- Failure to ``bind`` to the specified address/port, -- Failure while accepting new ``IncommingConnection`` s, for example when the OS has run out of file descriptors or memory, -- Failure while handling a connection, for example if the incoming ``HttpRequest`` is malformed. - -This section describes how to handle each failure situation, and in which situations these failures may occur. - -Bind failures -^^^^^^^^^^^^^ - -The first type of failure is when the server is unable to bind to the given port. For example when the port -is already taken by another application, or if the port is privileged (i.e. only usable by ``root``). -In this case the "binding future" will fail immediatly, and we can react to if by listening on the Future's completion: - -.. includecode2:: ../code/docs/http/scaladsl/HttpServerExampleSpec.scala - :snippet: binding-failure-handling - -Once the server has successfully bound to a port, the ``Source[IncomingConnection, _]`` starts running and emiting -new incoming connections. This source technically can signal a failure as well, however this should only happen in very -dramantic situations such as running out of file descriptors or memory available to the system, such that it's not able -to accept a new incoming connection. Handling failures in Akka Streams is pretty stright forward, as failures are signaled -through the stream starting from the stage which failed, all the way downstream to the final stages. - -Connections Source failures -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -In the example below we add a custom ``GraphStage`` (see :ref:`stream-customize-scala`) in order to react to the -stream's failure. We signal a ``failureMonitor`` actor with the cause why the stream is going down, and let the Actor -handle the rest – maybe it'll decide to restart the server or shutdown the ActorSystem, that however is not our concern anymore. - -.. includecode2:: ../code/docs/http/scaladsl/HttpServerExampleSpec.scala - :snippet: incoming-connections-source-failure-handling - -Connection failures -^^^^^^^^^^^^^^^^^^^ - -The third type of failure that can occur is when the connection has been properly established, -however afterwards is terminated abruptly – for example by the client aborting the underlying TCP connection. -To handle this failure we can use the same pattern as in the previous snippet, however apply it to the connection's Flow: - -.. includecode2:: ../code/docs/http/scaladsl/HttpServerExampleSpec.scala - :snippet: connection-stream-failure-handling - -These failures can be described more or less infrastructure related, they are failing bindings or connections. -Most of the time you won't need to dive into those very deeply, as Akka will simply log errors of this kind -anyway, which is a reasonable default for such problems. - -In order to learn more about handling exceptions in the actual routing layer, which is where your application code -comes into the picture, refer to :ref:`exception-handling-scala` which focuses explicitly on explaining how exceptions -thrown in routes can be handled and transformed into :class:`HttpResponse` s with apropriate error codes and human-readable failure descriptions. - diff --git a/akka-docs/rst/scala/http/migration-from-old-http-javadsl.rst b/akka-docs/rst/scala/http/migration-from-old-http-javadsl.rst deleted file mode 100644 index 8ce46f53a2..0000000000 --- a/akka-docs/rst/scala/http/migration-from-old-http-javadsl.rst +++ /dev/null @@ -1,70 +0,0 @@ -.. _http-javadsl-migration-guide: - -Migration Guide from "old" HTTP JavaDSL -======================================= - -The so-called "old" JavaDSL for Akka HTTP was initially developed during the project's experimental phase, -and thanks to multiple user comments and contributions we were able to come up with a more Java 8 "feel", -which at the same time is also closer to the existing ScalaDSL. - -The previous DSL has been entirely removed and replaced with the the so-called "new" one. -Upgrading to the new DSL is **highly encouraged** since the old one not only was rather hard to work with, -it actually was not possible to express many typical use-cases using it. - -The most major changes include: - - -HttpApp is gone ---------------- -``HttpApp`` (a helper class containing a ``main()`` implementation) is gone, as we would like to encourage understanding -how the various elements of the API fit together. - -Instead developers should start applications "manually", by converting a ``Route`` to a ``Flow`` -using the ``Route.flow`` method. For examples of full apps refer to :ref:`http-testkit-java`. - -``RequestVal`` is gone ----------------------- -The old API heavily relied on the concept of "request values" which could be used to extract a value from a request context. - -Based on community feedback and our own experience we found them too hard to work with in more complex settings. -The concept of a request value has been completely removed, and replaced with proper "directives", exacly like in the ScalaDSL. - -**Previously**:: - - RequestVal host = Headers.byClass(Host.class).instance(); - - final Route route = - route( - handleWith1(host, (ctx, h) -> - ctx.complete(String.format("Host header was: %s", h.host())) - ) - ); - - -**Now**:: - - final Route route = - headerValueByType(Host.class, host -> complete("Host was: " + host)); - -All of ScalaDSL routing has corresponding JavaDSL -------------------------------------------------- -Both ``Route``, ``RouteResult`` and other important core concepts such as ``Rejections`` are now modeled 1:1 with Scala, -making is much simpler to understand one API based on the other one – tremendously useful when learning about some nice -pattern from blogs which used Scala, yet need to apply it in Java and the other way around. - -It is now possible to implement marshallers using Java. Refer to :ref:`marshalling-java` for details. - -Some complete* overloads changed to completeOK* ------------------------------------------------ -In JavaDSL when complete is called with only an entity, the ``OK`` response code is *assumed*, -to make this more explicit these methods contain the word ``OK`` in them. - -This has been made more consistent than previously, across all overloads and Future-versions of these APIs. - -Migration help --------------- -As always, feel free to reach out via the `akka-user `_ mailing list or gitter channels, -to seek help or guidance when migrating from the old APIs. - -For Lightbend subscription owners it is possible to reach out to the core team for help in the migration by asking specific -questions via the `Lightbend customer portal `_. diff --git a/akka-docs/rst/scala/http/migration-from-spray.rst b/akka-docs/rst/scala/http/migration-from-spray.rst deleted file mode 100644 index f49c0ae8d8..0000000000 --- a/akka-docs/rst/scala/http/migration-from-spray.rst +++ /dev/null @@ -1,185 +0,0 @@ -Migration Guide from Spray -========================== - -General notes -------------- - -Features which are not ported to the akka-http: - -- ``respondWithStatus`` also known as ``overrideStatusCode`` has not been forward ported to Akka HTTP, - as it has been seen mostly as an anti-pattern. More information here: https://github.com/akka/akka/issues/18626 -- ``respondWithMediaType`` was considered an anti-pattern in spray and is not ported to Akka HTTP. - Instead users should rely on content type negotiation as Akka HTTP implements it. - More information here: https://github.com/akka/akka/issues/18625 -- :ref:`registeringCustomMediaTypes` changed from Spray in order not to rely on global state. - -Removed HttpService -------------------- - -Spray’s ``HttpService`` was removed. This means that scala code like this:: - - val service = system.actorOf(Props(new HttpServiceActor(routes))) - IO(Http)(system) ! Http.Bind(service, "0.0.0.0", port = 8080) - -needs to be changed into:: - - Http().bindAndHandle(routes, "0.0.0.0", port = 8080) - -Changes in Marshalling ----------------------- - -Marshaller.of can be replaced with ``Marshaller.withFixedContentType``. - -Was:: - - Marshaller.of[JsonApiObject](`application/json`) { (value, contentType, ctx) => - ctx.marshalTo(HttpEntity(contentType, value.toJson.toString)) - } - -Replace with:: - - Marshaller.withFixedContentType(`application/json`) { obj => - HttpEntity(`application/json`, obj.toJson.compactPrint) - } - -Akka HTTP marshallers support content negotiation, now it's not necessary to specify content type -when creating one “super” marshaller from other marshallers: - -Before:: - - ToResponseMarshaller.oneOf( - `application/vnd.api+json`, - `application/json` - )( - jsonApiMarshaller, - jsonMarshaller - } - -After:: - - Marshaller.oneOf( - jsonApiMarshaller, - jsonMarshaller - ) - -Changes in Unmarshalling ------------------------- - -Akka Http contains a set of predefined unmarshallers. This means that scala code like this:: - - Unmarshaller[Entity](`application/json`) { - case HttpEntity.NonEmpty(contentType, data) => - data.asString.parseJson.convertTo[Entity] - } - -needs to be changed into:: - - Unmarshaller - .stringUnmarshaller - .forContentTypes(`application/json`) - .map(_.parseJson.convertTo[Entity]) - -Changes in MediaTypes ---------------------- - -``MediaType.custom`` can be replaced with specific methods in ``MediaType`` object. - -Was:: - - MediaType.custom("application/vnd.acme+json") - -Replace with:: - - MediaType.applicationWithFixedCharset("application/vnd.acme+json", HttpCharsets.`UTF-8`) - -Changes in Rejection Handling ------------------------------ - -``RejectionHandler`` now uses a builder pattern – see the example: - -Before:: - - def rootRejectionHandler = RejectionHandler { - case Nil => - requestUri { uri => - logger.error("Route: {} does not exist.", uri) - complete((NotFound, mapErrorToRootObject(notFoundError))) - } - case AuthenticationFailedRejection(cause, challengeHeaders) :: _ => { - logger.error(s"Request is rejected with cause: $cause") - complete((Unauthorized, mapErrorToRootObject(unauthenticatedError))) - } - } - - -After:: - - RejectionHandler - .newBuilder() - .handle { - case AuthenticationFailedRejection(cause, challengeHeaders) => - logger.error(s"Request is rejected with cause: $cause") - complete((Unauthorized, mapErrorToRootObject(unauthenticatedError))) - .handleNotFound { ctx => - logger.error("Route: {} does not exist.", ctx.request.uri.toString()) - ctx.complete((NotFound, mapErrorToRootObject(notFoundError))) - } - .result() - .withFallback(RejectionHandler.default) - -Changes in HTTP Client ----------------------- - -The Spray-client pipeline was removed. Http’s ``singleRequest`` should be used instead of ``sendReceive``:: - - //this will not longer work - val token = Authorization(OAuth2BearerToken(accessToken)) - val pipeline: HttpRequest => Future[HttpResponse] = (addHeader(token) ~> sendReceive) - val patch: HttpRequest = Patch(uri, object)) - - pipeline(patch).map { response ⇒ - … - } - -needs to be changed into:: - - val request = HttpRequest( - method = PATCH, - uri = Uri(uri), - headers = List(Authorization(OAuth2BearerToken(accessToken))), - entity = HttpEntity(MediaTypes.`application/json`, object) - ) - - http.singleRequest(request).map { - case … => … - } - -Changes in Headers ------------------- - -All HTTP headers have been moved to the ``akka.http.scaladsl.model.headers._`` package. - - -Changes in form fields and file upload directives -------------------------------------------------- - -With the streaming nature of http entity, it’s important to have a strict http entity before accessing -multiple form fields or use file upload directives. -One solution might be using next directive before working with form fields:: - - val toStrict: Directive0 = extractRequest flatMap { request => - onComplete(request.entity.toStrict(5.seconds)) flatMap { - case Success(strict) => - mapRequest( req => req.copy(entity = strict)) - case _ => reject - } - } - -And one can use it like this:: - - toStrict { - formFields("name".as[String]) { name => - ... - } - } - diff --git a/akka-docs/rst/scala/http/migration-guide-2.4.x-experimental.rst b/akka-docs/rst/scala/http/migration-guide-2.4.x-experimental.rst deleted file mode 100644 index fd7d36e05b..0000000000 --- a/akka-docs/rst/scala/http/migration-guide-2.4.x-experimental.rst +++ /dev/null @@ -1,42 +0,0 @@ -Migration Guide between experimental builds of Akka HTTP (2.4.x) -================================================================ - -General notes -------------- -Please note that Akka HTTP consists of a number of modules, most notably `akka-http-core` -which is **stable** and won't be breaking compatibility without a proper deprecation cycle, -and `akka-http` which contains the routing DSLs which is **experimental** still. - -The following migration guide explains migration steps to be made between breaking -versions of the **experimental** part of Akka HTTP. - -.. note:: - Please note that experimental modules are allowed (and are expected to) break compatibility - in search of the best API we can offer, before the API is frozen in a stable release. - - Please read :ref:`BinCompatRules` to understand in depth what bin-compat rules are, and where they are applied. - -Akka HTTP 2.4.7 -> 2.4.8 ------------------------- - -``SecurityDirectives#challengeFor`` has moved -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The ``challengeFor`` directive was actually more like a factory for ``HttpChallenge``, -thus it was moved to become such. It is now available as ``akka.http.javadsl.model.headers.HttpChallenge#create[Basic|OAuth2]`` -for JavaDSL and ``akka.http.scaladsl.model.headers.HttpChallenges#[basic|oAuth2]`` for ScalaDSL. - -Akka HTTP 2.4.8 -> 2.4.9 ------------------------- - -Java DSL Package structure changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -We have aligned the package structure of the Java based DSL with the Scala based DSL -and moved classes that were in the wrong or unexpected places around a bit. This means -that Java DSL users must update their imports as follows: - -Classes dealing with unmarshalling and marshalling used to reside in ``akka.http.javadsl.server``, -but are now available from the packages ``akka.http.javadsl.unmarshalling`` and ``akka.http.javadsl.marshalling``. - -``akka.http.javadsl.server.Coder`` is now ``akka.http.javadsl.coding.Coder``. - -``akka.http.javadsl.server.RegexConverters`` is now ``akka.http.javadsl.common.RegexConverters``. diff --git a/akka-docs/rst/scala/http/routing-dsl/case-class-extraction.rst b/akka-docs/rst/scala/http/routing-dsl/case-class-extraction.rst deleted file mode 100644 index 6f6f1c3602..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/case-class-extraction.rst +++ /dev/null @@ -1,68 +0,0 @@ -.. _Case Class Extraction: - -Case Class Extraction -===================== - -The value extraction performed by :ref:`Directives` is a nice way of providing your route logic with interesting request -properties, all with proper type-safety and error handling. However, in some case you might want even more. -Consider this example: - -.. includecode2:: ../../code/docs/http/scaladsl/server/CaseClassExtractionExamplesSpec.scala - :snippet: example-1 - -Here the :ref:`-parameters-scala-` directives is employed to extract three ``Int`` values, which are then used to construct an -instance of the ``Color`` case class. So far so good. However, if the model classes we'd like to work with have more -than just a few parameters the overhead introduced by capturing the arguments as extractions only to feed them into the -model class constructor directly afterwards can somewhat clutter up your route definitions. - -If your model classes are case classes, as in our example, Akka HTTP supports an even shorter and more concise -syntax. You can also write the example above like this: - -.. includecode2:: ../../code/docs/http/scaladsl/server/CaseClassExtractionExamplesSpec.scala - :snippet: example-2 - -You can postfix any directive with extractions with an ``as(...)`` call. By simply passing the companion object of your -model case class to the ``as`` modifier method the underlying directive is transformed into an equivalent one, which -extracts only one value of the type of your model class. Note that there is no reflection involved and your case class -does not have to implement any special interfaces. The only requirement is that the directive you attach the ``as`` -call to produces the right number of extractions, with the right types and in the right order. - -If you'd like to construct a case class instance from extractions produced by *several* directives you can first join -the directives with the ``&`` operator before using the ``as`` call: - -.. includecode2:: ../../code/docs/http/scaladsl/server/CaseClassExtractionExamplesSpec.scala - :snippet: example-3 - -Here the ``Color`` class has gotten another member, ``name``, which is supplied not as a parameter but as a path -element. By joining the ``path`` and ``parameters`` directives with ``&`` you create a directive extracting 4 values, -which directly fit the member list of the ``Color`` case class. Therefore you can use the ``as`` modifier to convert -the directive into one extracting only a single ``Color`` instance. - -Generally, when you have routes that work with, say, more than 3 extractions it's a good idea to introduce a case class -for these and resort to case class extraction. Especially since it supports another nice feature: validation. - - -.. caution:: There is one quirk to look out for when using case class extraction: If you create an explicit companion - object for your case class, no matter whether you actually add any members to it or not, the syntax presented above - will not (quite) work anymore. Instead of ``as(Color)`` you will then have to say ``as(Color.apply)``. This behavior - appears as if it's not really intended, so this might be improved in future Scala versions. - - -Case Class Validation ---------------------- - -In many cases your web service needs to verify input parameters according to some logic before actually working with -them. E.g. in the example above the restriction might be that all color component values must be between 0 and 255. -You could get this done with a few :ref:`-validate-` directives but this would quickly become cumbersome and hard to -read. - -If you use case class extraction you can put the verification logic into the constructor of your case class, where it -should be: - -.. includecode2:: ../../code/docs/http/scaladsl/server/CaseClassExtractionExamplesSpec.scala - :snippet: example-4 - -If you write your validations like this Akka HTTP's case class extraction logic will properly pick up all error -messages and generate a ``ValidationRejection`` if something goes wrong. By default, ``ValidationRejections`` are -converted into ``400 Bad Request`` error response by the default :ref:`RejectionHandler `, if no -subsequent route successfully handles the request. \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/alphabetically.rst b/akka-docs/rst/scala/http/routing-dsl/directives/alphabetically.rst deleted file mode 100644 index abe414440f..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/alphabetically.rst +++ /dev/null @@ -1,232 +0,0 @@ -.. _Predefined Directives: - -Predefined Directives (alphabetically) -====================================== - -=========================================== ============================================================================ -Directive Description -=========================================== ============================================================================ -:ref:`-authenticateBasic-` Wraps the inner route with Http Basic authentication support using a given - ``Authenticator[T]`` -:ref:`-authenticateBasicAsync-` Wraps the inner route with Http Basic authentication support using a given - ``AsyncAuthenticator[T]`` -:ref:`-authenticateBasicPF-` Wraps the inner route with Http Basic authentication support using a given - ``AuthenticatorPF[T]`` -:ref:`-authenticateBasicPFAsync-` Wraps the inner route with Http Basic authentication support using a given - ``AsyncAuthenticatorPF[T]`` -:ref:`-authenticateOAuth2-` Wraps the inner route with OAuth Bearer Token authentication support using - a given ``AuthenticatorPF[T]`` -:ref:`-authenticateOAuth2Async-` Wraps the inner route with OAuth Bearer Token authentication support using - a given ``AsyncAuthenticator[T]`` -:ref:`-authenticateOAuth2PF-` Wraps the inner route with OAuth Bearer Token authentication support using - a given ``AuthenticatorPF[T]`` -:ref:`-authenticateOAuth2PFAsync-` Wraps the inner route with OAuth Bearer Token authentication support using - a given ``AsyncAuthenticatorPF[T]`` -:ref:`-authenticateOrRejectWithChallenge-` Lifts an authenticator function into a directive -:ref:`-authorize-` Applies the given authorization check to the request -:ref:`-authorizeAsync-` Applies the given asynchronous authorization check to the request -:ref:`-cancelRejection-` Adds a ``TransformationRejection`` cancelling all rejections equal to the - given one to the rejections potentially coming back from the inner route. -:ref:`-cancelRejections-` Adds a ``TransformationRejection`` cancelling all matching rejections - to the rejections potentially coming back from the inner route -:ref:`-checkSameOrigin-` Checks that the request comes from the same origin -:ref:`-complete-` Completes the request using the given arguments -:ref:`-completeOrRecoverWith-` "Unwraps" a ``Future[T]`` and runs the inner route when the future has - failed with the error as an extraction of type ``Throwable`` -:ref:`-completeWith-` Uses the marshaller for a given type to extract a completion function -:ref:`-conditional-` Wraps its inner route with support for conditional requests as defined - by http://tools.ietf.org/html/rfc7232 -:ref:`-cookie-` Extracts the ``HttpCookie`` with the given name -:ref:`-decodeRequest-` Decompresses the request if it is ``gzip`` or ``deflate`` compressed -:ref:`-decodeRequestWith-` Decodes the incoming request using one of the given decoders -:ref:`-delete-` Rejects all non-DELETE requests -:ref:`-deleteCookie-` Adds a ``Set-Cookie`` response header expiring the given cookies -:ref:`-encodeResponse-` Encodes the response with the encoding that is requested by the client - via the ``Accept-Encoding`` header (``NoCoding``, ``Gzip`` and ``Deflate``) -:ref:`-encodeResponseWith-` Encodes the response with the encoding that is requested by the client - via the ``Accept-Encoding`` header (from a user-defined set) -:ref:`-entity-` Extracts the request entity unmarshalled to a given type -:ref:`-extract-` Extracts a single value using a ``RequestContext ⇒ T`` function -:ref:`-extractDataBytes-` Extracts the entities data bytes as a stream ``Source[ByteString, Any]`` -:ref:`-extractClientIP-` Extracts the client's IP from either the ``X-Forwarded-``, - ``Remote-Address`` or ``X-Real-IP`` header -:ref:`-extractCredentials-` Extracts the potentially present ``HttpCredentials`` provided with the - request's ``Authorization`` header -:ref:`-extractExecutionContext-` Extracts the ``ExecutionContext`` from the ``RequestContext`` -:ref:`-extractMaterializer-` Extracts the ``Materializer`` from the ``RequestContext`` -:ref:`-extractHost-` Extracts the hostname part of the Host request header value -:ref:`-extractLog-` Extracts the ``LoggingAdapter`` from the ``RequestContext`` -:ref:`-extractMethod-` Extracts the request method -:ref:`-extractRequest-` Extracts the current ``HttpRequest`` instance -:ref:`-extractRequestContext-` Extracts the ``RequestContext`` itself -:ref:`-extractRequestEntity-` Extracts the ``RequestEntity`` from the ``RequestContext`` -:ref:`-extractScheme-` Extracts the URI scheme from the request -:ref:`-extractSettings-` Extracts the ``RoutingSettings`` from the ``RequestContext`` -:ref:`-extractUnmatchedPath-` Extracts the yet unmatched path from the ``RequestContext`` -:ref:`-extractUri-` Extracts the complete request URI -:ref:`-failWith-` Bubbles the given error up the response chain where it is dealt with by the - closest :ref:`-handleExceptions-` directive and its ``ExceptionHandler`` -:ref:`-fileUpload-` Provides a stream of an uploaded file from a multipart request -:ref:`-formField-scala-` Extracts an HTTP form field from the request -:ref:`-formFieldMap-` Extracts a number of HTTP form field from the request as - a ``Map[String, String]`` -:ref:`-formFieldMultiMap-` Extracts a number of HTTP form field from the request as - a ``Map[String, List[String]`` -:ref:`-formFields-` Extracts a number of HTTP form field from the request -:ref:`-formFieldSeq-` Extracts a number of HTTP form field from the request as - a ``Seq[(String, String)]`` -:ref:`-get-` Rejects all non-GET requests -:ref:`-getFromBrowseableDirectories-` 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-` 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-` Completes GET requests with the content of a file underneath a given - file-system directory -:ref:`-getFromFile-` Completes GET requests with the content of a given file -:ref:`-getFromResource-` Completes GET requests with the content of a given class-path resource -:ref:`-getFromResourceDirectory-` Completes GET requests with the content of a file underneath a given - "class-path resource directory" -:ref:`-handleExceptions-` Transforms exceptions thrown during evaluation of the inner route using the - given ``ExceptionHandler`` -:ref:`-handleRejections-` Transforms rejections produced by the inner route using the given - ``RejectionHandler`` -:ref:`-handleWebSocketMessages-` Handles websocket requests with the given handler and rejects other requests - with an ``ExpectedWebSocketRequestRejection`` -:ref:`-handleWebSocketMessagesForProtocol-` Handles websocket requests with the given handler if the subprotocol matches - and rejects other requests with an ``ExpectedWebSocketRequestRejection`` or - an ``UnsupportedWebSocketSubprotocolRejection``. -:ref:`-handleWith-` Completes the request using a given function -:ref:`-head-` Rejects all non-HEAD requests -:ref:`-headerValue-` Extracts an HTTP header value using a given ``HttpHeader ⇒ Option[T]`` - function -:ref:`-headerValueByName-` Extracts the value of the first HTTP request header with a given name -:ref:`-headerValueByType-` Extracts the first HTTP request header of the given type -:ref:`-headerValuePF-` Extracts an HTTP header value using a given - ``PartialFunction[HttpHeader, T]`` -:ref:`-host-` Rejects all requests with a non-matching host name -:ref:`-listDirectoryContents-` Completes GET requests with a unified listing of the contents of all given - file-system directories -:ref:`-logRequest-` Produces a log entry for every incoming request -:ref:`-logRequestResult-` Produces a log entry for every incoming request and ``RouteResult`` -:ref:`-logResult-` Produces a log entry for every ``RouteResult`` -:ref:`-mapInnerRoute-` Transforms its inner ``Route`` with a ``Route => Route`` function -:ref:`-mapRejections-` Transforms rejections from a previous route with an - ``immutable.Seq[Rejection] ⇒ immutable.Seq[Rejection]`` function -:ref:`-mapRequest-` Transforms the request with an ``HttpRequest => HttpRequest`` function -:ref:`-mapRequestContext-` Transforms the ``RequestContext`` with a - ``RequestContext => RequestContext`` function -:ref:`-mapResponse-` Transforms the response with an ``HttpResponse => HttpResponse`` function -:ref:`-mapResponseEntity-` Transforms the response entity with an ``ResponseEntity ⇒ ResponseEntity`` - function -:ref:`-mapResponseHeaders-` Transforms the response headers with an - ``immutable.Seq[HttpHeader] ⇒ immutable.Seq[HttpHeader]`` function -:ref:`-mapRouteResult-` Transforms the ``RouteResult`` with a ``RouteResult ⇒ RouteResult`` - function -:ref:`-mapRouteResultFuture-` Transforms the ``RouteResult`` future with a - ``Future[RouteResult] ⇒ Future[RouteResult]`` function -:ref:`-mapRouteResultPF-` Transforms the ``RouteResult`` with a - ``PartialFunction[RouteResult, RouteResult]`` -:ref:`-mapRouteResultWith-` Transforms the ``RouteResult`` with a - ``RouteResult ⇒ Future[RouteResult]`` function -:ref:`-mapRouteResultWithPF-` Transforms the ``RouteResult`` with a - ``PartialFunction[RouteResult, Future[RouteResult]]`` -:ref:`-mapSettings-` Transforms the ``RoutingSettings`` with a - ``RoutingSettings ⇒ RoutingSettings`` function -:ref:`-mapUnmatchedPath-` Transforms the ``unmatchedPath`` of the ``RequestContext`` using a - ``Uri.Path ⇒ Uri.Path`` function -:ref:`-method-` Rejects all requests whose HTTP method does not match the given one -:ref:`-onComplete-` "Unwraps" a ``Future[T]`` and runs the inner route after future completion - with the future's value as an extraction of type ``Try[T]`` -:ref:`-onCompleteWithBreaker-` "Unwraps" a ``Future[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-` "Unwraps" a ``Future[T]`` and runs the inner route after future completion - with the future's value as an extraction of type ``T`` -:ref:`-optionalCookie-` Extracts the ``HttpCookiePair`` with the given name as an - ``Option[HttpCookiePair]`` -:ref:`-optionalHeaderValue-` Extracts an optional HTTP header value using a given - ``HttpHeader ⇒ Option[T]`` function -:ref:`-optionalHeaderValueByName-` Extracts the value of the first optional HTTP request header with a given - name -:ref:`-optionalHeaderValueByType-` Extracts the first optional HTTP request header of the given type -:ref:`-optionalHeaderValuePF-` Extracts an optional HTTP header value using a given - ``PartialFunction[HttpHeader, T]`` -:ref:`-options-` Rejects all non-OPTIONS requests -:ref:`-overrideMethodWithParameter-` Changes the request method to the value of the specified query parameter -:ref:`-parameter-` Extracts a query parameter value from the request -:ref:`-parameterMap-` Extracts the request's query parameters as a ``Map[String, String]`` -:ref:`-parameterMultiMap-` Extracts the request's query parameters as a ``Map[String, List[String]]`` -:ref:`-parameters-scala-` Extracts a number of query parameter values from the request -:ref:`-parameterSeq-` Extracts the request's query parameters as a ``Seq[(String, String)]`` -:ref:`-pass-` 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-` Rejects all non-PATCH requests -:ref:`-path-` Applies the given ``PathMatcher`` to the remaining unmatched path after - consuming a leading slash -:ref:`-pathEnd-` Only passes on the request to its inner route if the request path has been - matched completely -:ref:`-pathEndOrSingleSlash-` 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-` Applies the given ``PathMatcher`` to a prefix of the remaining unmatched - path after consuming a leading slash -:ref:`-pathPrefixTest-` Checks whether the unmatchedPath has a prefix matched by the given - ``PathMatcher`` after implicitly consuming a leading slash -:ref:`-pathSingleSlash-` Only passes on the request to its inner route if the request path - consists of exactly one remaining slash -:ref:`-pathSuffix-` Applies the given ``PathMatcher`` to a suffix of the remaining unmatched - path (Caution: check scaladoc!) -:ref:`-pathSuffixTest-` Checks whether the unmatched path has a suffix matched by the given - ``PathMatcher`` (Caution: check scaladoc!) -:ref:`-post-` Rejects all non-POST requests -:ref:`-provide-` Injects a given value into a directive -:ref:`-put-` Rejects all non-PUT requests -:ref:`-rawPathPrefix-` Applies the given matcher directly to a prefix of the unmatched path of the - ``RequestContext``, without implicitly consuming a leading slash -:ref:`-rawPathPrefixTest-` Checks whether the unmatchedPath has a prefix matched by the given - ``PathMatcher`` -:ref:`-recoverRejections-` Transforms rejections from the inner route with an - ``immutable.Seq[Rejection] ⇒ RouteResult`` function -:ref:`-recoverRejectionsWith-` Transforms rejections from the inner route with an - ``immutable.Seq[Rejection] ⇒ Future[RouteResult]`` function -:ref:`-redirect-` Completes the request with redirection response of the given type to the - given URI -:ref:`-redirectToNoTrailingSlashIfPresent-` If the request path ends with a slash, redirects to the same uri without - trailing slash in the path -:ref:`-redirectToTrailingSlashIfMissing-` If the request path doesn't end with a slash, redirects to the same uri with - trailing slash in the path -:ref:`-reject-` Rejects the request with the given rejections -:ref:`-rejectEmptyResponse-` Converts responses with an empty entity into (empty) rejections -:ref:`-requestEncodedWith-` Rejects the request with an ``UnsupportedRequestEncodingRejection`` if its - encoding doesn't match the given one -:ref:`-requestEntityEmpty-` Rejects if the request entity is non-empty -:ref:`-requestEntityPresent-` Rejects with a ``RequestEntityExpectedRejection`` if the request entity is - empty -:ref:`-respondWithDefaultHeader-` Adds a given response header if the response doesn't already contain a - header with the same name -:ref:`-respondWithDefaultHeaders-` 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-` Unconditionally adds a given header to the outgoing response -:ref:`-respondWithHeaders-` Unconditionally adds the given headers to the outgoing response -:ref:`-responseEncodingAccepted-` Rejects the request with an ``UnacceptedResponseEncodingRejection`` if the - given response encoding is not accepted by the client -:ref:`-scheme-` Rejects all requests whose URI scheme doesn't match the given one -:ref:`-selectPreferredLanguage-` Inspects the request's ``Accept-Language`` header and determines, which of - a given set of language alternatives is preferred by the client -:ref:`-setCookie-` Adds a ``Set-Cookie`` response header with the given cookies -:ref:`-textract-` Extracts a number of values using a ``RequestContext ⇒ Tuple`` function -:ref:`-tprovide-` Injects a given tuple of values into a directive -:ref:`-uploadedFile-` Streams one uploaded file from a multipart request to a file on disk -:ref:`-validate-` Checks a given condition before running its inner route -:ref:`-withoutRequestTimeout-` Disables :ref:`request timeouts ` for a given route. -:ref:`-withoutSizeLimit-` Skips request entity size check -:ref:`-withExecutionContext-` Runs its inner route with the given alternative ``ExecutionContext`` -:ref:`-withMaterializer-` Runs its inner route with the given alternative ``Materializer`` -:ref:`-withLog-` Runs its inner route with the given alternative ``LoggingAdapter`` -:ref:`-withRangeSupport-` Adds ``Accept-Ranges: bytes`` to responses to GET requests, produces partial - responses if the initial request contained a valid ``Range`` header -:ref:`-withRequestTimeout-` Configures the :ref:`request timeouts ` for a given route. -:ref:`-withRequestTimeoutResponse-` Prepares the ``HttpResponse`` that is emitted if a request timeout is triggered. - ``RequestContext => RequestContext`` function -:ref:`-withSettings-` Runs its inner route with the given alternative ``RoutingSettings`` -:ref:`-withSizeLimit-` Applies request entity size check -=========================================== ============================================================================ diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/cancelRejection.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/cancelRejection.rst deleted file mode 100644 index 9df8d688d5..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/cancelRejection.rst +++ /dev/null @@ -1,27 +0,0 @@ -.. _-cancelRejection-: - -cancelRejection -=============== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: 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-scala` to learn more about rejections. - -For more advanced handling of rejections refer to the :ref:`-handleRejections-` directive -which provides a nicer DSL for building rejection handlers. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: cancelRejection-example diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/cancelRejections.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/cancelRejections.rst deleted file mode 100644 index 48440bb31a..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/cancelRejections.rst +++ /dev/null @@ -1,29 +0,0 @@ -.. _-cancelRejections-: - -cancelRejections -================ - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: 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-`, for canceling a specific rejection. - -Read :ref:`rejections-scala` to learn more about rejections. - -For more advanced handling of rejections refer to the :ref:`-handleRejections-` directive -which provides a nicer DSL for building rejection handlers. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: cancelRejections-filter-example diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extract.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extract.rst deleted file mode 100644 index dbe023245f..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extract.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. _-extract-: - -extract -======= - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: extract - -Description ------------ - -The ``extract`` directive is used as a building block for :ref:`Custom Directives` to extract data from the -``RequestContext`` and provide it to the inner route. It is a special case for extracting one value of the more -general :ref:`-textract-` directive that can be used to extract more than one value. - -See :ref:`ProvideDirectives` for an overview of similar directives. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: 0extract diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractActorSystem.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractActorSystem.rst deleted file mode 100644 index e3e2340257..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractActorSystem.rst +++ /dev/null @@ -1,26 +0,0 @@ -.. _-extractActorSystem-: - -extractActorSystem -================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: 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 -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: extractActorSystem-example diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractDataBytes.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractDataBytes.rst deleted file mode 100644 index 5b962b2de5..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractDataBytes.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. _-extractDataBytes-: - -extractDataBytes -================ - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: 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 -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: extractDataBytes-example diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractExecutionContext.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractExecutionContext.rst deleted file mode 100644 index b90a295aec..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractExecutionContext.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. _-extractExecutionContext-: - -extractExecutionContext -======================= - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: extractExecutionContext - -Description ------------ - -Extracts the ``ExecutionContext`` from the ``RequestContext``. - -See :ref:`-withExecutionContext-` to see how to customise the execution context provided for an inner route. - -See :ref:`-extract-` to learn more about how extractions work. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: extractExecutionContext-0 diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractLog.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractLog.rst deleted file mode 100644 index 8ff7891ca9..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractLog.rst +++ /dev/null @@ -1,26 +0,0 @@ -.. _-extractLog-: - -extractLog -========== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: 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-` and :ref:`ProvideDirectives` for an overview of similar directives. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: 0extractLog diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractMaterializer.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractMaterializer.rst deleted file mode 100644 index 947563e1d7..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractMaterializer.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. _-extractMaterializer-: - -extractMaterializer -=================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: 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-` to see how to customise the used materializer for specific inner routes. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: extractMaterializer-0 diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractRequest.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractRequest.rst deleted file mode 100644 index 5b0140b67b..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractRequest.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. _-extractRequest-: - -extractRequest -============== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: 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`. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: extractRequest-example diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractRequestContext.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractRequestContext.rst deleted file mode 100644 index 5c067e379d..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractRequestContext.rst +++ /dev/null @@ -1,27 +0,0 @@ -.. _-extractRequestContext-: - -extractRequestContext -===================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: 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-` if only interested in the :class:`HttpRequest` instance itself. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: extractRequestContext-example diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractRequestEntity.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractRequestEntity.rst deleted file mode 100644 index 53e142c5ca..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractRequestEntity.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. _-extractRequestEntity-: - -extractRequestEntity -==================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: extractRequestEntity - -Description ------------ - -Extracts the ``RequestEntity`` from the :class:`RequestContext`. - -The directive returns a ``RequestEntity`` without unmarshalling the request. To extract domain entity, -:ref:`-entity-` should be used. - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: extractRequestEntity-example diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractSettings.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractSettings.rst deleted file mode 100644 index 33adc1c794..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractSettings.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. _-extractSettings-: - -extractSettings -=============== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: extractSettings - -Description ------------ - -Extracts the ``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-` directive. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: extractSettings-examples diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractStrictEntity.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractStrictEntity.rst deleted file mode 100644 index 29ca0aa174..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractStrictEntity.rst +++ /dev/null @@ -1,30 +0,0 @@ -.. _-extractStrictEntity-: - -extractStrictEntity -=================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: 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-` or :ref:`-withoutSizeLimit-` directive. - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: extractStrictEntity-example diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractUnmatchedPath.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractUnmatchedPath.rst deleted file mode 100644 index 5a10db7170..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractUnmatchedPath.rst +++ /dev/null @@ -1,26 +0,0 @@ -.. _-extractUnmatchedPath-: - -extractUnmatchedPath -==================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: 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` -(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 -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: extractUnmatchedPath-example diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractUri.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractUri.rst deleted file mode 100644 index 7aef8df9b3..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/extractUri.rst +++ /dev/null @@ -1,23 +0,0 @@ -.. _-extractUri-: - -extractUri -========== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: extractUri - -Description ------------ -Access the full URI of the request. - -Use :ref:`SchemeDirectives`, :ref:`HostDirectives`, :ref:`PathDirectives`, and :ref:`ParameterDirectives` for more -targeted access to parts of the URI. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: extractUri-example diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/index.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/index.rst deleted file mode 100644 index 4395e8da9f..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/index.rst +++ /dev/null @@ -1,137 +0,0 @@ -.. _BasicDirectives: - -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: - -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-` - * :ref:`-extractActorSystem-` - * :ref:`-extractDataBytes-` - * :ref:`-extractExecutionContext-` - * :ref:`-extractMaterializer-` - * :ref:`-extractStrictEntity-` - * :ref:`-extractLog-` - * :ref:`-extractRequest-` - * :ref:`-extractRequestContext-` - * :ref:`-extractRequestEntity-` - * :ref:`-extractSettings-` - * :ref:`-extractUnmatchedPath-` - * :ref:`-extractUri-` - * :ref:`-textract-` - * :ref:`-provide-` - * :ref:`-tprovide-` - - -.. _Request Transforming Directives: - -Transforming the Request(Context) ---------------------------------- - - * :ref:`-mapRequest-` - * :ref:`-mapRequestContext-` - * :ref:`-mapSettings-` - * :ref:`-mapUnmatchedPath-` - * :ref:`-withExecutionContext-` - * :ref:`-withMaterializer-` - * :ref:`-withLog-` - * :ref:`-withSettings-` - * :ref:`-toStrictEntity-` - - -.. _Response Transforming Directives: - -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-` - * :ref:`-mapResponseEntity-` - * :ref:`-mapResponseHeaders-` - - -.. _Result Transformation Directives: - -Transforming the RouteResult ----------------------------- - -These directives allow to transform the RouteResult of the inner route. - - * :ref:`-cancelRejection-` - * :ref:`-cancelRejections-` - * :ref:`-mapRejections-` - * :ref:`-mapRouteResult-` - * :ref:`-mapRouteResultFuture-` - * :ref:`-mapRouteResultPF-` - * :ref:`-mapRouteResultWith-` - * :ref:`-mapRouteResultWithPF-` - * :ref:`-recoverRejections-` - * :ref:`-recoverRejectionsWith-` - - -Other ------ - - * :ref:`-mapInnerRoute-` - * :ref:`-pass-` - - -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 - textract - toStrictEntity - tprovide - withExecutionContext - withMaterializer - withLog - withSettings diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapInnerRoute.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapInnerRoute.rst deleted file mode 100644 index 65f234d589..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapInnerRoute.rst +++ /dev/null @@ -1,23 +0,0 @@ -.. _-mapInnerRoute-: - -mapInnerRoute -============= - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: 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` to replace the inner route -with any other route. Usually, the returned route wraps the original one with custom execution logic. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: mapInnerRoute diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapRejections.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapRejections.rst deleted file mode 100644 index d2f29df33f..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapRejections.rst +++ /dev/null @@ -1,27 +0,0 @@ -.. _-mapRejections-: - -mapRejections -============= - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: 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-` directive which provides a nicer DSL for building rejection handlers. - -The ``mapRejections`` directive is used as a building block for :ref:`Custom Directives` to transform a list -of rejections from the inner route to a new list of rejections. - -See :ref:`Response Transforming Directives` for similar directives. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: mapRejections diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapRequest.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapRequest.rst deleted file mode 100644 index 50fba8e1a7..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapRequest.rst +++ /dev/null @@ -1,27 +0,0 @@ -.. _-mapRequest-: - -mapRequest -========== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: 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` 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-` directive. - -See :ref:`Request Transforming Directives` for an overview of similar directives. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: 0mapRequest diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapRequestContext.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapRequestContext.rst deleted file mode 100644 index 3eedd9839a..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapRequestContext.rst +++ /dev/null @@ -1,26 +0,0 @@ -.. _-mapRequestContext-: - -mapRequestContext -================= - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: 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` to transform -the request context before it is passed to the inner route. To change only the request value itself the -:ref:`-mapRequest-` directive can be used instead. - -See :ref:`Request Transforming Directives` for an overview of similar directives. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: mapRequestContext diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapResponse.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapResponse.rst deleted file mode 100644 index 22a2287fee..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapResponse.rst +++ /dev/null @@ -1,31 +0,0 @@ -.. _-mapResponse-: - -mapResponse -=========== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: mapResponse - -Description ------------ - -The ``mapResponse`` directive is used as a building block for :ref:`Custom Directives` to transform a response that -was generated by the inner route. This directive transforms complete responses. - -See also :ref:`-mapResponseHeaders-` or :ref:`-mapResponseEntity-` for more specialized variants and -:ref:`Response Transforming Directives` for similar directives. - -Example: Override status ------------------------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: 0mapResponse - -Example: Default to empty JSON response on errors -------------------------------------------------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: 1mapResponse-advanced \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapResponseEntity.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapResponseEntity.rst deleted file mode 100644 index dcb2cd76e0..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapResponseEntity.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. _-mapResponseEntity-: - -mapResponseEntity -================= - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: mapResponseEntity - -Description ------------ - -The ``mapResponseEntity`` directive is used as a building block for :ref:`Custom Directives` to transform a -response entity that was generated by the inner route. - -See :ref:`Response Transforming Directives` for similar directives. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: mapResponseEntity diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapResponseHeaders.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapResponseHeaders.rst deleted file mode 100644 index c167683c4d..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapResponseHeaders.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. _-mapResponseHeaders-: - -mapResponseHeaders -================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: 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` to transform the list of -response headers that was generated by the inner route. - -See :ref:`Response Transforming Directives` for similar directives. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: mapResponseHeaders diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapRouteResult.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapRouteResult.rst deleted file mode 100644 index a9b8a94333..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapRouteResult.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. _-mapRouteResult-: - -mapRouteResult -============== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: 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` to transform the -:ref:`RouteResult` coming back from the inner route. - -See :ref:`Result Transformation Directives` for similar directives. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: 0mapRouteResult diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapRouteResultFuture.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapRouteResultFuture.rst deleted file mode 100644 index 9b8e35b030..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapRouteResultFuture.rst +++ /dev/null @@ -1,27 +0,0 @@ -.. _-mapRouteResultFuture-: - -mapRouteResultFuture -==================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: mapRouteResultFuture - -Description ------------ - -Asynchronous version of :ref:`-mapRouteResult-`. - -It's similar to :ref:`-mapRouteResultWith-`, however it's ``Future[RouteResult] ⇒ Future[RouteResult]`` -instead of ``RouteResult ⇒ Future[RouteResult]`` which may be useful when combining multiple transformantions -and / or wanting to ``recover`` from a failed route result. - -See :ref:`Result Transformation Directives` for similar directives. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: mapRouteResultFuture diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapRouteResultPF.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapRouteResultPF.rst deleted file mode 100644 index 898bd4c749..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapRouteResultPF.rst +++ /dev/null @@ -1,28 +0,0 @@ -.. _-mapRouteResultPF-: - -mapRouteResultPF -================ - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: mapRouteResultPF - -Description ------------ -*Partial Function* version of :ref:`-mapRouteResult-`. - -Changes the message the inner route sends to the responder. - -The ``mapRouteResult`` directive is used as a building block for :ref:`Custom Directives` to transform the -:ref:`RouteResult` coming back from the inner route. It's similar to the :ref:`-mapRouteResult-` directive but allows to -specify a partial function that doesn't have to handle all potential ``RouteResult`` instances. - -See :ref:`Result Transformation Directives` for similar directives. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: mapRouteResultPF diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapRouteResultWith.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapRouteResultWith.rst deleted file mode 100644 index d51bec559a..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapRouteResultWith.rst +++ /dev/null @@ -1,27 +0,0 @@ -.. _-mapRouteResultWith-: - -mapRouteResultWith -================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: 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` to transform the -:ref:`RouteResult` coming back from the inner route. It's similar to the :ref:`-mapRouteResult-` directive but -returning a ``Future`` instead of a result immediadly, which may be useful for longer running transformations. - -See :ref:`Result Transformation Directives` for similar directives. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: mapRouteResultWith-0 diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapRouteResultWithPF.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapRouteResultWithPF.rst deleted file mode 100644 index 47cdb6a60e..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapRouteResultWithPF.rst +++ /dev/null @@ -1,28 +0,0 @@ -.. _-mapRouteResultWithPF-: - -mapRouteResultWithPF -==================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: mapRouteResultWithPF - -Description ------------ - -Asynchronous variant of :ref:`-mapRouteResultPF-`. - -Changes the message the inner route sends to the responder. - -The ``mapRouteResult`` directive is used as a building block for :ref:`Custom Directives` to transform the -:ref:`RouteResult` coming back from the inner route. - -See :ref:`Result Transformation Directives` for similar directives. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: mapRouteResultWithPF-0 diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapSettings.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapSettings.rst deleted file mode 100644 index 27efee5255..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapSettings.rst +++ /dev/null @@ -1,23 +0,0 @@ -.. _-mapSettings-: - -mapSettings -=========== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: mapSettings - -Description ------------ - -Transforms the ``RoutingSettings`` with a ``RoutingSettings ⇒ RoutingSettings`` function. - -See also :ref:`-withSettings-` or :ref:`-extractSettings-`. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: withSettings-0 diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapUnmatchedPath.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapUnmatchedPath.rst deleted file mode 100644 index ef153c9c0b..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/mapUnmatchedPath.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. _-mapUnmatchedPath-: - -mapUnmatchedPath -================ - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: 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`. You can use it -for implementing custom path matching directives. - -Use ``extractUnmatchedPath`` for extracting the current value of the unmatched path. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: mapUnmatchedPath-example diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/pass.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/pass.rst deleted file mode 100644 index cac3ab8736..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/pass.rst +++ /dev/null @@ -1,22 +0,0 @@ -.. _-pass-: - -pass -==== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: 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 -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: pass diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/provide.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/provide.rst deleted file mode 100644 index 6ef3f54268..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/provide.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. _-provide-: - -provide -======= - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: provide - -Description ------------ -Provides a constant value to the inner route. - -The `provide` directive is used as a building block for :ref:`Custom Directives` to provide a single value to the -inner route. To provide several values use the :ref:`-tprovide-` directive. - -See :ref:`ProvideDirectives` for an overview of similar directives. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: 0provide diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/recoverRejections.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/recoverRejections.rst deleted file mode 100644 index f56f05d032..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/recoverRejections.rst +++ /dev/null @@ -1,28 +0,0 @@ -.. _-recoverRejections-: - -recoverRejections -================= - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: 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-` directive which provides a nicer DSL for building rejection handlers. - -Transforms rejections from the inner route with an ``immutable.Seq[Rejection] ⇒ RouteResult`` function. -A ``RouteResult`` is either a ``Complete(HttpResponse(...))`` or rejections ``Rejected(rejections)``. - -.. note:: - To learn more about how and why rejections work read the :ref:`rejections-scala` section of the documentation. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: recoverRejections diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/recoverRejectionsWith.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/recoverRejectionsWith.rst deleted file mode 100644 index 2e3eee0ece..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/recoverRejectionsWith.rst +++ /dev/null @@ -1,31 +0,0 @@ -.. _-recoverRejectionsWith-: - -recoverRejectionsWith -===================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: 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-` directive which provides a nicer DSL for building rejection handlers. - -Transforms rejections from the inner route with an ``immutable.Seq[Rejection] ⇒ Future[RouteResult]`` function. - -Asynchronous version of :ref:`-recoverRejections-`. - -See :ref:`-recoverRejections-` (the synchronous equivalent of this directive) for a detailed description. - -.. note:: - To learn more about how and why rejections work read the :ref:`rejections-scala` section of the documentation. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: recoverRejectionsWith diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/textract.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/textract.rst deleted file mode 100644 index a4ef8d37bc..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/textract.rst +++ /dev/null @@ -1,30 +0,0 @@ -.. _-textract-: - -textract -======== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: textract - -Description ------------ -Extracts a tuple of values from the request context and provides them to the inner route. - - -The ``textract`` directive is used as a building block for :ref:`Custom Directives` to extract data from the -``RequestContext`` and provide it to the inner route. To extract just one value use the :ref:`-extract-` directive. To -provide a constant value independent of the ``RequestContext`` use the :ref:`-tprovide-` directive instead. - -See :ref:`ProvideDirectives` for an overview of similar directives. - -See also :ref:`-extract-` for extracting a single value. - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: textract diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/toStrictEntity.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/toStrictEntity.rst deleted file mode 100644 index f45a9ac048..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/toStrictEntity.rst +++ /dev/null @@ -1,30 +0,0 @@ -.. _-toStrictEntity-: - -toStrictEntity -============== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: 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-` or :ref:`-withoutSizeLimit-` directive. - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: toStrictEntity-example diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/tprovide.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/tprovide.rst deleted file mode 100644 index 571a2de8cf..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/tprovide.rst +++ /dev/null @@ -1,30 +0,0 @@ -.. _-tprovide-: - -tprovide -======== - - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: tprovide - -Description ------------ - -Provides a tuple of values to the inner route. - -The ``tprovide`` directive is used as a building block for :ref:`Custom Directives` to provide data to the inner route. -To provide just one value use the :ref:`-provide-` directive. If you want to provide values calculated from the -``RequestContext`` use the :ref:`-textract-` directive instead. - -See :ref:`ProvideDirectives` for an overview of similar directives. - -See also :ref:`-provide-` for providing a single value. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: tprovide diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/withExecutionContext.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/withExecutionContext.rst deleted file mode 100644 index db7e57c422..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/withExecutionContext.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. _-withExecutionContext-: - -withExecutionContext -==================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: withExecutionContext - -Description ------------ - -Allows running an inner route using an alternative ``ExecutionContext`` in place of the default one. - -The execution context can be extracted in an inner route using :ref:`-extractExecutionContext-` directly, -or used by directives which internally extract the materializer without sufracing this fact in the API. - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: withExecutionContext-0 diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/withLog.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/withLog.rst deleted file mode 100644 index 7956e8c024..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/withLog.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. _-withLog-: - -withLog -======= - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: withLog - -Description ------------ - -Allows running an inner route using an alternative :class:`LoggingAdapter` in place of the default one. - -The logging adapter can be extracted in an inner route using :ref:`-extractLog-` directly, -or used by directives which internally extract the materializer without sufracing this fact in the API. - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: 0withLog diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/withMaterializer.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/withMaterializer.rst deleted file mode 100644 index a6c2995286..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/withMaterializer.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. _-withMaterializer-: - -withMaterializer -================ - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: withMaterializer - -Description ------------ - -Allows running an inner route using an alternative ``Materializer`` in place of the default one. - -The materializer can be extracted in an inner route using :ref:`-extractMaterializer-` directly, -or used by directives which internally extract the materializer without sufracing this fact in the API -(e.g. responding with a Chunked entity). - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: withMaterializer-0 diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/withSettings.rst b/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/withSettings.rst deleted file mode 100644 index 148589815c..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/basic-directives/withSettings.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. _-withSettings-: - -withSettings -============ - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala - :snippet: withSettings - -Description ------------ - -Allows running an inner route using an alternative :class:`RoutingSettings` in place of the default one. - -The execution context can be extracted in an inner route using :ref:`-extractSettings-` directly, -or used by directives which internally extract the materializer without sufracing this fact in the API. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala - :snippet: withSettings-0 diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/by-trait.rst b/akka-docs/rst/scala/http/routing-dsl/directives/by-trait.rst deleted file mode 100644 index 1d2b08062d..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/by-trait.rst +++ /dev/null @@ -1,110 +0,0 @@ -Predefined Directives (by trait) -================================ - -All predefined directives are organized into traits that form one part of the overarching ``Directives`` trait. - -.. _Request Directives: - -Directives filtering or extracting from the request ---------------------------------------------------- - -:ref:`MethodDirectives` - Filter and extract based on the request method. - -:ref:`HeaderDirectives` - Filter and extract based on request headers. - -:ref:`PathDirectives` - Filter and extract from the request URI path. - -:ref:`HostDirectives` - Filter and extract based on the target host. - -:ref:`ParameterDirectives`, :ref:`FormFieldDirectives` - Filter and extract based on query parameters or form fields. - -:ref:`CodingDirectives` - Filter and decode compressed request content. - -:ref:`MarshallingDirectives` - Extract the request entity. - -:ref:`SchemeDirectives` - Filter and extract based on the request scheme. - -:ref:`SecurityDirectives` - Handle authentication data from the request. - -:ref:`CookieDirectives` - Filter and extract cookies. - -:ref:`BasicDirectives` and :ref:`MiscDirectives` - Directives handling request properties. - -:ref:`FileUploadDirectives` - Handle file uploads. - - -.. _Response Directives: - -Directives creating or transforming the response ------------------------------------------------- - -:ref:`CacheConditionDirectives` - Support for conditional requests (``304 Not Modified`` responses). - -:ref:`CookieDirectives` - Set, modify, or delete cookies. - -:ref:`CodingDirectives` - Compress responses. - -:ref:`FileAndResourceDirectives` - Deliver responses from files and resources. - -:ref:`RangeDirectives` - Support for range requests (``206 Partial Content`` responses). - -:ref:`RespondWithDirectives` - Change response properties. - -:ref:`RouteDirectives` - Complete or reject a request with a response. - -:ref:`BasicDirectives` and :ref:`MiscDirectives` - Directives handling or transforming response properties. - -:ref:`TimeoutDirectives` - Configure request timeouts and automatic timeout responses. - - -List of predefined directives by trait --------------------------------------- - -.. toctree:: - :maxdepth: 1 - - basic-directives/index - cache-condition-directives/index - coding-directives/index - cookie-directives/index - debugging-directives/index - execution-directives/index - file-and-resource-directives/index - file-upload-directives/index - form-field-directives/index - future-directives/index - header-directives/index - host-directives/index - marshalling-directives/index - method-directives/index - misc-directives/index - parameter-directives/index - path-directives/index - range-directives/index - respond-with-directives/index - route-directives/index - scheme-directives/index - security-directives/index - websocket-directives/index - timeout-directives/index diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/cache-condition-directives/conditional.rst b/akka-docs/rst/scala/http/routing-dsl/directives/cache-condition-directives/conditional.rst deleted file mode 100644 index c456854f84..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/cache-condition-directives/conditional.rst +++ /dev/null @@ -1,40 +0,0 @@ -.. _-conditional-: - -conditional -=========== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/CacheConditionDirectives.scala - :snippet: conditional - - -Description ------------ - -Wraps its inner route with support for Conditional Requests as defined -by http://tools.ietf.org/html/draft-ietf-httpbis-p4-conditional-26. - - -Depending on the given ``eTag`` and ``lastModified`` values this directive immediately responds with -``304 Not Modified`` or ``412 Precondition Failed`` (without calling its inner route) if the request comes with the -respective conditional headers. Otherwise the request is simply passed on to its inner route. - -The algorithm implemented by this directive closely follows what is defined in `this section`__ of the -`HTTPbis spec`__. - -All responses (the ones produces by this directive itself as well as the ones coming back from the inner route) are -augmented with respective ``ETag`` and ``Last-Modified`` response headers. - -Since this directive requires the ``EntityTag`` and ``lastModified`` time stamp for the resource as concrete arguments -it is usually used quite deep down in the route structure (i.e. close to the leaf-level), where the exact resource -targeted by the request has already been established and the respective ETag/Last-Modified values can be determined. - - -The :ref:`FileAndResourceDirectives` internally use the ``conditional`` directive for ETag and Last-Modified support -(if the ``akka.http.routing.file-get-conditional`` setting is enabled). - -__ http://tools.ietf.org/html/draft-ietf-httpbis-p4-conditional-26#section-6 -__ https://datatracker.ietf.org/wg/httpbis/ - diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/cache-condition-directives/index.rst b/akka-docs/rst/scala/http/routing-dsl/directives/cache-condition-directives/index.rst deleted file mode 100644 index dc556082cd..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/cache-condition-directives/index.rst +++ /dev/null @@ -1,9 +0,0 @@ -.. _CacheConditionDirectives: - -CacheConditionDirectives -======================== - -.. toctree:: - :maxdepth: 1 - - conditional \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/coding-directives/decodeRequest.rst b/akka-docs/rst/scala/http/routing-dsl/directives/coding-directives/decodeRequest.rst deleted file mode 100644 index 02c64179ab..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/coding-directives/decodeRequest.rst +++ /dev/null @@ -1,21 +0,0 @@ -.. _-decodeRequest-: - -decodeRequest -============= - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/CodingDirectives.scala - :snippet: decodeRequest - -Description ------------ - -Decompresses the incoming request if it is ``gzip`` or ``deflate`` compressed. Uncompressed requests are passed through untouched. If the request encoded with another encoding the request is rejected with an ``UnsupportedRequestEncodingRejection``. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/CodingDirectivesExamplesSpec.scala - :snippet: "decodeRequest" diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/coding-directives/decodeRequestWith.rst b/akka-docs/rst/scala/http/routing-dsl/directives/coding-directives/decodeRequestWith.rst deleted file mode 100644 index 28e028a4d9..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/coding-directives/decodeRequestWith.rst +++ /dev/null @@ -1,21 +0,0 @@ -.. _-decodeRequestWith-: - -decodeRequestWith -================= - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/CodingDirectives.scala - :snippet: decodeRequestWith - -Description ------------ - -Decodes the incoming request if it is encoded with one of the given encoders. If the request encoding doesn't match one of the given encoders the request is rejected with an ``UnsupportedRequestEncodingRejection``. If no decoders are given the default encoders (``Gzip``, ``Deflate``, ``NoCoding``) are used. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/CodingDirectivesExamplesSpec.scala - :snippet: decodeRequestWith diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/coding-directives/encodeResponse.rst b/akka-docs/rst/scala/http/routing-dsl/directives/coding-directives/encodeResponse.rst deleted file mode 100644 index b9e3f1a40e..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/coding-directives/encodeResponse.rst +++ /dev/null @@ -1,27 +0,0 @@ -.. _-encodeResponse-: - -encodeResponse -============== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/CodingDirectives.scala - :snippet: encodeResponse - -Description ------------ - -Encodes the response with the encoding that is requested by the client via the ``Accept-Encoding`` header or rejects the request with an ``UnacceptedResponseEncodingRejection(supportedEncodings)``. - -The response encoding is determined by the rules specified in RFC7231_. - -If the ``Accept-Encoding`` header is missing or empty or specifies an encoding other than identity, gzip or deflate then no encoding is used. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/CodingDirectivesExamplesSpec.scala - :snippet: "encodeResponse" - -.. _RFC7231: http://tools.ietf.org/html/rfc7231#section-5.3.4 diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/coding-directives/encodeResponseWith.rst b/akka-docs/rst/scala/http/routing-dsl/directives/coding-directives/encodeResponseWith.rst deleted file mode 100644 index 725fbdbfb6..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/coding-directives/encodeResponseWith.rst +++ /dev/null @@ -1,30 +0,0 @@ -.. _-encodeResponseWith-: - -encodeResponseWith -================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/CodingDirectives.scala - :snippet: encodeResponseWith - -Description ------------ - -Encodes the response with the encoding that is requested by the client via the ``Accept-Encoding`` if it is among the provided encoders or rejects the request with an ``UnacceptedResponseEncodingRejection(supportedEncodings)``. - -The response encoding is determined by the rules specified in RFC7231_. - -If the ``Accept-Encoding`` header is missing then the response is encoded using the ``first`` encoder. - -If the ``Accept-Encoding`` header is empty and ``NoCoding`` is part of the encoders then no -response encoding is used. Otherwise the request is rejected. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/CodingDirectivesExamplesSpec.scala - :snippet: encodeResponseWith - -.. _RFC7231: http://tools.ietf.org/html/rfc7231#section-5.3.4 diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/coding-directives/index.rst b/akka-docs/rst/scala/http/routing-dsl/directives/coding-directives/index.rst deleted file mode 100644 index c4661495e0..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/coding-directives/index.rst +++ /dev/null @@ -1,14 +0,0 @@ -.. _CodingDirectives: - -CodingDirectives -================ - -.. toctree:: - :maxdepth: 1 - - decodeRequest - decodeRequestWith - encodeResponse - encodeResponseWith - requestEncodedWith - responseEncodingAccepted \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/coding-directives/requestEncodedWith.rst b/akka-docs/rst/scala/http/routing-dsl/directives/coding-directives/requestEncodedWith.rst deleted file mode 100644 index b388ac23e5..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/coding-directives/requestEncodedWith.rst +++ /dev/null @@ -1,19 +0,0 @@ -.. _-requestEncodedWith-: - -requestEncodedWith -================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/CodingDirectives.scala - :snippet: requestEncodedWith - -Description ------------ - -Passes the request to the inner route if the request is encoded with the argument encoding. Otherwise, rejects the request with an ``UnacceptedRequestEncodingRejection(encoding)``. - -This directive is the `building block`_ for ``decodeRequest`` to reject unsupported encodings. - -.. _`building block`: @github@/akka-http/src/main/scala/akka/http/scaladsl/server/directives/CodingDirectives.scala diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/coding-directives/responseEncodingAccepted.rst b/akka-docs/rst/scala/http/routing-dsl/directives/coding-directives/responseEncodingAccepted.rst deleted file mode 100644 index 6096f5432a..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/coding-directives/responseEncodingAccepted.rst +++ /dev/null @@ -1,21 +0,0 @@ -.. _-responseEncodingAccepted-: - -responseEncodingAccepted -======================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/CodingDirectives.scala - :snippet: responseEncodingAccepted - -Description ------------ - -Passes the request to the inner route if the request accepts the argument encoding. Otherwise, rejects the request with an ``UnacceptedResponseEncodingRejection(encoding)``. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/CodingDirectivesExamplesSpec.scala - :snippet: responseEncodingAccepted diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/cookie-directives/cookie.rst b/akka-docs/rst/scala/http/routing-dsl/directives/cookie-directives/cookie.rst deleted file mode 100644 index f75dcab233..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/cookie-directives/cookie.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. _-cookie-: - -cookie -====== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/CookieDirectives.scala - :snippet: cookie - -Description ------------ -Extracts a cookie with a given name from a request or otherwise rejects the request with a ``MissingCookieRejection`` if -the cookie is missing. - -Use the :ref:`-optionalCookie-` directive instead if you want to support missing cookies in your inner route. - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/CookieDirectivesExamplesSpec.scala - :snippet: cookie diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/cookie-directives/deleteCookie.rst b/akka-docs/rst/scala/http/routing-dsl/directives/cookie-directives/deleteCookie.rst deleted file mode 100644 index 3414abbf1a..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/cookie-directives/deleteCookie.rst +++ /dev/null @@ -1,22 +0,0 @@ -.. _-deleteCookie-: - -deleteCookie -============ - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/CookieDirectives.scala - :snippet: deleteCookie - -Description ------------ -Adds a header to the response to request the removal of the cookie with the given name on the client. - -Use the :ref:`-setCookie-` directive to update a cookie. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/CookieDirectivesExamplesSpec.scala - :snippet: deleteCookie diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/cookie-directives/index.rst b/akka-docs/rst/scala/http/routing-dsl/directives/cookie-directives/index.rst deleted file mode 100644 index 7ca1aba3fd..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/cookie-directives/index.rst +++ /dev/null @@ -1,12 +0,0 @@ -.. _CookieDirectives: - -CookieDirectives -================ - -.. toctree:: - :maxdepth: 1 - - cookie - deleteCookie - optionalCookie - setCookie diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/cookie-directives/optionalCookie.rst b/akka-docs/rst/scala/http/routing-dsl/directives/cookie-directives/optionalCookie.rst deleted file mode 100644 index b880fa53a0..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/cookie-directives/optionalCookie.rst +++ /dev/null @@ -1,23 +0,0 @@ -.. _-optionalCookie-: - -optionalCookie -============== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/CookieDirectives.scala - :snippet: optionalCookie - -Description ------------ -Extracts an optional cookie with a given name from a request. - -Use the :ref:`-cookie-` directive instead if the inner route does not handle a missing cookie. - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/CookieDirectivesExamplesSpec.scala - :snippet: optionalCookie diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/cookie-directives/setCookie.rst b/akka-docs/rst/scala/http/routing-dsl/directives/cookie-directives/setCookie.rst deleted file mode 100644 index 0651e64e74..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/cookie-directives/setCookie.rst +++ /dev/null @@ -1,23 +0,0 @@ -.. _-setCookie-: - -setCookie -========= - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/CookieDirectives.scala - :snippet: setCookie - -Description ------------ -Adds a header to the response to request the update of the cookie with the given name on the client. - -Use the :ref:`-deleteCookie-` directive to delete a cookie. - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/CookieDirectivesExamplesSpec.scala - :snippet: setCookie diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/custom-directives.rst b/akka-docs/rst/scala/http/routing-dsl/directives/custom-directives.rst deleted file mode 100644 index e45901cb58..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/custom-directives.rst +++ /dev/null @@ -1,196 +0,0 @@ -.. _Custom Directives: - -Custom Directives -================= - -Part of the power of akka-http directives comes from the ease with which it’s possible to define -custom directives at differing levels of abstraction. - -There are essentially three ways of creating custom directives: - -1. By introducing new “labels” for configurations of existing directives -2. By transforming existing directives -3. By writing a directive “from scratch” - -Configuration Labeling -______________________ -The easiest way to create a custom directive is to simply assign a new name for a certain configuration -of one or more existing directives. In fact, most of the predefined akka-http directives can be considered -named configurations of more low-level directives. - -The basic technique is explained in the chapter about Composing Directives, where, for example, a new directive -``getOrPut`` is defined like this: - -.. includecode2:: ../../../code/docs/http/scaladsl/server/directives/CustomDirectivesExamplesSpec.scala - :snippet: labeling - -Another example is the :ref:`MethodDirectives` which are simply instances of a preconfigured :ref:`-method-` directive. -The low-level directives that most often form the basis of higher-level “named configuration” directives are grouped -together in the :ref:`BasicDirectives` trait. - - -Transforming Directives -_______________________ - -The second option for creating new directives is to transform an existing one using one of the -“transformation methods”, which are defined on the `Directive`__ class, the base class of all “regular” directives. - -__ @github@/akka-http/src/main/scala/akka/http/scaladsl/server/Directive.scala - -Apart from the combinator operators (``|`` and ``&``) and the case-class extractor (``as[T]``) -there following transformations is also defined on all ``Directive`` instances: - - * :ref:`map/tmap` - * :ref:`flatMap/tflatMap` - * :ref:`require/trequire` - * :ref:`recover/recoverPF` - -.. _map/tmap: - -map and tmap ------------- -If the Directive is a single-value ``Directive``, the ``map`` method allows -for simple transformations: - -.. includecode2:: ../../../code/docs/http/scaladsl/server/directives/CustomDirectivesExamplesSpec.scala - :snippet: map-0 - -One example of a predefined directive relying on ``map`` is the `optionalHeaderValue`__ directive. - -__ @github@/akka-http/src/main/scala/akka/http/scaladsl/server/directives/HeaderDirectives.scala#L67 - -The tmap modifier has this signature (somewhat simplified):: - - def tmap[R](f: L ⇒ R): Directive[Out] - -It can be used to transform the ``Tuple`` of extractions into another ``Tuple``. -The number and/or types of the extractions can be changed arbitrarily. For example -if ``R`` is ``Tuple2[A, B]`` then the result will be a ``Directive[(A, B)]``. Here is a -somewhat contrived example: - -.. includecode2:: ../../../code/docs/http/scaladsl/server/directives/CustomDirectivesExamplesSpec.scala - :snippet: tmap-1 - - - -.. _flatMap/tflatMap: - -flatMap and tflatMap --------------------- - -With map and tmap you can transform the values a directive extracts -but you cannot change the “extracting” nature of the directive. -For example, if you have a directive extracting an ``Int`` you can use map to turn -it into a directive that extracts that ``Int`` and doubles it, but you cannot transform -it into a directive, that doubles all positive ``Int`` values and rejects all others. - -In order to do the latter you need ``flatMap`` or ``tflatMap``. The ``tflatMap`` -modifier has this signature:: - - def tflatMap[R: Tuple](f: L ⇒ Directive[R]): Directive[R] - -The given function produces a new directive depending on the Tuple of extractions -of the underlying one. As in the case of :ref:`map/tmap` there is also a single-value -variant called ``flatMap``, which simplifies the operation for Directives only extracting one single value. - -Here is the (contrived) example from above, which doubles positive Int values and rejects all others: - -.. includecode2:: ../../../code/docs/http/scaladsl/server/directives/CustomDirectivesExamplesSpec.scala - :snippet: flatMap-0 - -A common pattern that relies on flatMap is to first extract a value -from the RequestContext with the extract directive and then flatMap with -some kind of filtering logic. For example, this is the implementation -of the method directive: - -.. includecode2:: ../../../../../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/MethodDirectives.scala - :snippet: method - -The explicit type parameter ``[Unit]`` on the flatMap i`s needed in this case -because the result of the flatMap is directly concatenated with the -``cancelAllRejections`` directive, thereby preventing “outside-in” -inference of the type parameter value. - -.. _require/trequire: - -require and trequire --------------------- - -The require modifier transforms a single-extraction directive into a directive -without extractions, which filters the requests according the a predicate function. -All requests, for which the predicate is false are rejected, all others pass unchanged. - -The signature of require is this:: - - def require(predicate: T ⇒ Boolean, rejections: Rejection*): Directive0 - -One example of a predefined directive relying on require is the first overload of the host directive: - -.. includecode2:: ../../../../../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/HostDirectives.scala - :snippet: require-host - -You can only call require on single-extraction directives. The trequire modifier is the -more general variant, which takes a predicate of type ``Tuple => Boolean``. -It can therefore also be used on directives with several extractions. - - -.. _recover/recoverPF: - -recover and recoverPF ---------------------- - -The ``recover`` modifier allows you “catch” rejections produced by the underlying -directive and, instead of rejecting, produce an alternative directive with the same type(s) of extractions. - -The signature of recover is this:: - - def recover[R >: L: Tuple](recovery: Seq[Rejection] ⇒ Directive[R]): Directive[R] = - -In many cases the very similar ``recoverPF`` modifier might be little bit -easier to use since it doesn’t require the handling of all rejections:: - - def recoverPF[R >: L: Tuple]( - recovery: PartialFunction[Seq[Rejection], Directive[R]]): Directive[R] - - -One example of a predefined directive relying ``recoverPF`` is the optionalHeaderValue directive: - -.. includecode2:: ../../../../../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/HeaderDirectives.scala - :snippet: optional-header - - - -Directives from Scratch -_______________________ - -The third option for creating custom directives is to do it “from scratch”, -by directly subclassing the Directive class. The Directive is defined like this -(leaving away operators and modifiers): - -.. includecode2:: ../../../../../../akka-http/src/main/scala/akka/http/scaladsl/server/Directive.scala - :snippet: basic - -It only has one abstract member that you need to implement, the happly method, which creates -the Route the directives presents to the outside from its inner Route building function -(taking the extractions as parameter). - -Extractions are kept as a Tuple. Here are a few examples: - -A ``Directive[Unit]`` extracts nothing (like the get directive). -Because this type is used quite frequently akka-http defines a type alias for it:: - - type Directive0 = Directive[Unit] - -A ``Directive[(String)]`` extracts one String value (like the hostName directive). The type alias for it is:: - - type Directive1[T] = Directive[Tuple1[T]] - -A Directive[(Int, String)] extracts an ``Int`` value and a ``String`` value -(like a ``parameters('a.as[Int], 'b.as[String])`` directive). - -Keeping extractions as ``Tuples`` has a lot of advantages, mainly great flexibility -while upholding full type safety and “inferability”. However, the number of times -where you’ll really have to fall back to defining a directive from scratch should -be very small. In fact, if you find yourself in a position where a “from scratch” -directive is your only option, we’d like to hear about it, -so we can provide a higher-level “something” for other users. diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/debugging-directives/index.rst b/akka-docs/rst/scala/http/routing-dsl/directives/debugging-directives/index.rst deleted file mode 100644 index cec8a165e4..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/debugging-directives/index.rst +++ /dev/null @@ -1,11 +0,0 @@ -.. _DebuggingDirectives: - -DebuggingDirectives -=================== - -.. toctree:: - :maxdepth: 1 - - logRequest - logRequestResult - logResult diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/debugging-directives/logRequest.rst b/akka-docs/rst/scala/http/routing-dsl/directives/debugging-directives/logRequest.rst deleted file mode 100644 index 2cab1bd701..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/debugging-directives/logRequest.rst +++ /dev/null @@ -1,44 +0,0 @@ -.. _-logRequest-: - -logRequest -========== - -Signature ---------- - -:: - - def logRequest(marker: String)(implicit log: LoggingContext): Directive0 - def logRequest(marker: String, level: LogLevel)(implicit log: LoggingContext): Directive0 - def logRequest(show: HttpRequest => String)(implicit log: LoggingContext): Directive0 - def logRequest(show: HttpRequest => LogEntry)(implicit log: LoggingContext): Directive0 - def logRequest(magnet: LoggingMagnet[HttpRequest => Unit])(implicit log: LoggingContext): Directive0 - -The signature shown is simplified, the real signature uses magnets. [1]_ - -.. [1] See `The Magnet Pattern`_ for an explanation of magnet-based overloading. -.. _`The Magnet Pattern`: http://spray.io/blog/2012-12-13-the-magnet-pattern/ - -Description ------------ - -Logs the request using the supplied ``LoggingMagnet[HttpRequest => Unit]``. This ``LoggingMagnet`` is a wrapped -function ``HttpRequest => Unit`` that can be implicitly created from the different constructors shown above. These -constructors build a ``LoggingMagnet`` from these components: - - * A marker to prefix each log message with. - * A log level. - * A ``show`` function that calculates a string representation for a request. - * An implicit ``LoggingContext`` that is used to emit the log message. - * A function that creates a ``LogEntry`` which is a combination of the elements above. - -It is also possible to use any other function ``HttpRequest => Unit`` for logging by wrapping it with ``LoggingMagnet``. -See the examples for ways to use the ``logRequest`` directive. - -Use ``logResult`` for logging the response, or ``logRequestResult`` for logging both. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/DebuggingDirectivesExamplesSpec.scala - :snippet: logRequest-0 diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/debugging-directives/logRequestResult.rst b/akka-docs/rst/scala/http/routing-dsl/directives/debugging-directives/logRequestResult.rst deleted file mode 100644 index 532f3c1192..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/debugging-directives/logRequestResult.rst +++ /dev/null @@ -1,42 +0,0 @@ -.. _-logRequestResult-: - -logRequestResult -================ - -Signature ---------- - -:: - - def logRequestResult(marker: String)(implicit log: LoggingContext): Directive0 - def logRequestResult(marker: String, level: LogLevel)(implicit log: LoggingContext): Directive0 - def logRequestResult(show: HttpRequest => RouteResult => Option[LogEntry])(implicit log: LoggingContext): Directive0 - -The signature shown is simplified, the real signature uses magnets. [1]_ - -.. [1] See `The Magnet Pattern`_ for an explanation of magnet-based overloading. -.. _`The Magnet Pattern`: http://spray.io/blog/2012-12-13-the-magnet-pattern/ - -Description ------------ -Logs both, the request and the response. - -This directive is a combination of :ref:`-logRequest-` and :ref:`-logResult-`. - -See :ref:`-logRequest-` for the general description how these directives work. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/DebuggingDirectivesExamplesSpec.scala - :snippet: logRequestResult - - -Building Advanced Directives ----------------------------- - -This example will showcase the advanced logging using the ``DebuggingDirectives``. -The built `logResponseTime` directive will log the request time (or rejection reason): - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/DebuggingDirectivesExamplesSpec.scala - :snippet: logRequestResultWithResponseTime diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/debugging-directives/logResult.rst b/akka-docs/rst/scala/http/routing-dsl/directives/debugging-directives/logResult.rst deleted file mode 100644 index 17403bb018..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/debugging-directives/logResult.rst +++ /dev/null @@ -1,36 +0,0 @@ -.. _-logResult-: - -logResult -========= - -Signature ---------- - -:: - - def logResult(marker: String)(implicit log: LoggingContext): Directive0 - def logResult(marker: String, level: LogLevel)(implicit log: LoggingContext): Directive0 - def logResult(show: RouteResult => String)(implicit log: LoggingContext): Directive0 - def logResult(show: RouteResult => LogEntry)(implicit log: LoggingContext): Directive0 - def logResult(magnet: LoggingMagnet[RouteResult => Unit])(implicit log: LoggingContext): Directive0 - -The signature shown is simplified, the real signature uses magnets. [1]_ - -.. [1] See `The Magnet Pattern`_ for an explanation of magnet-based overloading. -.. _`The Magnet Pattern`: http://spray.io/blog/2012-12-13-the-magnet-pattern/ - -Description ------------ -Logs the response. - -See :ref:`-logRequest-` for the general description how these directives work. This directive is different -as it requires a ``LoggingMagnet[RouteResult => Unit]``. Instead of just logging ``HttpResponses``, ``logResult`` is able to -log any :ref:`RouteResult` coming back from the inner route. - -Use ``logRequest`` for logging the request, or ``logRequestResult`` for logging both. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/DebuggingDirectivesExamplesSpec.scala - :snippet: logResult diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/execution-directives/handleExceptions.rst b/akka-docs/rst/scala/http/routing-dsl/directives/execution-directives/handleExceptions.rst deleted file mode 100644 index 9735e77277..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/execution-directives/handleExceptions.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. _-handleExceptions-: - -handleExceptions -================ - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/ExecutionDirectives.scala - :snippet: handleExceptions - -Description ------------ -Catches exceptions thrown by the inner route and handles them using the specified ``ExceptionHandler``. - -Using this directive is an alternative to using a global implicitly defined ``ExceptionHandler`` that -applies to the complete route. - -See :ref:`exception-handling-scala` for general information about options for handling exceptions. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/ExecutionDirectivesExamplesSpec.scala - :snippet: handleExceptions diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/execution-directives/handleRejections.rst b/akka-docs/rst/scala/http/routing-dsl/directives/execution-directives/handleRejections.rst deleted file mode 100644 index c0e34c8d72..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/execution-directives/handleRejections.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. _-handleRejections-: - -handleRejections -================ - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/ExecutionDirectives.scala - :snippet: handleRejections - -Description ------------ - -Using this directive is an alternative to using a global implicitly defined ``RejectionHandler`` that -applies to the complete route. - -See :ref:`rejections-scala` for general information about options for handling rejections. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/ExecutionDirectivesExamplesSpec.scala - :snippet: handleRejections diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/execution-directives/index.rst b/akka-docs/rst/scala/http/routing-dsl/directives/execution-directives/index.rst deleted file mode 100644 index facc98d2c4..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/execution-directives/index.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. _ExecutionDirectives: - -ExecutionDirectives -=================== - -.. toctree:: - :maxdepth: 1 - - handleExceptions - handleRejections diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/file-and-resource-directives/getFromBrowseableDirectories.rst b/akka-docs/rst/scala/http/routing-dsl/directives/file-and-resource-directives/getFromBrowseableDirectories.rst deleted file mode 100644 index 245d7adf0d..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/file-and-resource-directives/getFromBrowseableDirectories.rst +++ /dev/null @@ -1,30 +0,0 @@ -.. _-getFromBrowseableDirectories-: - -getFromBrowseableDirectories -============================ - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/FileAndResourceDirectives.scala - :snippet: getFromBrowseableDirectories - -Description ------------ - -The ``getFromBrowseableDirectories`` is a combination of serving files from the specified directories -(like ``getFromDirectory``) and listing a browseable directory with ``listDirectoryContents``. - -Nesting this directive beneath ``get`` is not necessary as this directive will only respond to ``GET`` requests. - -Use ``getFromBrowseableDirectory`` to serve only one directory. - -Use ``getFromDirectory`` if directory browsing isn't required. - -For more details refer to :ref:`-getFromBrowseableDirectory-`. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/FileAndResourceDirectivesExamplesSpec.scala - :snippet: getFromBrowseableDirectories-examples diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/file-and-resource-directives/getFromBrowseableDirectory.rst b/akka-docs/rst/scala/http/routing-dsl/directives/file-and-resource-directives/getFromBrowseableDirectory.rst deleted file mode 100644 index 126860f912..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/file-and-resource-directives/getFromBrowseableDirectory.rst +++ /dev/null @@ -1,51 +0,0 @@ -.. _-getFromBrowseableDirectory-: - -getFromBrowseableDirectory -========================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/FileAndResourceDirectives.scala - :snippet: getFromBrowseableDirectory - -Description ------------ - -The ``getFromBrowseableDirectories`` is a combination of serving files from the specified directories (like -``getFromDirectory``) and listing a browseable directory with ``listDirectoryContents``. - -Nesting this directive beneath ``get`` is not necessary as this directive will only respond to ``GET`` requests. - -Use ``getFromBrowseableDirectory`` to serve only one directory. - -Use ``getFromDirectory`` if directory browsing isn't required. - -For more details refer to :ref:`-getFromBrowseableDirectory-`. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/FileAndResourceDirectivesExamplesSpec.scala - :snippet: getFromBrowseableDirectory-examples - - -Default file listing page example -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Directives which list directories (e.g. ``getFromBrowsableDirectory``) use an implicit ``DirectoryRenderer`` -instance to perfm the actual rendering of the file listing. This rendered can be easily overriden by simply -providing one in-scope for the directives to use, so you can build your custom directory listings. - - -The default renderer is ``akka.http.scaladsl.server.directives.FileAndResourceDirectives.defaultDirectoryRenderer``, -and renders a listing which looks like this: - -.. figure:: ../../../../../images/akka-http-file-listing.png - :scale: 75% - :align: center - - Example page rendered by the ``defaultDirectoryRenderer``. - -It's possible to turn off rendering the footer stating which version of Akka HTTP is rendering this page by configuring -the ``akka.http.routing.render-vanity-footer`` configuration option to ``off``. \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/file-and-resource-directives/getFromDirectory.rst b/akka-docs/rst/scala/http/routing-dsl/directives/file-and-resource-directives/getFromDirectory.rst deleted file mode 100644 index f5db74de59..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/file-and-resource-directives/getFromDirectory.rst +++ /dev/null @@ -1,38 +0,0 @@ -.. _-getFromDirectory-: - -getFromDirectory -================ - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/FileAndResourceDirectives.scala - :snippet: getFromDirectory - -Description ------------ - -Allows exposing a directory's files for GET requests for its contents. - -The ``unmatchedPath`` (see :ref:`-extractUnmatchedPath-`) of the ``RequestContext`` is first transformed by -the given ``pathRewriter`` function, before being appended to the given directory name to build the final file name. - -To serve a single file use :ref:`-getFromFile-`. -To serve browsable directory listings use :ref:`-getFromBrowseableDirectories-`. -To serve files from a classpath directory use :ref:`-getFromResourceDirectory-` instead. - -Note that it's not required to wrap this directive with ``get`` as this directive will only respond to ``GET`` requests. - -.. note:: - The file's contents will be read using an Akka Streams `Source` which *automatically uses - a pre-configured dedicated blocking io dispatcher*, which separates the blocking file operations from the rest of the stream. - - Note also that thanks to using Akka Streams internally, the file will be served at the highest speed reachable by - the client, and not faster – i.e. the file will *not* end up being loaded in full into memory before writing it to - the client. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/FileAndResourceDirectivesExamplesSpec.scala - :snippet: getFromDirectory-examples \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/file-and-resource-directives/getFromFile.rst b/akka-docs/rst/scala/http/routing-dsl/directives/file-and-resource-directives/getFromFile.rst deleted file mode 100644 index f9734087eb..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/file-and-resource-directives/getFromFile.rst +++ /dev/null @@ -1,38 +0,0 @@ -.. _-getFromFile-: - -getFromFile -=========== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/FileAndResourceDirectives.scala - :snippet: getFromFile - -Description ------------ - -Allows exposing a file to be streamed to the client issuing the request. - -The ``unmatchedPath`` (see :ref:`-extractUnmatchedPath-`) of the ``RequestContext`` is first transformed by -the given ``pathRewriter`` function, before being appended to the given directory name to build the final file name. - -To files from a given directory use :ref:`-getFromDirectory-`. -To serve browsable directory listings use :ref:`-getFromBrowseableDirectories-`. -To serve files from a classpath directory use :ref:`-getFromResourceDirectory-` instead. - -Note that it's not required to wrap this directive with ``get`` as this directive will only respond to ``GET`` requests. - -.. note:: - The file's contents will be read using an Akka Streams `Source` which *automatically uses - a pre-configured dedicated blocking io dispatcher*, which separates the blocking file operations from the rest of the stream. - - Note also that thanks to using Akka Streams internally, the file will be served at the highest speed reachable by - the client, and not faster – i.e. the file will *not* end up being loaded in full into memory before writing it to - the client. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/FileAndResourceDirectivesExamplesSpec.scala - :snippet: getFromFile-examples diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/file-and-resource-directives/getFromResource.rst b/akka-docs/rst/scala/http/routing-dsl/directives/file-and-resource-directives/getFromResource.rst deleted file mode 100644 index 559ac9c962..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/file-and-resource-directives/getFromResource.rst +++ /dev/null @@ -1,26 +0,0 @@ -.. _-getFromResource-: - -getFromResource -=============== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/FileAndResourceDirectives.scala - :snippet: getFromResource - -Description ------------ - -Completes GET requests with the content of the given classpath resource. - -For details refer to :ref:`-getFromFile-` which works the same way but obtaining the file from the filesystem -instead of the applications classpath. - -Note that it's not required to wrap this directive with ``get`` as this directive will only respond to ``GET`` requests. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/FileAndResourceDirectivesExamplesSpec.scala - :snippet: getFromResource-examples \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/file-and-resource-directives/getFromResourceDirectory.rst b/akka-docs/rst/scala/http/routing-dsl/directives/file-and-resource-directives/getFromResourceDirectory.rst deleted file mode 100644 index 0780b5c2be..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/file-and-resource-directives/getFromResourceDirectory.rst +++ /dev/null @@ -1,26 +0,0 @@ -.. _-getFromResourceDirectory-: - -getFromResourceDirectory -======================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/FileAndResourceDirectives.scala - :snippet: getFromResourceDirectory - -Description ------------ - -Completes GET requests with the content of the given classpath resource directory. - -For details refer to :ref:`-getFromDirectory-` which works the same way but obtaining the file from the filesystem -instead of the applications classpath. - -Note that it's not required to wrap this directive with ``get`` as this directive will only respond to ``GET`` requests. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/FileAndResourceDirectivesExamplesSpec.scala - :snippet: getFromResourceDirectory-examples diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/file-and-resource-directives/index.rst b/akka-docs/rst/scala/http/routing-dsl/directives/file-and-resource-directives/index.rst deleted file mode 100644 index 41acfad2b8..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/file-and-resource-directives/index.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. _FileAndResourceDirectives: - -FileAndResourceDirectives -========================= - -Like the :ref:`RouteDirectives` the ``FileAndResourceDirectives`` are somewhat special in akka-http's routing DSL. -Contrary to all other directives they do not produce instances of type ``Directive[L <: HList]`` but rather "plain" -routes of type ``Route``. -The reason is that they are not meant for wrapping an inner route (like most other directives, as intermediate-level -elements of a route structure, do) but rather form the actual route structure **leaves**. - -So in most cases the inner-most element of a route structure branch is one of the :ref:`RouteDirectives` or -``FileAndResourceDirectives``. - -.. toctree:: - :maxdepth: 1 - - getFromBrowseableDirectories - getFromBrowseableDirectory - getFromDirectory - getFromFile - getFromResource - getFromResourceDirectory - listDirectoryContents diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/file-and-resource-directives/listDirectoryContents.rst b/akka-docs/rst/scala/http/routing-dsl/directives/file-and-resource-directives/listDirectoryContents.rst deleted file mode 100644 index 4104a88652..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/file-and-resource-directives/listDirectoryContents.rst +++ /dev/null @@ -1,31 +0,0 @@ -.. _-listDirectoryContents-: - -listDirectoryContents -===================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/FileAndResourceDirectives.scala - :snippet: listDirectoryContents - -Description ------------ - -Completes GET requests with a unified listing of the contents of all given directories. The actual rendering of the -directory contents is performed by the in-scope ``Marshaller[DirectoryListing]``. - -To just serve files use :ref:`-getFromDirectory-`. - -To serve files and provide a browseable directory listing use :ref:`-getFromBrowseableDirectories-` instead. - -The rendering can be overridden by providing a custom ``Marshaller[DirectoryListing]``, you can read more about it in -:ref:`-getFromDirectory-` 's documentation. - -Note that it's not required to wrap this directive with ``get`` as this directive will only respond to ``GET`` requests. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/FileAndResourceDirectivesExamplesSpec.scala - :snippet: listDirectoryContents-examples diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/file-upload-directives/fileUpload.rst b/akka-docs/rst/scala/http/routing-dsl/directives/file-upload-directives/fileUpload.rst deleted file mode 100644 index 3d3cc2e231..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/file-upload-directives/fileUpload.rst +++ /dev/null @@ -1,29 +0,0 @@ -.. _-fileUpload-: - -fileUpload -========== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/FileUploadDirectives.scala - :snippet: fileUpload - -Description ------------ -Simple access to the stream of bytes for a file uploaded as a multipart form together with metadata -about the upload as extracted value. - -If there is no field with the given name the request will be rejected, if there are multiple file parts -with the same name, the first one will be used and the subsequent ones ignored. - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/FileUploadDirectivesExamplesSpec.scala - :snippet: fileUpload - -:: - - curl --form "csv=@uploadFile.txt" http://: diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/file-upload-directives/index.rst b/akka-docs/rst/scala/http/routing-dsl/directives/file-upload-directives/index.rst deleted file mode 100644 index 1af52aaeab..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/file-upload-directives/index.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. _FileUploadDirectives: - -FileUploadDirectives -==================== - -.. toctree:: - :maxdepth: 1 - - uploadedFile - fileUpload diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/file-upload-directives/uploadedFile.rst b/akka-docs/rst/scala/http/routing-dsl/directives/file-upload-directives/uploadedFile.rst deleted file mode 100644 index 99216da5ea..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/file-upload-directives/uploadedFile.rst +++ /dev/null @@ -1,31 +0,0 @@ -.. _-uploadedFile-: - -uploadedFile -============ - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/FileUploadDirectives.scala - :snippet: uploadedFile - -Description ------------ -Streams the contents of a file uploaded as a multipart form into a temporary file on disk and provides the file and -metadata about the upload as extracted value. - -If there is an error writing to disk the request will be failed with the thrown exception, if there is no field -with the given name the request will be rejected, if there are multiple file parts with the same name, the first -one will be used and the subsequent ones ignored. - -.. note:: - This directive will stream contents of the request into a file, however one can not start processing these - until the file has been written completely. For streaming APIs it is preferred to use the :ref:`-fileUpload-` - directive, as it allows for streaming handling of the incoming data bytes. - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/FileUploadDirectivesExamplesSpec.scala - :snippet: uploadedFile diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/form-field-directives/formField.rst b/akka-docs/rst/scala/http/routing-dsl/directives/form-field-directives/formField.rst deleted file mode 100644 index ed3aab8de5..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/form-field-directives/formField.rst +++ /dev/null @@ -1,22 +0,0 @@ -.. _-formField-scala-: - -formField -========= - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/FormFieldDirectives.scala - :snippet: formField - -Description ------------ -Allows extracting a single Form field sent in the request. - -See :ref:`-formFields-` for an in-depth description. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/FormFieldDirectivesExamplesSpec.scala - :snippet: formField diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/form-field-directives/formFieldMap.rst b/akka-docs/rst/scala/http/routing-dsl/directives/form-field-directives/formFieldMap.rst deleted file mode 100644 index fc20e6492e..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/form-field-directives/formFieldMap.rst +++ /dev/null @@ -1,29 +0,0 @@ -.. _-formFieldMap-: - -formFieldMap -============ - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/FormFieldDirectives.scala - :snippet: formFieldMap - -Description ------------ -Extracts all HTTP form fields at once as a ``Map[String, String]`` mapping form field names to form field values. - -If form data contain a field value several times, the map will contain the last one. - -See :ref:`-formFields-` for an in-depth description. - -Warning -------- -Use of this directive can result in performance degradation or even in ``OutOfMemoryError`` s. -See :ref:`-formFieldSeq-` for details. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/FormFieldDirectivesExamplesSpec.scala - :snippet: formFieldMap diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/form-field-directives/formFieldMultiMap.rst b/akka-docs/rst/scala/http/routing-dsl/directives/form-field-directives/formFieldMultiMap.rst deleted file mode 100644 index 3f023484e4..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/form-field-directives/formFieldMultiMap.rst +++ /dev/null @@ -1,33 +0,0 @@ -.. _-formFieldMultiMap-: - -formFieldMultiMap -================= - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/FormFieldDirectives.scala - :snippet: formFieldMultiMap - -Description ------------ - -Extracts all HTTP form fields at once as a multi-map of type ``Map[String, List[String]`` mapping -a form name to a list of all its values. - -This directive can be used if form fields can occur several times. - -The order of values is *not* specified. - -See :ref:`-formFields-` for an in-depth description. - -Warning -------- -Use of this directive can result in performance degradation or even in ``OutOfMemoryError`` s. -See :ref:`-formFieldSeq-` for details. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/FormFieldDirectivesExamplesSpec.scala - :snippet: formFieldMultiMap diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/form-field-directives/formFieldSeq.rst b/akka-docs/rst/scala/http/routing-dsl/directives/form-field-directives/formFieldSeq.rst deleted file mode 100644 index 3ff16bb611..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/form-field-directives/formFieldSeq.rst +++ /dev/null @@ -1,30 +0,0 @@ -.. _-formFieldSeq-: - -formFieldSeq -============ - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/FormFieldDirectives.scala - :snippet: formFieldSeq - -Description ------------ -Extracts all HTTP form fields at once in the original order as (name, value) tuples of type ``(String, String)``. - -This directive can be used if the exact order of form fields is important or if parameters can occur several times. - -See :ref:`-formFields-` for an in-depth description. - -Warning -------- -The directive reads all incoming HTT form fields without any configured upper bound. -It means, that requests with form fields holding significant amount of data (ie. during a file upload) -can cause performance issues or even an ``OutOfMemoryError`` s. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/FormFieldDirectivesExamplesSpec.scala - :snippet: formFieldSeq diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/form-field-directives/formFields.rst b/akka-docs/rst/scala/http/routing-dsl/directives/form-field-directives/formFields.rst deleted file mode 100644 index 639890f3e4..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/form-field-directives/formFields.rst +++ /dev/null @@ -1,105 +0,0 @@ -.. _-formFields-: - -formFields -========== - -Signature ---------- - -:: - - def formFields(field: ): Directive1[T] - def formFields(fields: *): Directive[T_0 :: ... T_i ... :: HNil] - def formFields(fields: :: ... ... :: HNil): Directive[T_0 :: ... T_i ... :: HNil] - -The signature shown is simplified and written in pseudo-syntax, the real signature uses magnets. [1]_ The type -```` doesn't really exist but consists of the syntactic variants as shown in the description and the examples. - -.. [1] See `The Magnet Pattern`_ for an explanation of magnet-based overloading. -.. _`The Magnet Pattern`: http://spray.io/blog/2012-12-13-the-magnet-pattern/ - -Description ------------ -Extracts fields from requests generated by HTML forms (independently of ``HttpMethod`` used). - -Form fields can be either extracted as a String or can be converted to another type. The parameter name -can be supplied either as a String or as a Symbol. Form field extraction can be modified to mark a field -as required, optional, or repeated, or to filter requests where a form field has a certain value: - -``"color"`` - extract value of field "color" as ``String`` -``"color".?`` - extract optional value of field "color" as ``Option[String]`` -``"color" ? "red"`` - extract optional value of field "color" as ``String`` with default value ``"red"`` -``"color" ! "blue"`` - require value of field "color" to be ``"blue"`` and extract nothing -``"amount".as[Int]`` - extract value of field "amount" as ``Int``, you need a matching implicit ``Unmarshaller`` in scope for that to work - (see also :ref:`http-unmarshalling-scala`) -``"amount".as(unmarshaller)`` - extract value of field "amount" with an explicit ``Unmarshaller`` -``"distance".*`` - extract multiple occurrences of field "distance" as ``Iterable[String]`` -``"distance".as[Int].*`` - extract multiple occurrences of field "distance" as ``Iterable[Int]``, you need a matching implicit ``Unmarshaller`` in scope for that to work - (see also :ref:`http-unmarshalling-scala`) -``"distance".as(unmarshaller).*`` - extract multiple occurrences of field "distance" with an explicit ``Unmarshaller`` - -You can use :ref:`Case Class Extraction` to group several extracted values together into a case-class -instance. - -Requests missing a required field or field value will be rejected with an appropriate rejection. - -There's also a singular version, :ref:`-formField-scala-`. - -Query parameters can be handled in a similar way, see :ref:`-parameters-scala-`. - -Unmarshalling -------------- - -Data POSTed from `HTML forms`_ is either of type ``application/x-www-form-urlencoded`` or of type -``multipart/form-data``. The value of an url-encoded field is a ``String`` while the value of a -``multipart/form-data``-encoded field is a "body part" containing an entity. This means that different kind of unmarshallers are needed depending -on what the Content-Type of the request is: - - - A ``application/x-www-form-urlencoded`` encoded field needs an implicit ``Unmarshaller[Option[String], T]`` - - A ``multipart/form-data`` encoded field needs an implicit ``FromStrictFormFieldUnmarshaller[T]`` - -For common data-types, these implicits are predefined so that you usually don't need to care. For custom data-types it -should usually suffice to create a ``FromStringUnmarshaller[T]`` if the value will be encoded as a ``String``. -This should be valid for all values generated by HTML forms apart from file uploads. - -Details -....... - -It should only be necessary to read and understand this paragraph if you have very special needs and need to process -arbitrary forms, especially ones not generated by HTML forms. - -The ``formFields`` directive contains this logic to find and decide how to deserialize a POSTed form field: - - - It tries to find implicits of both types at the definition site if possible or otherwise at least one of both. If - none is available compilation will fail with an "implicit not found" error. - - Depending on the ``Content-Type`` of the incoming request it first tries the matching (see above) one if available. - - If only a ``Unmarshaller[Option[String], T]`` is available when a request of type ``multipart/form-data`` is - received, this unmarshaller will be tried to deserialize the body part for a field if the entity is of type - ``text/plain`` or unspecified. - - If only a ``FromStrictFormFieldUnmarshaller[T]`` is available when a request of type - ``application/x-www-form-urlencoded`` is received, this unmarshaller will be tried to deserialize the field value by - packing the field value into a body part with an entity of type ``text/plain``. Deserializing will only succeed if - the unmarshaller accepts entities of type ``text/plain``. - -If you need to handle encoded fields of a ``multipart/form-data``-encoded request for a custom type, you therefore need -to provide a ``FromStrictFormFieldUnmarshaller[T]``. - -.. _HTML forms: http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4 - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/FormFieldDirectivesExamplesSpec.scala - :snippet: formFields - -For more examples about the way how fields can specified see the examples for the ``parameters`` directive. diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/form-field-directives/index.rst b/akka-docs/rst/scala/http/routing-dsl/directives/form-field-directives/index.rst deleted file mode 100644 index 416da4ee44..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/form-field-directives/index.rst +++ /dev/null @@ -1,13 +0,0 @@ -.. _FormFieldDirectives: - -FormFieldDirectives -=================== - -.. toctree:: - :maxdepth: 1 - - formField - formFields - formFieldSeq - formFieldMap - formFieldMultiMap \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/future-directives/completeOrRecoverWith.rst b/akka-docs/rst/scala/http/routing-dsl/directives/future-directives/completeOrRecoverWith.rst deleted file mode 100644 index 6ad762b2a4..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/future-directives/completeOrRecoverWith.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. _-completeOrRecoverWith-: - -completeOrRecoverWith -===================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/FutureDirectives.scala - :snippet: completeOrRecoverWith - -Description ------------ -If the ``Future[T]`` succeeds the request is completed using the value's marshaller (this directive therefore -requires a marshaller for the future's parameter type to be implicitly available). The execution of the inner -route passed to this directive is only executed if the given future completed with a failure, -exposing the reason of failure as an extraction of type ``Throwable``. - -To handle the successful case manually as well, use the :ref:`-onComplete-` directive, instead. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/FutureDirectivesExamplesSpec.scala - :snippet: completeOrRecoverWith diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/future-directives/index.rst b/akka-docs/rst/scala/http/routing-dsl/directives/future-directives/index.rst deleted file mode 100644 index a8157ea93e..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/future-directives/index.rst +++ /dev/null @@ -1,15 +0,0 @@ -.. _FutureDirectives: - -FuturesDirectives -================= - -Future directives can be used to run inner routes once the provided ``Future[T]`` has been completed. - -.. toctree:: - :maxdepth: 1 - - onComplete - onCompleteWithBreaker - onSuccess - completeOrRecoverWith - diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/future-directives/onComplete.rst b/akka-docs/rst/scala/http/routing-dsl/directives/future-directives/onComplete.rst deleted file mode 100644 index 64b7625e66..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/future-directives/onComplete.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. _-onComplete-: - -onComplete -========== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/FutureDirectives.scala - :snippet: onComplete - -Description ------------ -Evaluates its parameter of type ``Future[T]``, and once the ``Future`` has been completed, extracts its -result as a value of type ``Try[T]`` and passes it to the inner route. - -To handle the ``Failure`` case automatically and only work with the result value, use :ref:`-onSuccess-`. - -To complete with a successful result automatically and just handle the failure result, use :ref:`-completeOrRecoverWith-`, instead. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/FutureDirectivesExamplesSpec.scala - :snippet: onComplete diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/future-directives/onCompleteWithBreaker.rst b/akka-docs/rst/scala/http/routing-dsl/directives/future-directives/onCompleteWithBreaker.rst deleted file mode 100644 index 5e4b8d952b..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/future-directives/onCompleteWithBreaker.rst +++ /dev/null @@ -1,27 +0,0 @@ -.. _-onCompleteWithBreaker-: - -onCompleteWithBreaker -===================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/FutureDirectives.scala - :snippet: onCompleteWithBreaker - -Description ------------ -Evaluates its parameter of type ``Future[T]`` protecting it with the specified ``CircuitBreaker``. -Refer to :ref:`Akka Circuit Breaker` for a detailed description of this pattern. - -If the ``CircuitBreaker`` is open, the request is rejected with a ``CircuitBreakerOpenRejection``. -Note that in this case the request's entity databytes stream is cancelled, and the connection is closed -as a consequence. - -Otherwise, the same behaviour provided by :ref:`-onComplete-` is to be expected. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/FutureDirectivesExamplesSpec.scala - :snippet: onCompleteWithBreaker diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/future-directives/onSuccess.rst b/akka-docs/rst/scala/http/routing-dsl/directives/future-directives/onSuccess.rst deleted file mode 100644 index 2658782346..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/future-directives/onSuccess.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. _-onSuccess-: - -onSuccess -========= - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/FutureDirectives.scala - :snippet: onSuccess - -Description ------------ -Evaluates its parameter of type ``Future[T]``, and once the ``Future`` has been completed successfully, -extracts its result as a value of type ``T`` and passes it to the inner route. - -If the future fails its failure throwable is bubbled up to the nearest ``ExceptionHandler``. - -To handle the ``Failure`` case manually as well, use :ref:`-onComplete-`, instead. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/FutureDirectivesExamplesSpec.scala - :snippet: onSuccess diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/header-directives/checkSameOrigin.rst b/akka-docs/rst/scala/http/routing-dsl/directives/header-directives/checkSameOrigin.rst deleted file mode 100644 index cd8cafcddc..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/header-directives/checkSameOrigin.rst +++ /dev/null @@ -1,23 +0,0 @@ -.. _-checkSameOrigin-: - -checkSameOrigin -=============== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/HeaderDirectives.scala - :snippet: checkSameOrigin - -Description ------------ -Checks that request comes from the same origin. Extracts the ``Origin`` header value and verifies that allowed range -contains the obtained value. In the case of absent of the ``Origin`` header rejects with a ``MissingHeaderRejection``. -If the origin value is not in the allowed range rejects with an ``InvalidOriginHeaderRejection`` -and ``StatusCodes.Forbidden`` status. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/HeaderDirectivesExamplesSpec.scala - :snippet: checkSameOrigin-0 diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/header-directives/headerValue.rst b/akka-docs/rst/scala/http/routing-dsl/directives/header-directives/headerValue.rst deleted file mode 100644 index c245cfd8c2..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/header-directives/headerValue.rst +++ /dev/null @@ -1,30 +0,0 @@ -.. _-headerValue-: - -headerValue -=========== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/HeaderDirectives.scala - :snippet: headerValue - -Description ------------ -Traverses the list of request headers with the specified function and extracts the first value the function returns as -``Some(value)``. - -The :ref:`-headerValue-` directive is a mixture of ``map`` and ``find`` on the list of request headers. The specified function -is called once for each header until the function returns ``Some(value)``. This value is extracted and presented to the -inner route. If the function throws an exception the request is rejected with a ``MalformedHeaderRejection``. If the -function returns ``None`` for every header the request is rejected as "NotFound". - -This directive is the basis for building other request header related directives. - -See also :ref:`-headerValuePF-` for a nicer syntactic alternative. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/HeaderDirectivesExamplesSpec.scala - :snippet: headerValue-0 diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/header-directives/headerValueByName.rst b/akka-docs/rst/scala/http/routing-dsl/directives/header-directives/headerValueByName.rst deleted file mode 100644 index 55f322c772..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/header-directives/headerValueByName.rst +++ /dev/null @@ -1,26 +0,0 @@ -.. _-headerValueByName-: - -headerValueByName -================= - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/HeaderDirectives.scala - :snippet: headerValueByName - -Description ------------ -Extracts the value of the HTTP request header with the given name. - -The name can be given as a ``String`` or as a ``Symbol``. If no header with a matching name is found the request -is rejected with a ``MissingHeaderRejection``. - -If the header is expected to be missing in some cases or to customize -handling when the header is missing use the :ref:`-optionalHeaderValueByName-` directive instead. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/HeaderDirectivesExamplesSpec.scala - :snippet: headerValueByName-0 diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/header-directives/headerValueByType.rst b/akka-docs/rst/scala/http/routing-dsl/directives/header-directives/headerValueByType.rst deleted file mode 100644 index 2f4c65f224..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/header-directives/headerValueByType.rst +++ /dev/null @@ -1,39 +0,0 @@ -.. _-headerValueByType-: - -headerValueByType -================= - -Signature ---------- - -:: - - def headerValueByType[T <: HttpHeader: ClassTag](): Directive1[T] - -The signature shown is simplified, the real signature uses magnets. [1]_ - -.. [1] See `The Magnet Pattern`_ for an explanation of magnet-based overloading. -.. _`The Magnet Pattern`: http://spray.io/blog/2012-12-13-the-magnet-pattern/ - -Description ------------ -Traverses the list of request headers and extracts the first header of the given type. - -The ``headerValueByType`` directive finds a header of the given type in the list of request header. If no header of -the given type is found the request is rejected with a ``MissingHeaderRejection``. - -If the header is expected to be missing in some cases or to customize handling when the header -is missing use the :ref:`-optionalHeaderValueByType-` directive instead. - -.. note:: - Custom headers will only be matched by this directive if they extend ``ModeledCustomHeader`` - and provide a companion extending ``ModeledCustomHeaderCompanion``, otherwise the routing - infrastructure does now know where to search for the needed companion and header name. - - To learn more about defining custom headers, read: :ref:`custom-headers-scala`. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/HeaderDirectivesExamplesSpec.scala - :snippet: headerValueByType-0 diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/header-directives/headerValuePF.rst b/akka-docs/rst/scala/http/routing-dsl/directives/header-directives/headerValuePF.rst deleted file mode 100644 index 7b3085f423..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/header-directives/headerValuePF.rst +++ /dev/null @@ -1,27 +0,0 @@ -.. _-headerValuePF-: - -headerValuePF -============= - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/HeaderDirectives.scala - :snippet: headerValuePF - -Description ------------ -Calls the specified partial function with the first request header the function is ``isDefinedAt`` and extracts the -result of calling the function. - -The ``headerValuePF`` directive is an alternative syntax version of :ref:`-headerValue-`. - -If the function throws an exception the request is rejected with a ``MalformedHeaderRejection``. - -If the function is not defined for any header the request is rejected as "NotFound". - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/HeaderDirectivesExamplesSpec.scala - :snippet: headerValuePF-0 diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/header-directives/index.rst b/akka-docs/rst/scala/http/routing-dsl/directives/header-directives/index.rst deleted file mode 100644 index 78feab239c..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/header-directives/index.rst +++ /dev/null @@ -1,20 +0,0 @@ -.. _HeaderDirectives: - -HeaderDirectives -================ - -Header directives can be used to extract header values from the request. To change -response headers use one of the :ref:`RespondWithDirectives`. - -.. toctree:: - :maxdepth: 1 - - headerValue - headerValueByName - headerValueByType - headerValuePF - optionalHeaderValue - optionalHeaderValueByName - optionalHeaderValueByType - optionalHeaderValuePF - checkSameOrigin diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/header-directives/optionalHeaderValue.rst b/akka-docs/rst/scala/http/routing-dsl/directives/header-directives/optionalHeaderValue.rst deleted file mode 100644 index cf9aa299c7..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/header-directives/optionalHeaderValue.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. _-optionalHeaderValue-: - -optionalHeaderValue -=================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/HeaderDirectives.scala - :snippet: optionalHeaderValue - -Description ------------ -Traverses the list of request headers with the specified function and extracts the first value the function returns as -``Some(value)``. - -The ``optionalHeaderValue`` directive is similar to the :ref:`-headerValue-` directive but always extracts an ``Option`` -value instead of rejecting the request if no matching header could be found. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/HeaderDirectivesExamplesSpec.scala - :snippet: optionalHeaderValue-0 diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/header-directives/optionalHeaderValueByName.rst b/akka-docs/rst/scala/http/routing-dsl/directives/header-directives/optionalHeaderValueByName.rst deleted file mode 100644 index cf51fb5d6e..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/header-directives/optionalHeaderValueByName.rst +++ /dev/null @@ -1,23 +0,0 @@ -.. _-optionalHeaderValueByName-: - -optionalHeaderValueByName -========================= - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/HeaderDirectives.scala - :snippet: optionalHeaderValueByName - -Description ------------ -Optionally extracts the value of the HTTP request header with the given name. - -The ``optionalHeaderValueByName`` directive is similar to the :ref:`-headerValueByName-` directive but always extracts -an ``Option`` value instead of rejecting the request if no matching header could be found. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/HeaderDirectivesExamplesSpec.scala - :snippet: optionalHeaderValueByName-0 \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/header-directives/optionalHeaderValueByType.rst b/akka-docs/rst/scala/http/routing-dsl/directives/header-directives/optionalHeaderValueByType.rst deleted file mode 100644 index e77ce8db82..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/header-directives/optionalHeaderValueByType.rst +++ /dev/null @@ -1,36 +0,0 @@ -.. _-optionalHeaderValueByType-: - -optionalHeaderValueByType -========================= - -Signature ---------- - -:: - - def optionalHeaderValueByType[T <: HttpHeader: ClassTag](): Directive1[Option[T]] - -The signature shown is simplified, the real signature uses magnets. [1]_ - -.. [1] See `The Magnet Pattern`_ for an explanation of magnet-based overloading. -.. _`The Magnet Pattern`: http://spray.io/blog/2012-12-13-the-magnet-pattern/ - -Description ------------ -Optionally extracts the value of the HTTP request header of the given type. - -The ``optionalHeaderValueByType`` directive is similar to the :ref:`-headerValueByType-` directive but always extracts -an ``Option`` value instead of rejecting the request if no matching header could be found. - -.. note:: - Custom headers will only be matched by this directive if they extend ``ModeledCustomHeader`` - and provide a companion extending ``ModeledCustomHeaderCompanion``, otherwise the routing - infrastructure does now know where to search for the needed companion and header name. - - To learn more about defining custom headers, read: :ref:`custom-headers-scala`. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/HeaderDirectivesExamplesSpec.scala - :snippet: optionalHeaderValueByType-0 diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/header-directives/optionalHeaderValuePF.rst b/akka-docs/rst/scala/http/routing-dsl/directives/header-directives/optionalHeaderValuePF.rst deleted file mode 100644 index 728b9131ad..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/header-directives/optionalHeaderValuePF.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. _-optionalHeaderValuePF-: - -optionalHeaderValuePF -===================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/HeaderDirectives.scala - :snippet: optionalHeaderValuePF - -Description ------------ -Calls the specified partial function with the first request header the function is ``isDefinedAt`` and extracts the -result of calling the function. - -The ``optionalHeaderValuePF`` directive is similar to the :ref:`-headerValuePF-` directive but always extracts an ``Option`` -value instead of rejecting the request if no matching header could be found. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/HeaderDirectivesExamplesSpec.scala - :snippet: optionalHeaderValuePF-0 diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/host-directives/extractHost.rst b/akka-docs/rst/scala/http/routing-dsl/directives/host-directives/extractHost.rst deleted file mode 100644 index 3240db8a4c..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/host-directives/extractHost.rst +++ /dev/null @@ -1,23 +0,0 @@ -.. _-extractHost-: - -extractHost -=========== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/HostDirectives.scala - :snippet: extractHost - - -Description ------------ - -Extract the hostname part of the ``Host`` request header and expose it as a ``String`` extraction to its inner route. - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/HostDirectivesExamplesSpec.scala - :snippet: extractHost \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/host-directives/host.rst b/akka-docs/rst/scala/http/routing-dsl/directives/host-directives/host.rst deleted file mode 100644 index 4248c1e4b4..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/host-directives/host.rst +++ /dev/null @@ -1,55 +0,0 @@ -.. _-host-: - -host -==== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/HostDirectives.scala - :snippet: host - - -Description ------------ -Filter requests matching conditions against the hostname part of the Host header value in the request. - -The ``def host(hostNames: String*)`` overload rejects all requests with a hostname different from the given ones. - -The ``def host(predicate: String ⇒ Boolean)`` overload rejects all requests for which the hostname does -not satisfy the given predicate. - -The ``def host(regex: Regex)`` overload works a little bit different: it rejects all requests with a hostname -that doesn't have a prefix matching the given regular expression and also extracts a ``String`` to its -inner route following this rules: - - * For all matching requests the prefix string matching the regex is extracted and passed to the inner route. - * If the regex contains a capturing group only the string matched by this group is extracted. - * If the regex contains more than one capturing group an ``IllegalArgumentException`` is thrown. - - -Example -------- - -Matching a list of hosts: - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/HostDirectivesExamplesSpec.scala - :snippet: list-of-hosts - -Making sure the host satisfies the given predicate - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/HostDirectivesExamplesSpec.scala - :snippet: predicate - -Using a regular expressions: - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/HostDirectivesExamplesSpec.scala - :snippet: using-regex - -Beware that in the case of introducing multiple capturing groups in the regex such as in the case bellow, the -directive will fail at runtime, at the moment the route tree is evaluated for the first time. This might cause -your http handler actor to enter in a fail/restart loop depending on your supervision strategy. - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/HostDirectivesExamplesSpec.scala - :snippet: failing-regex - diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/host-directives/index.rst b/akka-docs/rst/scala/http/routing-dsl/directives/host-directives/index.rst deleted file mode 100644 index 2bd2f44252..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/host-directives/index.rst +++ /dev/null @@ -1,13 +0,0 @@ -.. _HostDirectives: - -HostDirectives -============== - -HostDirectives allow you to filter requests based on the hostname part of the ``Host`` header -contained in incoming requests as well as extracting its value for usage in inner routes. - -.. toctree:: - :maxdepth: 1 - - extractHost - host diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/index.rst b/akka-docs/rst/scala/http/routing-dsl/directives/index.rst deleted file mode 100644 index 4e30d49f50..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/index.rst +++ /dev/null @@ -1,227 +0,0 @@ -.. _directives: - -Directives -========== - -A "Directive" is a small building block used for creating arbitrarily complex :ref:`route structures `. -Akka HTTP already pre-defines a large number of directives and you can easily construct your own: - -.. toctree:: - :maxdepth: 1 - - alphabetically - by-trait - custom-directives - - -Basics ------- - -Directives create :ref:`Routes`. To understand how directives work it is helpful to contrast them with the "primitive" -way of creating routes. - -Since ``Route`` is just a type alias for a function type ``Route`` instances can be written in any way in which function -instances can be written, e.g. as a function literal:: - - val route: Route = { ctx => ctx.complete("yeah") } - -or shorter:: - - val route: Route = _.complete("yeah") - -With the :ref:`-complete-` directive this becomes even shorter:: - - val route = complete("yeah") - -These three ways of writing this ``Route`` are fully equivalent, the created ``route`` will behave identically in all -cases. - -Let's look at a slightly more complicated example to highlight one important point in particular. -Consider these two routes:: - - val a: Route = { - println("MARK") - ctx => ctx.complete("yeah") - } - - val b: Route = { ctx => - println("MARK") - ctx.complete("yeah") - } - -The difference between ``a`` and ``b`` is when the ``println`` statement is executed. -In the case of ``a`` it is executed *once*, when the route is constructed, whereas in the case of ``b`` it is executed -every time the route is *run*. - -Using the :ref:`-complete-` directive the same effects are achieved like this:: - - val a = { - println("MARK") - complete("yeah") - } - - val b = complete { - println("MARK") - "yeah" - } - -This works because the argument to the :ref:`-complete-` directive is evaluated *by-name*, i.e. it is re-evaluated -every time the produced route is run. - -Let's take things one step further:: - - val route: Route = { ctx => - if (ctx.request.method == HttpMethods.GET) - ctx.complete("Received GET") - else - ctx.complete("Received something else") - } - -Using the :ref:`-get-` and :ref:`-complete-` directives we can write this route like this:: - - val route = - get { - complete("Received GET") - } ~ - complete("Received something else") - -Again, the produced routes will behave identically in all cases. - -Note that, if you wish, you can also mix the two styles of route creation:: - - val route = - get { ctx => - ctx.complete("Received GET") - } ~ - complete("Received something else") - -Here, the inner route of the :ref:`-get-` directive is written as an explicit function literal. - -However, as you can see from these examples, building routes with directives rather than "manually" results in code that -is a lot more concise and as such more readable and maintainable. In addition it provides for better composability (as -you will see in the coming sections). So, when using Akka HTTP's Routing DSL you should almost never have to fall back -to creating routes via ``Route`` function literals that directly manipulate the :ref:`RequestContext`. - - -Structure ---------- - -The general anatomy of a directive is as follows:: - - name(arguments) { extractions => - ... // inner route - } - -It has a name, zero or more arguments and optionally an inner route (The :ref:`RouteDirectives` are special in that they -are always used at the leaf-level and as such cannot have inner routes). -Additionally directives can "extract" a number of values and make them available to their inner routes as function -arguments. When seen "from the outside" a directive with its inner route form an expression of type ``Route``. - - -What Directives do ------------------- - -A directive can do one or more of the following: - -.. rst-class:: wide - -* Transform the incoming ``RequestContext`` before passing it on to its inner route (i.e. modify the request) -* Filter the ``RequestContext`` according to some logic, i.e. only pass on certain requests and reject others -* Extract values from the ``RequestContext`` and make them available to its inner route as "extractions" -* Chain some logic into the :ref:`RouteResult` future transformation chain (i.e. modify the response or rejection) -* Complete the request - -This means a ``Directive`` completely wraps the functionality of its inner route and can apply arbitrarily complex -transformations, both (or either) on the request and on the response side. - - -Composing Directives --------------------- - -.. note:: Gotcha: forgetting the ``~`` (tilde) character in between directives can result in perfectly valid - Scala code that compiles but does not work as expected. What would be intended as a single expression would actually be multiple expressions, and only the final one would be used as the result of the parent directive. Alternatively, you might choose to use the ``concat`` combinator. ``concat(a, b, c)`` is the same as ``a ~ b ~ c``. - -As you have seen from the examples presented so far the "normal" way of composing directives is nesting. -Let's take a look at this concrete example: - -.. includecode2:: ../../../code/docs/http/scaladsl/server/DirectiveExamplesSpec.scala - :snippet: example-1 - -Here the ``get`` and ``put`` directives are chained together with the ``~`` operator to form a higher-level route that -serves as the inner route of the ``path`` directive. To make this structure more explicit you could also write the whole -thing like this: - -.. includecode2:: ../../../code/docs/http/scaladsl/server/DirectiveExamplesSpec.scala - :snippet: example-2 - -What you can't see from this snippet is that directives are not implemented as simple methods but rather as stand-alone -objects of type ``Directive``. This gives you more flexibility when composing directives. For example you can -also use the ``|`` operator on directives. Here is yet another way to write the example: - -.. includecode2:: ../../../code/docs/http/scaladsl/server/DirectiveExamplesSpec.scala - :snippet: example-3 - -Or better (without dropping down to writing an explicit ``Route`` function manually): - -.. includecode2:: ../../../code/docs/http/scaladsl/server/DirectiveExamplesSpec.scala - :snippet: example-4 - -If you have a larger route structure where the ``(get | put)`` snippet appears several times you could also factor it -out like this: - -.. includecode2:: ../../../code/docs/http/scaladsl/server/DirectiveExamplesSpec.scala - :snippet: example-5 - -Note that, because ``getOrPut`` doesn't take any parameters, it can be a ``val`` here. - -As an alternative to nesting you can also use the `&` operator: - -.. includecode2:: ../../../code/docs/http/scaladsl/server/DirectiveExamplesSpec.scala - :snippet: example-6 - -Here you can see that, when directives producing extractions are combined with ``&``, the resulting "super-directive" -simply extracts the concatenation of its sub-extractions. - -And once again, you can factor things out if you want, thereby pushing the "factoring out" of directive configurations -to its extreme: - -.. includecode2:: ../../../code/docs/http/scaladsl/server/DirectiveExamplesSpec.scala - :snippet: example-7 - -This type of combining directives with the ``|`` and ``&`` operators as well as "saving" more complex directive -configurations as a ``val`` works across the board, with all directives taking inner routes. - -Note that going this far with "compressing" several directives into a single one probably doesn't result in the most -readable and therefore maintainable routing code. It might even be that the very first of this series of examples -is in fact the most readable one. - -Still, the purpose of the exercise presented here is to show you how flexible directives can be and how you can -use their power to define your web service behavior at the level of abstraction that is right for **your** application. - - -Type Safety of Directives -------------------------- - -When you combine directives with the ``|`` and ``&`` operators the routing DSL makes sure that all extractions work as -expected and logical constraints are enforced at compile-time. - -For example you cannot ``|`` a directive producing an extraction with one that doesn't:: - - val route = path("order" / IntNumber) | get // doesn't compile - -Also the number of extractions and their types have to match up:: - - val route = path("order" / IntNumber) | path("order" / DoubleNumber) // doesn't compile - val route = path("order" / IntNumber) | parameter('order.as[Int]) // ok - -When you combine directives producing extractions with the ``&`` operator all extractions will be properly gathered up:: - - val order = path("order" / IntNumber) & parameters('oem, 'expired ?) - val route = - order { (orderId, oem, expired) => - ... - } - -Directives offer a great way of constructing your web service logic from small building blocks in a plug and play -fashion while maintaining DRYness and full type-safety. If the large range of :ref:`Predefined Directives` does not -fully satisfy your needs you can also easily create :ref:`Custom Directives`. diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/marshalling-directives/completeWith.rst b/akka-docs/rst/scala/http/routing-dsl/directives/marshalling-directives/completeWith.rst deleted file mode 100644 index 89db94fec7..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/marshalling-directives/completeWith.rst +++ /dev/null @@ -1,45 +0,0 @@ -.. _-completeWith-: - -completeWith -============ - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/MarshallingDirectives.scala - :snippet: completeWith[T] - -Description ------------ -Uses the marshaller for a given type to produce a completion function that is passed to its -inner route. You can use it to decouple marshaller resolution from request completion. - -The ``completeWith`` directive works in conjuction with ``instanceOf`` and ``spray.httpx.marshalling`` -to convert higher-level (object) structure into some lower-level serialized "wire format". -:ref:`The marshalling documentation ` explains this process in detail. -This directive simplifies exposing types to clients via a route while providing some -form of access to the current context. - -``completeWith`` is similar to ``handleWith``. The main difference is with ``completeWith`` you must eventually call -the completion function generated by ``completeWith``. ``handleWith`` will automatically call ``complete`` when the -``handleWith`` function returns. - -Examples --------- - -The following example uses ``spray-json`` to marshall a simple ``Person`` class to a json -response. It utilizes ``SprayJsonSupport`` via the ``PersonJsonSupport`` object as the in-scope -unmarshaller. - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MarshallingDirectivesExamplesSpec.scala - :snippet: person-json-support - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MarshallingDirectivesExamplesSpec.scala - :snippet: person-case-class - -The ``findPerson`` takes an argument of type ``Person => Unit`` which is generated by the ``completeWith`` -call. We can handle any logic we want in ``findPerson`` and call our completion function to -complete the request. - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MarshallingDirectivesExamplesSpec.scala - :snippet: example-completeWith-with-json diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/marshalling-directives/entity.rst b/akka-docs/rst/scala/http/routing-dsl/directives/marshalling-directives/entity.rst deleted file mode 100644 index 5635ac1c0d..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/marshalling-directives/entity.rst +++ /dev/null @@ -1,59 +0,0 @@ -.. _-entity-: - -entity -====== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/MarshallingDirectives.scala - :snippet: entity - -Description ------------ -Unmarshalls the request entity to the given type and passes it to its inner Route. An unmarshaller -returns an ``Either`` with ``Right(value)`` if successful or ``Left(exception)`` for a failure. -The ``entity`` method will either pass the ``value`` to the inner route or map the ``exception`` to a -``akka.http.scaladsl.server.Rejection``. - -The ``entity`` directive works in conjuction with ``as`` and ``akka.http.scaladsl.unmarshalling`` to -convert some serialized "wire format" value into a higher-level object structure. -:ref:`The unmarshalling documentation ` explains this process in detail. -This directive simplifies extraction and error handling to the specified type from the request. - -An unmarshaller will return a ``Left(exception)`` in the case of an error. This is converted to a -``akka.http.scaladsl.server.Rejection`` within the ``entity`` directive. The following table lists how exceptions -are mapped to rejections: - -========================== ============ -Left(exception) Rejection --------------------------- ------------ -``ContentExpected`` ``RequestEntityExpectedRejection`` -``UnsupportedContentType`` ``UnsupportedRequestContentTypeRejection``, which lists the supported types -``MaformedContent`` ``MalformedRequestContentRejection``, with an error message and cause -========================== ============ - -Examples --------- - -The following example uses ``spray-json`` to unmarshall a json request into a simple ``Person`` -class. It utilizes ``SprayJsonSupport`` via the ``PersonJsonSupport`` object as the in-scope unmarshaller. - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MarshallingDirectivesExamplesSpec.scala - :snippet: person-case-class - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MarshallingDirectivesExamplesSpec.scala - :snippet: person-json-support - - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MarshallingDirectivesExamplesSpec.scala - :snippet: example-entity-with-json - -It is also possible to use the ``entity`` directive to obtain raw ``JsValue`` ( spray-json_ ) objects, by simply using -``as[JsValue]``, or any other JSON type for which you have marshallers in-scope. - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MarshallingDirectivesExamplesSpec.scala - :snippet: example-entity-with-raw-json - - -.. _spray-json: https://github.com/spray/spray-json diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/marshalling-directives/handleWith.rst b/akka-docs/rst/scala/http/routing-dsl/directives/marshalling-directives/handleWith.rst deleted file mode 100644 index d06688332e..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/marshalling-directives/handleWith.rst +++ /dev/null @@ -1,47 +0,0 @@ -.. _-handleWith-: - -handleWith -========== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/MarshallingDirectives.scala - :snippet: handleWith - -Description ------------ -Completes the request using the given function. The input to the function is produced with -the in-scope entity unmarshaller and the result value of the function is marshalled with -the in-scope marshaller. ``handleWith`` can be a convenient method combining ``entity`` with -``complete``. - -The ``handleWith`` directive is used when you want to handle a route with a given function of -type A ⇒ B. ``handleWith`` will use both an in-scope unmarshaller to convert a request into -type A and an in-scope marshaller to convert type B into a response. This is helpful when your -core business logic resides in some other class or you want your business logic to be independent -of the REST interface written with akka-http. You can use ``handleWith`` to "hand off" processing -to a given function without requiring any akka-http-specific functionality. - -``handleWith`` is similar to ``produce``. The main difference is ``handleWith`` automatically -calls ``complete`` when the function passed to ``handleWith`` returns. Using ``produce`` you -must explicity call the completion function passed from the ``produce`` function. - -See :ref:`marshalling ` and :ref:`unmarshalling ` for guidance -on marshalling entities with akka-http. - -Examples --------- - -The following example uses an ``updatePerson`` function with a ``Person`` case class as an input and output. We plug this function into our route using ``handleWith``. - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MarshallingDirectivesExamplesSpec.scala - :snippet: person-case-class - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MarshallingDirectivesExamplesSpec.scala - :snippet: example-handleWith-with-json - -The PersonJsonSupport object handles both marshalling and unmarshalling of the Person case class. - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MarshallingDirectivesExamplesSpec.scala - :snippet: person-json-support diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/marshalling-directives/index.rst b/akka-docs/rst/scala/http/routing-dsl/directives/marshalling-directives/index.rst deleted file mode 100644 index ba0d9ed891..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/marshalling-directives/index.rst +++ /dev/null @@ -1,34 +0,0 @@ -.. _MarshallingDirectives: - -Marshalling Directives -====================== - -Marshalling directives work in conjunction with ``akka.http.scaladsl.marshalling`` and ``akka.http.scaladsl.unmarshalling`` to convert -a request entity to a specific type or a type to a response. - -See :ref:`marshalling ` and :ref:`unmarshalling ` for specific -serialization (also known as pickling) guidance. - -Marshalling directives usually rely on an in-scope implicit marshaller to handle conversion. - -.. toctree:: - :maxdepth: 1 - - completeWith - entity - handleWith - -Understanding Specific Marshalling Directives ---------------------------------------------- - -======================================= ======================================= -directive behavior -======================================= ======================================= -:ref:`-completeWith-` Uses a marshaller for a given type to produce a completion function for an inner route. Used in conjuction with *instanceOf* to format responses. -:ref:`-entity-` Unmarshalls the request entity to the given type and passes it to its inner route. Used in conjection with *as* to convert requests to objects. -:ref:`-handleWith-` Completes a request with a given function, using an in-scope unmarshaller for an input and in-scope marshaller for the output. -======================================= ======================================= - - - - diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/delete.rst b/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/delete.rst deleted file mode 100644 index 1d182b9c0c..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/delete.rst +++ /dev/null @@ -1,27 +0,0 @@ -.. _-delete-: - -delete -====== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/MethodDirectives.scala - :snippet: delete - - -Description ------------ -Matches requests with HTTP method ``DELETE``. - -This directive filters an incoming request by its HTTP method. Only requests with -method ``DELETE`` are passed on to the inner route. All others are rejected with a -``MethodRejection``, which is translated into a ``405 Method Not Allowed`` response -by the default :ref:`RejectionHandler `. - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MethodDirectivesExamplesSpec.scala - :snippet: delete-method diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/extractMethod.rst b/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/extractMethod.rst deleted file mode 100644 index 9c25b91741..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/extractMethod.rst +++ /dev/null @@ -1,34 +0,0 @@ -.. _-extractMethod-: - -extractMethod -============= - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/MethodDirectives.scala - :snippet: extractMethod - -Description ------------ -Extracts the :class:`HttpMethod` from the request context and provides it for use for other directives explicitly. - -Example -------- - -In the below example our route first matches all ``GET`` requests, and if an incoming request wasn't a ``GET``, -the matching continues and the extractMethod route will be applied which we can use to programatically -print what type of request it was - independent of what actual HttpMethod it was: - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MethodDirectivesExamplesSpec.scala - :snippet: extractMethod-example - - -Custom Http Method ------------------- - -When you define a custom HttpMethod, you can define a route using extractMethod. - -.. includecode:: ../../../../code/docs/http/scaladsl/server/directives/CustomHttpMethodSpec.scala - :include: application-custom - diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/get.rst b/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/get.rst deleted file mode 100644 index d336db8e99..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/get.rst +++ /dev/null @@ -1,26 +0,0 @@ -.. _-get-: - -get -=== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/MethodDirectives.scala - :snippet: get - -Description ------------ -Matches requests with HTTP method ``GET``. - -This directive filters the incoming request by its HTTP method. Only requests with -method ``GET`` are passed on to the inner route. All others are rejected with a -``MethodRejection``, which is translated into a ``405 Method Not Allowed`` response -by the default :ref:`RejectionHandler `. - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MethodDirectivesExamplesSpec.scala - :snippet: get-method diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/head.rst b/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/head.rst deleted file mode 100644 index 039258fc80..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/head.rst +++ /dev/null @@ -1,29 +0,0 @@ -.. _-head-: - -head -==== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/MethodDirectives.scala - :snippet: head - -Description ------------ -Matches requests with HTTP method ``HEAD``. - -This directive filters the incoming request by its HTTP method. Only requests with -method ``HEAD`` are passed on to the inner route. All others are rejected with a -``MethodRejection``, which is translated into a ``405 Method Not Allowed`` response -by the default :ref:`RejectionHandler `. - -.. note:: By default, akka-http handles HEAD-requests transparently by dispatching a GET-request to the handler and - stripping of the result body. See the ``akka.http.server.transparent-head-requests`` setting for how to disable - this behavior. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MethodDirectivesExamplesSpec.scala - :snippet: head-method diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/index.rst b/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/index.rst deleted file mode 100644 index e5241a42f2..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/index.rst +++ /dev/null @@ -1,18 +0,0 @@ -.. _MethodDirectives: - -MethodDirectives -================ - -.. toctree:: - :maxdepth: 1 - - delete - extractMethod - get - head - method - options - overrideMethodWithParameter - patch - post - put diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/method.rst b/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/method.rst deleted file mode 100644 index ad43393cda..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/method.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. _-method-: - -method -====== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/MethodDirectives.scala - :snippet: method - -Description ------------ -Matches HTTP requests based on their method. - -This directive filters the incoming request by its HTTP method. Only requests with -the specified method are passed on to the inner route. All others are rejected with a -``MethodRejection``, which is translated into a ``405 Method Not Allowed`` response -by the default :ref:`RejectionHandler `. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MethodDirectivesExamplesSpec.scala - :snippet: method-example diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/options.rst b/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/options.rst deleted file mode 100644 index f3f0fabca9..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/options.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. _-options-: - -options -======= - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/MethodDirectives.scala - :snippet: options - -Description ------------ -Matches requests with HTTP method ``OPTIONS``. - -This directive filters the incoming request by its HTTP method. Only requests with -method ``OPTIONS`` are passed on to the inner route. All others are rejected with a -``MethodRejection``, which is translated into a ``405 Method Not Allowed`` response -by the default :ref:`RejectionHandler `. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MethodDirectivesExamplesSpec.scala - :snippet: options-method diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/overrideMethodWithParameter.rst b/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/overrideMethodWithParameter.rst deleted file mode 100644 index 2affb57809..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/overrideMethodWithParameter.rst +++ /dev/null @@ -1,31 +0,0 @@ -.. _-overrideMethodWithParameter-: - -overrideMethodWithParameter -=========================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/MethodDirectives.scala - :snippet: overrideMethodWithParameter - -Description ------------ -Changes the request method to the value of the specified query parameter. - -Changes the HTTP method of the request to the value of the specified query string parameter. -If the query string parameter is not specified this directive has no effect. - -If the query string is specified as something that is not a HTTP method, -then this directive completes the request with a ``501 Not Implemented`` response. - -This directive is useful for: - -- Use in combination with JSONP (JSONP only supports GET) -- Supporting older browsers that lack support for certain HTTP methods. E.g. IE8 does not support PATCH - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MethodDirectivesExamplesSpec.scala - :snippet: overrideMethodWithParameter-0 diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/patch.rst b/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/patch.rst deleted file mode 100644 index 224b3e04e0..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/patch.rst +++ /dev/null @@ -1,26 +0,0 @@ -.. _-patch-: - -patch -===== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/MethodDirectives.scala - :snippet: patch - -Description ------------ -Matches requests with HTTP method ``PATCH``. - -This directive filters the incoming request by its HTTP method. Only requests with -method ``PATCH`` are passed on to the inner route. All others are rejected with a -``MethodRejection``, which is translated into a ``405 Method Not Allowed`` response -by the default :ref:`RejectionHandler `. - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MethodDirectivesExamplesSpec.scala - :snippet: patch-method diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/post.rst b/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/post.rst deleted file mode 100644 index 2e4901cfcd..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/post.rst +++ /dev/null @@ -1,26 +0,0 @@ -.. _-post-: - -post -==== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/MethodDirectives.scala - :snippet: post - -Description ------------ -Matches requests with HTTP method ``POST``. - -This directive filters the incoming request by its HTTP method. Only requests with -method ``POST`` are passed on to the inner route. All others are rejected with a -``MethodRejection``, which is translated into a ``405 Method Not Allowed`` response -by the default :ref:`RejectionHandler `. - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MethodDirectivesExamplesSpec.scala - :snippet: post-method diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/put.rst b/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/put.rst deleted file mode 100644 index b824a3828d..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/method-directives/put.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. _-put-: - -put -=== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/MethodDirectives.scala - :snippet: put - -Description ------------ -Matches requests with HTTP method ``PUT``. - -This directive filters the incoming request by its HTTP method. Only requests with -method ``PUT`` are passed on to the inner route. All others are rejected with a -``MethodRejection``, which is translated into a ``405 Method Not Allowed`` response -by the default :ref:`RejectionHandler `. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MethodDirectivesExamplesSpec.scala - :snippet: put-method diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/misc-directives/extractClientIP.rst b/akka-docs/rst/scala/http/routing-dsl/directives/misc-directives/extractClientIP.rst deleted file mode 100644 index 2afd187200..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/misc-directives/extractClientIP.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. _-extractClientIP-: - -extractClientIP -=============== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/MiscDirectives.scala - :snippet: extractClientIP - -Description ------------ -Provides the value of ``X-Forwarded-For``, ``Remote-Address``, or ``X-Real-IP`` headers as an instance of ``RemoteAddress``. - -The akka-http server engine adds the ``Remote-Address`` header to every request automatically if the respective -setting ``akka.http.server.remote-address-header`` is set to ``on``. Per default it is set to ``off``. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MiscDirectivesExamplesSpec.scala - :snippet: extractClientIP-example - diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/misc-directives/index.rst b/akka-docs/rst/scala/http/routing-dsl/directives/misc-directives/index.rst deleted file mode 100644 index 9857ed1923..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/misc-directives/index.rst +++ /dev/null @@ -1,16 +0,0 @@ -.. _MiscDirectives: - -MiscDirectives -============== - -.. toctree:: - :maxdepth: 1 - - extractClientIP - rejectEmptyResponse - requestEntityEmpty - requestEntityPresent - selectPreferredLanguage - validate - withoutSizeLimit - withSizeLimit \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/misc-directives/rejectEmptyResponse.rst b/akka-docs/rst/scala/http/routing-dsl/directives/misc-directives/rejectEmptyResponse.rst deleted file mode 100644 index dd2f684a74..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/misc-directives/rejectEmptyResponse.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. _-rejectEmptyResponse-: - -rejectEmptyResponse -=================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/MiscDirectives.scala - :snippet: rejectEmptyResponse - - -Description ------------ -Replaces a response with no content with an empty rejection. - -The ``rejectEmptyResponse`` directive is mostly used with marshalling ``Option[T]`` instances. The value ``None`` is -usually marshalled to an empty but successful result. In many cases ``None`` should instead be handled as -``404 Not Found`` which is the effect of using ``rejectEmptyResponse``. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MiscDirectivesExamplesSpec.scala - :snippet: rejectEmptyResponse-example diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/misc-directives/requestEntityEmpty.rst b/akka-docs/rst/scala/http/routing-dsl/directives/misc-directives/requestEntityEmpty.rst deleted file mode 100644 index 5684a71c76..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/misc-directives/requestEntityEmpty.rst +++ /dev/null @@ -1,26 +0,0 @@ -.. _-requestEntityEmpty-: - -requestEntityEmpty -================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/MiscDirectives.scala - :snippet: requestEntityEmpty - - -Description ------------ -A filter that checks if the request entity is empty and only then passes processing to the inner route. -Otherwise, the request is rejected. - - -See also :ref:`-requestEntityPresent-` for the opposite effect. - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MiscDirectivesExamplesSpec.scala - :snippet: requestEntityEmptyPresent-example diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/misc-directives/requestEntityPresent.rst b/akka-docs/rst/scala/http/routing-dsl/directives/misc-directives/requestEntityPresent.rst deleted file mode 100644 index 6e16da12da..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/misc-directives/requestEntityPresent.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. _-requestEntityPresent-: - -requestEntityPresent -==================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/MiscDirectives.scala - :snippet: requestEntityPresent - - -Description ------------ -A simple filter that checks if the request entity is present and only then passes processing to the inner route. -Otherwise, the request is rejected. - -See also :ref:`-requestEntityEmpty-` for the opposite effect. - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MiscDirectivesExamplesSpec.scala - :snippet: requestEntityEmptyPresent-example diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/misc-directives/selectPreferredLanguage.rst b/akka-docs/rst/scala/http/routing-dsl/directives/misc-directives/selectPreferredLanguage.rst deleted file mode 100644 index 5b5b6647c1..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/misc-directives/selectPreferredLanguage.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. _-selectPreferredLanguage-: - -selectPreferredLanguage -======================= - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/MiscDirectives.scala - :snippet: selectPreferredLanguage - -Description ------------ -Inspects the request's ``Accept-Language`` header and determines, -which of a given set of language alternatives is preferred by the client according to content negotiation rules -defined by http://tools.ietf.org/html/rfc7231#section-5.3.5. - -If there are several best language alternatives that the client has equal preference for -(even if this preference is zero!) the order of the arguments is used as a tie breaker (first one wins). - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MiscDirectivesExamplesSpec.scala - :snippet: selectPreferredLanguage-example diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/misc-directives/validate.rst b/akka-docs/rst/scala/http/routing-dsl/directives/misc-directives/validate.rst deleted file mode 100644 index 8782881255..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/misc-directives/validate.rst +++ /dev/null @@ -1,23 +0,0 @@ -.. _-validate-: - -validate -======== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/MiscDirectives.scala - :snippet: validate - -Description ------------ -Allows validating a precondition before handling a route. - -Checks an arbitrary condition and passes control to the inner route if it returns ``true``. -Otherwise, rejects the request with a ``ValidationRejection`` containing the given error message. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MiscDirectivesExamplesSpec.scala - :snippet: validate-example diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/misc-directives/withSizeLimit.rst b/akka-docs/rst/scala/http/routing-dsl/directives/misc-directives/withSizeLimit.rst deleted file mode 100644 index 7f944e5fab..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/misc-directives/withSizeLimit.rst +++ /dev/null @@ -1,40 +0,0 @@ -.. _-withSizeLimit-: - -withSizeLimit -=============== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/MiscDirectives.scala - :snippet: withSizeLimit - -Description ------------ -Fails the stream with ``EntityStreamSizeException`` if its request entity size exceeds given limit. Limit given -as parameter overrides limit configured with ``akka.http.parsing.max-content-length``. - -The whole mechanism of entity size checking is intended to prevent certain Denial-of-Service attacks. -So suggested setup is to have ``akka.http.parsing.max-content-length`` relatively low and use ``withSizeLimit`` -directive for endpoints which expects bigger entities. - -See also :ref:`-withoutSizeLimit-` for skipping request entity size check. - -Examples --------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MiscDirectivesExamplesSpec.scala - :snippet: withSizeLimit-example - -Beware that request entity size check is executed when entity is consumed. Therefore in the following example -even request with entity greater than argument to ``withSizeLimit`` will succeed (because this route -does not consume entity): - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MiscDirectivesExamplesSpec.scala - :snippet: withSizeLimit-execution-moment-example - -Directive ``withSizeLimit`` is implemented in terms of ``HttpEntity.withSizeLimit`` which means that in case of -nested ``withSizeLimit`` directives the innermost is applied: - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MiscDirectivesExamplesSpec.scala - :snippet: withSizeLimit-nested-example diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/misc-directives/withoutSizeLimit.rst b/akka-docs/rst/scala/http/routing-dsl/directives/misc-directives/withoutSizeLimit.rst deleted file mode 100644 index 8833e0e90c..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/misc-directives/withoutSizeLimit.rst +++ /dev/null @@ -1,26 +0,0 @@ -.. _-withoutSizeLimit-: - -withoutSizeLimit -================ - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/MiscDirectives.scala - :snippet: withoutSizeLimit - -Description ------------ -Skips request entity size verification. - -The whole mechanism of entity size checking is intended to prevent certain Denial-of-Service attacks. -So suggested setup is to have ``akka.http.parsing.max-content-length`` relatively low and use ``withoutSizeLimit`` -directive just for endpoints for which size verification should not be performed. - -See also :ref:`-withSizeLimit-` for setting request entity size limit. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MiscDirectivesExamplesSpec.scala - :snippet: withoutSizeLimit-example diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/parameter-directives/index.rst b/akka-docs/rst/scala/http/routing-dsl/directives/parameter-directives/index.rst deleted file mode 100644 index 81b355bb30..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/parameter-directives/index.rst +++ /dev/null @@ -1,43 +0,0 @@ -.. _ParameterDirectives: - -ParameterDirectives -=================== - -.. toctree:: - :maxdepth: 1 - - parameter - parameterMap - parameterMultiMap - parameters - parameterSeq - -.. _which-parameter-directive: - -When to use which parameter directive? --------------------------------------- - -Usually, you want to use the high-level :ref:`-parameters-scala-` directive. When you need -more low-level access you can use the table below to decide which directive -to use which shows properties of different parameter directives. - -================================ ====== ======== ===== -directive level ordering multi -================================ ====== ======== ===== -:ref:`-parameter-` high no no -:ref:`-parameters-scala-` high no yes -:ref:`-parameterMap-` low no no -:ref:`-parameterMultiMap-` low no yes -:ref:`-parameterSeq-` low yes yes -================================ ====== ======== ===== - -level - high-level parameter directives extract subset of all parameters by name and allow conversions - and automatically report errors if expectations are not met, low-level directives give you - all parameters at once, leaving all further processing to you - -ordering - original ordering from request URL is preserved - -multi - multiple values per parameter name are possible diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/parameter-directives/parameter.rst b/akka-docs/rst/scala/http/routing-dsl/directives/parameter-directives/parameter.rst deleted file mode 100644 index 2151694d97..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/parameter-directives/parameter.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. _-parameter-: - -parameter -========= - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/ParameterDirectives.scala - :snippet: parameter - -Description ------------ -Extracts a *query* parameter value from the request. - -See :ref:`-parameters-scala-` for a detailed description of this directive. - -See :ref:`which-parameter-directive` to understand when to use which directive. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/ParameterDirectivesExamplesSpec.scala - :snippet: example-1 diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/parameter-directives/parameterMap.rst b/akka-docs/rst/scala/http/routing-dsl/directives/parameter-directives/parameterMap.rst deleted file mode 100644 index 531d6f2e77..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/parameter-directives/parameterMap.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. _-parameterMap-: - -parameterMap -============ - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/ParameterDirectives.scala - :snippet: parameterMap - -Description ------------ -Extracts all parameters at once as a ``Map[String, String]`` mapping parameter names to parameter values. - -If a query contains a parameter value several times, the map will contain the last one. - -See also :ref:`which-parameter-directive` to understand when to use which directive. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/ParameterDirectivesExamplesSpec.scala - :snippet: parameterMap diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/parameter-directives/parameterMultiMap.rst b/akka-docs/rst/scala/http/routing-dsl/directives/parameter-directives/parameterMultiMap.rst deleted file mode 100644 index 163d033975..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/parameter-directives/parameterMultiMap.rst +++ /dev/null @@ -1,28 +0,0 @@ -.. _-parameterMultiMap-: - -parameterMultiMap -================= - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/ParameterDirectives.scala - :snippet: parameterMultiMap - -Description ------------ - -Extracts all parameters at once as a multi-map of type ``Map[String, List[String]`` mapping -a parameter name to a list of all its values. - -This directive can be used if parameters can occur several times. - -The order of values is *not* specified. - -See :ref:`which-parameter-directive` to understand when to use which directive. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/ParameterDirectivesExamplesSpec.scala - :snippet: parameterMultiMap diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/parameter-directives/parameterSeq.rst b/akka-docs/rst/scala/http/routing-dsl/directives/parameter-directives/parameterSeq.rst deleted file mode 100644 index fbcf145369..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/parameter-directives/parameterSeq.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. _-parameterSeq-: - -parameterSeq -============ - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/ParameterDirectives.scala - :snippet: parameterSeq - -Description ------------ -Extracts all parameters at once in the original order as (name, value) tuples of type ``(String, String)``. - -This directive can be used if the exact order of parameters is important or if parameters can occur several times. - -See :ref:`which-parameter-directive` to understand when to use which directive. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/ParameterDirectivesExamplesSpec.scala - :snippet: parameterSeq diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/parameter-directives/parameters.rst b/akka-docs/rst/scala/http/routing-dsl/directives/parameter-directives/parameters.rst deleted file mode 100644 index 082586e356..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/parameter-directives/parameters.rst +++ /dev/null @@ -1,107 +0,0 @@ -.. _-parameters-scala-: - -parameters -========== - -Signature ---------- - -:: - - def parameters(param: ): Directive1[T] - def parameters(params: *): Directive[T_0 :: ... T_i ... :: HNil] - def parameters(params: :: ... ... :: HNil): Directive[T_0 :: ... T_i ... :: HNil] - -The signature shown is simplified and written in pseudo-syntax, the real signature uses magnets. [1]_ The type -```` doesn't really exist but consists of the syntactic variants as shown in the description and the examples. - -.. [1] See `The Magnet Pattern`_ for an explanation of magnet-based overloading. -.. _`The Magnet Pattern`: http://spray.io/blog/2012-12-13-the-magnet-pattern/ - -Description ------------ -The parameters directive filters on the existence of several query parameters and extract their values. - -Query parameters can be either extracted as a String or can be converted to another type. The parameter name -can be supplied either as a String or as a Symbol. Parameter extraction can be modified to mark a query parameter -as required, optional, or repeated, or to filter requests where a parameter has a certain value: - -``"color"`` - extract value of parameter "color" as ``String`` -``"color".?`` - extract optional value of parameter "color" as ``Option[String]`` -``"color" ? "red"`` - extract optional value of parameter "color" as ``String`` with default value ``"red"`` -``"color" ! "blue"`` - require value of parameter "color" to be ``"blue"`` and extract nothing -``"amount".as[Int]`` - extract value of parameter "amount" as ``Int``, you need a matching ``Deserializer`` in scope for that to work - (see also :ref:`http-unmarshalling-scala`) -``"amount".as(deserializer)`` - extract value of parameter "amount" with an explicit ``Deserializer`` -``"distance".*`` - extract multiple occurrences of parameter "distance" as ``Iterable[String]`` -``"distance".as[Int].*`` - extract multiple occurrences of parameter "distance" as ``Iterable[Int]``, you need a matching ``Deserializer`` in scope for that to work - (see also :ref:`http-unmarshalling-scala`) -``"distance".as(deserializer).*`` - extract multiple occurrences of parameter "distance" with an explicit ``Deserializer`` - -You can use :ref:`Case Class Extraction` to group several extracted values together into a case-class -instance. - -Requests missing a required parameter or parameter value will be rejected with an appropriate rejection. - -There's also a singular version, :ref:`-parameter-`. Form fields can be handled in a similar way, see ``formFields``. If -you want unified handling for both query parameters and form fields, see ``anyParams``. - -Examples --------- - -Required parameter -^^^^^^^^^^^^^^^^^^ - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/ParameterDirectivesExamplesSpec.scala - :snippet: required-1 - -Optional parameter -^^^^^^^^^^^^^^^^^^ - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/ParameterDirectivesExamplesSpec.scala - :snippet: optional - -Optional parameter with default value -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/ParameterDirectivesExamplesSpec.scala - :snippet: optional-with-default - -Parameter with required value -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/ParameterDirectivesExamplesSpec.scala - :snippet: required-value - -Deserialized parameter -^^^^^^^^^^^^^^^^^^^^^^ - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/ParameterDirectivesExamplesSpec.scala - :snippet: mapped-value - -Repeated parameter -^^^^^^^^^^^^^^^^^^ - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/ParameterDirectivesExamplesSpec.scala - :snippet: repeated - -CSV parameter -^^^^^^^^^^^^^ - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/ParameterDirectivesExamplesSpec.scala - :snippet: csv - -Repeated, deserialized parameter -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/ParameterDirectivesExamplesSpec.scala - :snippet: mapped-repeated diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/index.rst b/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/index.rst deleted file mode 100644 index 1a126e783d..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/index.rst +++ /dev/null @@ -1,21 +0,0 @@ -.. _PathDirectives: - -PathDirectives -============== - -.. toctree:: - :maxdepth: 1 - - path - pathEnd - pathEndOrSingleSlash - pathPrefix - pathPrefixTest - pathSingleSlash - pathSuffix - pathSuffixTest - rawPathPrefix - rawPathPrefixTest - redirectToNoTrailingSlashIfPresent - redirectToTrailingSlashIfMissing - ../../path-matchers diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/path.rst b/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/path.rst deleted file mode 100644 index 0beac3c264..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/path.rst +++ /dev/null @@ -1,43 +0,0 @@ -.. _-path-: - -path -==== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/PathDirectives.scala - :snippet: path - - -Description ------------ -Matches the complete unmatched path of the ``RequestContext`` against the given ``PathMatcher``, potentially extracts -one or more values (depending on the type of the argument). - -This directive filters incoming requests based on the part of their URI that hasn't been matched yet by other -potentially existing :ref:`-pathPrefix-` directives on higher levels of the routing structure. -Its one parameter is usually an expression evaluating to a ``PathMatcher`` instance (see also: :ref:`pathmatcher-dsl`). - -As opposed to the :ref:`-rawPathPrefix-` or :ref:`-rawPathPrefixTest-` directives ``path`` automatically adds a leading -slash to its ``PathMatcher`` argument, you therefore don't have to start your matching expression with an explicit slash. - -The ``path`` directive attempts to match the **complete** remaining path, not just a prefix. If you only want to match -a path prefix and then delegate further filtering to a lower level in your routing structure use the :ref:`-pathPrefix-` -directive instead. As a consequence it doesn't make sense to nest a ``path`` or :ref:`-pathPrefix-` directive -underneath another ``path`` directive, as there is no way that they will ever match (since the unmatched path underneath -a ``path`` directive will always be empty). - -Depending on the type of its ``PathMatcher`` argument the ``path`` directive extracts zero or more values from the URI. -If the match fails the request is rejected with an :ref:`empty rejection set `. - -.. note:: The empty string (also called empty word or identity) is a **neutral element** of string concatenation operation, - so it will match everything, but remember that ``path`` requires whole remaining path being matched, so (``/``) will succeed - and (``/whatever``) will fail. The :ref:`-pathPrefix-` provides more liberal behaviour. - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/PathDirectivesExamplesSpec.scala - :snippet: path-example diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/pathEnd.rst b/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/pathEnd.rst deleted file mode 100644 index df70df9073..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/pathEnd.rst +++ /dev/null @@ -1,27 +0,0 @@ -.. _-pathEnd-: - -pathEnd -======= - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/PathDirectives.scala - :snippet: pathEnd - - -Description ------------ -Only passes the request to its inner route if the unmatched path of the ``RequestContext`` is empty, i.e. the request -path has been fully matched by a higher-level :ref:`-path-` or :ref:`-pathPrefix-` directive. - - -This directive is a simple alias for ``rawPathPrefix(PathEnd)`` and is mostly used on an -inner-level to discriminate "path already fully matched" from other alternatives (see the example below). - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/PathDirectivesExamplesSpec.scala - :snippet: pathEnd- \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/pathEndOrSingleSlash.rst b/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/pathEndOrSingleSlash.rst deleted file mode 100644 index 6fd330089d..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/pathEndOrSingleSlash.rst +++ /dev/null @@ -1,28 +0,0 @@ -.. _-pathEndOrSingleSlash-: - -pathEndOrSingleSlash -==================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/PathDirectives.scala - :snippet: pathEndOrSingleSlash - - -Description ------------ -Only passes the request to its inner route if the unmatched path of the ``RequestContext`` is either empty -or contains only one single slash. - -This directive is a simple alias for ``rawPathPrefix(Slash.? ~ PathEnd)`` and is mostly used on an inner-level to -discriminate "path already fully matched" from other alternatives (see the example below). - -It is equivalent to ``pathEnd | pathSingleSlash`` but slightly more efficient. - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/PathDirectivesExamplesSpec.scala - :snippet: pathEndOrSingleSlash- \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/pathPrefix.rst b/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/pathPrefix.rst deleted file mode 100644 index 579ab99d55..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/pathPrefix.rst +++ /dev/null @@ -1,36 +0,0 @@ -.. _-pathPrefix-: - -pathPrefix -========== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/PathDirectives.scala - :snippet: pathPrefix - - -Description ------------ -Matches and consumes a prefix of the unmatched path of the ``RequestContext`` against the given ``PathMatcher``, -potentially extracts one or more values (depending on the type of the argument). - -This directive filters incoming requests based on the part of their URI that hasn't been matched yet by other -potentially existing ``pathPrefix`` or :ref:`-rawPathPrefix-` directives on higher levels of the routing structure. -Its one parameter is usually an expression evaluating to a ``PathMatcher`` instance (see also: :ref:`pathmatcher-dsl`). - -As opposed to its :ref:`-rawPathPrefix-` counterpart ``pathPrefix`` automatically adds a leading slash to its -``PathMatcher`` argument, you therefore don't have to start your matching expression with an explicit slash. - -Depending on the type of its ``PathMatcher`` argument the ``pathPrefix`` directive extracts zero or more values from -the URI. If the match fails the request is rejected with an :ref:`empty rejection set `. - -.. note:: The empty string (also called empty word or identity) is a **neutral element** of string concatenation operation, - so it will match everything and consume nothing. The :ref:`-path-` provides more strict behaviour. - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/PathDirectivesExamplesSpec.scala - :snippet: pathPrefix- \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/pathPrefixTest.rst b/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/pathPrefixTest.rst deleted file mode 100644 index c29db430b2..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/pathPrefixTest.rst +++ /dev/null @@ -1,36 +0,0 @@ -.. _-pathPrefixTest-: - -pathPrefixTest -============== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/PathDirectives.scala - :snippet: pathPrefixTest - - -Description ------------ -Checks whether the unmatched path of the ``RequestContext`` has a prefix matched by the given ``PathMatcher``. -Potentially extracts one or more values (depending on the type of the argument) but doesn't consume its match from -the unmatched path. - -This directive is very similar to the :ref:`-pathPrefix-` directive with the one difference that the path prefix -it matched (if it matched) is *not* consumed. The unmatched path of the ``RequestContext`` is therefore left as -is even in the case that the directive successfully matched and the request is passed on to its inner route. - -For more info on how to create a ``PathMatcher`` see :ref:`pathmatcher-dsl`. - -As opposed to its :ref:`-rawPathPrefixTest-` counterpart ``pathPrefixTest`` automatically adds a leading slash to its -``PathMatcher`` argument, you therefore don't have to start your matching expression with an explicit slash. - -Depending on the type of its ``PathMatcher`` argument the ``pathPrefixTest`` directive extracts zero or more values from -the URI. If the match fails the request is rejected with an :ref:`empty rejection set `. - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/PathDirectivesExamplesSpec.scala - :snippet: pathPrefixTest- \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/pathSingleSlash.rst b/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/pathSingleSlash.rst deleted file mode 100644 index 21cf47b7bd..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/pathSingleSlash.rst +++ /dev/null @@ -1,26 +0,0 @@ -.. _-pathSingleSlash-: - -pathSingleSlash -=============== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/PathDirectives.scala - :snippet: pathSingleSlash - - -Description ------------ -Only passes the request to its inner route if the unmatched path of the ``RequestContext`` -contains exactly one single slash. - -This directive is a simple alias for ``pathPrefix(PathEnd)`` and is mostly used for matching requests to the root URI -(``/``) on an inner-level to discriminate "all path segments matched" from other alternatives (see the example below). - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/PathDirectivesExamplesSpec.scala - :snippet: pathSingleSlash- \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/pathSuffix.rst b/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/pathSuffix.rst deleted file mode 100644 index 2179e99d29..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/pathSuffix.rst +++ /dev/null @@ -1,36 +0,0 @@ -.. _-pathSuffix-: - -pathSuffix -========== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/PathDirectives.scala - :snippet: pathSuffix - - -Description ------------ -Matches and consumes a suffix of the unmatched path of the ``RequestContext`` against the given ``PathMatcher``, -potentially extracts one or more values (depending on the type of the argument). - -This directive filters incoming requests based on the part of their URI that hasn't been matched yet by other -potentially existing path matching directives on higher levels of the routing structure. -Its one parameter is usually an expression evaluating to a ``PathMatcher`` instance (see also: :ref:`pathmatcher-dsl`). - -As opposed to :ref:`-pathPrefix-` this directive matches and consumes the unmatched path from the right, i.e. the end. - -.. caution:: For efficiency reasons, the given ``PathMatcher`` must match the desired suffix in reversed-segment - order, i.e. ``pathSuffix("baz" / "bar")`` would match ``/foo/bar/baz``! The order within a segment match is - not reversed. - -Depending on the type of its ``PathMatcher`` argument the ``pathPrefix`` directive extracts zero or more values from -the URI. If the match fails the request is rejected with an :ref:`empty rejection set `. - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/PathDirectivesExamplesSpec.scala - :snippet: pathSuffix- \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/pathSuffixTest.rst b/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/pathSuffixTest.rst deleted file mode 100644 index e6c9b91137..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/pathSuffixTest.rst +++ /dev/null @@ -1,37 +0,0 @@ -.. _-pathSuffixTest-: - -pathSuffixTest -============== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/PathDirectives.scala - :snippet: pathSuffixTest - - -Description ------------ -Checks whether the unmatched path of the ``RequestContext`` has a suffix matched by the given ``PathMatcher``. -Potentially extracts one or more values (depending on the type of the argument) but doesn't consume its match from -the unmatched path. - -This directive is very similar to the :ref:`-pathSuffix-` directive with the one difference that the path suffix -it matched (if it matched) is *not* consumed. The unmatched path of the ``RequestContext`` is therefore left as -is even in the case that the directive successfully matched and the request is passed on to its inner route. - -As opposed to :ref:`-pathPrefixTest-` this directive matches and consumes the unmatched path from the right, i.e. the end. - -.. caution:: For efficiency reasons, the given ``PathMatcher`` must match the desired suffix in reversed-segment - order, i.e. ``pathSuffixTest("baz" / "bar")`` would match ``/foo/bar/baz``! The order within a segment match is - not reversed. - -Depending on the type of its ``PathMatcher`` argument the ``pathSuffixTest`` directive extracts zero or more values from -the URI. If the match fails the request is rejected with an :ref:`empty rejection set `. - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/PathDirectivesExamplesSpec.scala - :snippet: pathSuffixTest- \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/rawPathPrefix.rst b/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/rawPathPrefix.rst deleted file mode 100644 index 0b81ec382a..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/rawPathPrefix.rst +++ /dev/null @@ -1,33 +0,0 @@ -.. _-rawPathPrefix-: - -rawPathPrefix -============= - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/PathDirectives.scala - :snippet: rawPathPrefix - - -Description ------------ -Matches and consumes a prefix of the unmatched path of the ``RequestContext`` against the given ``PathMatcher``, -potentially extracts one or more values (depending on the type of the argument). - -This directive filters incoming requests based on the part of their URI that hasn't been matched yet by other -potentially existing ``rawPathPrefix`` or :ref:`-pathPrefix-` directives on higher levels of the routing structure. -Its one parameter is usually an expression evaluating to a ``PathMatcher`` instance (see also: :ref:`pathmatcher-dsl`). - -As opposed to its :ref:`-pathPrefix-` counterpart ``rawPathPrefix`` does *not* automatically add a leading slash to its -``PathMatcher`` argument. Rather its ``PathMatcher`` argument is applied to the unmatched path as is. - -Depending on the type of its ``PathMatcher`` argument the ``rawPathPrefix`` directive extracts zero or more values from -the URI. If the match fails the request is rejected with an :ref:`empty rejection set `. - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/PathDirectivesExamplesSpec.scala - :snippet: rawPathPrefix- \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/rawPathPrefixTest.rst b/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/rawPathPrefixTest.rst deleted file mode 100644 index 8886437bc0..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/rawPathPrefixTest.rst +++ /dev/null @@ -1,36 +0,0 @@ -.. _-rawPathPrefixTest-: - -rawPathPrefixTest -================= - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/PathDirectives.scala - :snippet: rawPathPrefixTest - - -Description ------------ -Checks whether the unmatched path of the ``RequestContext`` has a prefix matched by the given ``PathMatcher``. -Potentially extracts one or more values (depending on the type of the argument) but doesn't consume its match from -the unmatched path. - -This directive is very similar to the :ref:`-pathPrefix-` directive with the one difference that the path prefix -it matched (if it matched) is *not* consumed. The unmatched path of the ``RequestContext`` is therefore left as -is even in the case that the directive successfully matched and the request is passed on to its inner route. - -For more info on how to create a ``PathMatcher`` see :ref:`pathmatcher-dsl`. - -As opposed to its :ref:`-pathPrefixTest-` counterpart ``rawPathPrefixTest`` does *not* automatically add a leading slash -to its ``PathMatcher`` argument. Rather its ``PathMatcher`` argument is applied to the unmatched path as is. - -Depending on the type of its ``PathMatcher`` argument the ``rawPathPrefixTest`` directive extracts zero or more values -from the URI. If the match fails the request is rejected with an :ref:`empty rejection set `. - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/PathDirectivesExamplesSpec.scala - :snippet: rawPathPrefixTest- \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/redirectToNoTrailingSlashIfPresent.rst b/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/redirectToNoTrailingSlashIfPresent.rst deleted file mode 100644 index a1ecfe524c..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/redirectToNoTrailingSlashIfPresent.rst +++ /dev/null @@ -1,37 +0,0 @@ -.. _-redirectToNoTrailingSlashIfPresent-: - -redirectToNoTrailingSlashIfPresent -================================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/PathDirectives.scala - :snippet: redirectToNoTrailingSlashIfPresent - -Description ------------ -If the requested path does end with a trailing ``/`` character, -redirects to the same path without that trailing slash.. - -Redirects the HTTP Client to the same resource yet without the trailing ``/``, in case the request contained it. -When redirecting an HttpResponse with the given redirect response code (i.e. ``MovedPermanently`` or ``TemporaryRedirect`` -etc.) as well as a simple HTML page containing a "*click me to follow redirect*" link to be used in case the client can not, -or refuses to for security reasons, automatically follow redirects. - -Please note that the inner paths **MUST NOT** end with an explicit trailing slash (e.g. ``"things"./``) -for the re-directed-to route to match. - -A good read on the subject of how to deal with trailing slashes is available on `Google Webmaster Central - To Slash or not to Slash`_. - -See also :ref:`-redirectToTrailingSlashIfMissing-` for the opposite behaviour. - -.. _Google Webmaster Central - To Slash or not to Slash: http://googlewebmastercentral.blogspot.de/2010/04/to-slash-or-not-to-slash.html - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/PathDirectivesExamplesSpec.scala - :snippet: redirectToNoTrailingSlashIfPresent-0 - -See also :ref:`-redirectToTrailingSlashIfMissing-` which achieves the opposite - redirecting paths in case they do *not* have a trailing slash. \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/redirectToTrailingSlashIfMissing.rst b/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/redirectToTrailingSlashIfMissing.rst deleted file mode 100644 index 0792a5f246..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/path-directives/redirectToTrailingSlashIfMissing.rst +++ /dev/null @@ -1,33 +0,0 @@ -.. _-redirectToTrailingSlashIfMissing-: - -redirectToTrailingSlashIfMissing -================================ - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/PathDirectives.scala - :snippet: redirectToTrailingSlashIfMissing - -Description ------------ -If the requested path does not end with a trailing ``/`` character, -redirects to the same path followed by such trailing slash. - -Redirects the HTTP Client to the same resource yet followed by a trailing ``/``, in case the request did not contain it. -When redirecting an HttpResponse with the given redirect response code (i.e. ``MovedPermanently`` or ``TemporaryRedirect`` -etc.) as well as a simple HTML page containing a "*click me to follow redirect*" link to be used in case the client can not, -or refuses to for security reasons, automatically follow redirects. - -Please note that the inner paths **MUST** end with an explicit trailing slash (e.g. ``"things"./``) for the -re-directed-to route to match. - -See also :ref:`-redirectToNoTrailingSlashIfPresent-` for the opposite behaviour. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/PathDirectivesExamplesSpec.scala - :snippet: redirectToTrailingSlashIfMissing-0 - -See also :ref:`-redirectToNoTrailingSlashIfPresent-` which achieves the opposite - redirecting paths in case they do have a trailing slash. \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/range-directives/index.rst b/akka-docs/rst/scala/http/routing-dsl/directives/range-directives/index.rst deleted file mode 100644 index 8b0bb94e88..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/range-directives/index.rst +++ /dev/null @@ -1,9 +0,0 @@ -.. _RangeDirectives: - -RangeDirectives -=============== - -.. toctree:: - :maxdepth: 1 - - withRangeSupport \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/range-directives/withRangeSupport.rst b/akka-docs/rst/scala/http/routing-dsl/directives/range-directives/withRangeSupport.rst deleted file mode 100644 index b078d9efd4..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/range-directives/withRangeSupport.rst +++ /dev/null @@ -1,46 +0,0 @@ -.. _-withRangeSupport-: - -withRangeSupport -================ - -Signature ---------- - -:: - - def withRangeSupport(): Directive0 - def withRangeSupport(rangeCountLimit: Int, rangeCoalescingThreshold:Long): Directive0 - -The signature shown is simplified, the real signature uses magnets. [1]_ - -.. [1] See `The Magnet Pattern`_ for an explanation of magnet-based overloading. -.. _`The Magnet Pattern`: http://spray.io/blog/2012-12-13-the-magnet-pattern/ - - -Description ------------ -Transforms the response from its inner route into a ``206 Partial Content`` -response if the client requested only part of the resource with a ``Range`` header. - -Augments responses to ``GET`` requests with an ``Accept-Ranges: bytes`` header and converts them into partial responses -if the request contains a valid ``Range`` request header. The requested byte-ranges are coalesced (merged) if they -lie closer together than the specified ``rangeCoalescingThreshold`` argument. - -In order to prevent the server from becoming overloaded with trying to prepare ``multipart/byteranges`` responses for -high numbers of potentially very small ranges the directive rejects requests requesting more than ``rangeCountLimit`` -ranges with a ``TooManyRangesRejection``. -Requests with unsatisfiable ranges are rejected with an ``UnsatisfiableRangeRejection``. - -The ``withRangeSupport()`` form (without parameters) uses the ``range-coalescing-threshold`` and ``range-count-limit`` -settings from the ``akka.http.routing`` configuration. - -This directive is transparent to non-``GET`` requests. - -See also: https://tools.ietf.org/html/rfc7233 - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/RangeDirectivesExamplesSpec.scala - :snippet: withRangeSupport diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/respond-with-directives/index.rst b/akka-docs/rst/scala/http/routing-dsl/directives/respond-with-directives/index.rst deleted file mode 100644 index 7232fefa57..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/respond-with-directives/index.rst +++ /dev/null @@ -1,13 +0,0 @@ -.. _RespondWithDirectives: - -RespondWithDirectives -===================== - -.. toctree:: - :maxdepth: 1 - - respondWithDefaultHeader - respondWithDefaultHeaders - respondWithHeader - respondWithHeaders - respondWithHeaders \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/respond-with-directives/respondWithDefaultHeader.rst b/akka-docs/rst/scala/http/routing-dsl/directives/respond-with-directives/respondWithDefaultHeader.rst deleted file mode 100644 index 6e6788ea10..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/respond-with-directives/respondWithDefaultHeader.rst +++ /dev/null @@ -1,30 +0,0 @@ -.. _-respondWithDefaultHeader-: - -respondWithDefaultHeader -======================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/RespondWithDirectives.scala - :snippet: respondWithDefaultHeader - - -Description ------------ -Adds a given HTTP header to all responses coming back from its inner route only if a header with the same name doesn't -exist yet in the response. - - -This directive transforms ``HttpResponse`` and ``ChunkedResponseStart`` messages coming back from its inner route by -potentially adding the given ``HttpHeader`` instance to the headers list. -The header is only added if there is no header instance with the same name (case insensitively) already present in the -response. - -See also :ref:`-respondWithDefaultHeaders-` if you'd like to add more than one header. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/RespondWithDirectivesExamplesSpec.scala - :snippet: respondWithDefaultHeader-0 diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/respond-with-directives/respondWithDefaultHeaders.rst b/akka-docs/rst/scala/http/routing-dsl/directives/respond-with-directives/respondWithDefaultHeaders.rst deleted file mode 100644 index a9a83f844b..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/respond-with-directives/respondWithDefaultHeaders.rst +++ /dev/null @@ -1,43 +0,0 @@ -.. _-respondWithDefaultHeaders-: - -respondWithDefaultHeaders -========================= - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/RespondWithDirectives.scala - :snippet: respondWithDefaultHeaders - - -Description ------------ -Adds the given HTTP headers to all responses coming back from its inner route only if a respective header with the same -name doesn't exist yet in the response. - - -This directive transforms ``HttpResponse`` and ``ChunkedResponseStart`` messages coming back from its inner route by -potentially adding the given ``HttpHeader`` instances to the headers list. -A header is only added if there is no header instance with the same name (case insensitively) already present in the -response. - -See also :ref:`-respondWithDefaultHeader-` if you'd like to add only a single header. - - -Example -------- - -The ``respondWithDefaultHeaders`` directive is equivalent to the ``respondWithDefaultHeader`` directive which -is shown in the example below, however it allows including multiple default headers at once in the directive, like so:: - - respondWithDefaultHeaders( - Origin(HttpOrigin("http://akka.io"), - RawHeader("X-Fish-Name", "Blippy"))) { /*...*/ } - - -The semantics remain the same however, as explained by the following example: - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/RespondWithDirectivesExamplesSpec.scala - :snippet: respondWithDefaultHeader-0 - -See the :ref:`-respondWithDefaultHeader-` directive for an example with only one header. \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/respond-with-directives/respondWithHeader.rst b/akka-docs/rst/scala/http/routing-dsl/directives/respond-with-directives/respondWithHeader.rst deleted file mode 100644 index cd72217be7..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/respond-with-directives/respondWithHeader.rst +++ /dev/null @@ -1,26 +0,0 @@ -.. _-respondWithHeader-: - -respondWithHeader -================= - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/RespondWithDirectives.scala - :snippet: respondWithHeader - - -Description ------------ -Adds a given HTTP header to all responses coming back from its inner route. - -This directive transforms ``HttpResponse`` and ``ChunkedResponseStart`` messages coming back from its inner route by -adding the given ``HttpHeader`` instance to the headers list. - -See also :ref:`-respondWithHeaders-` if you'd like to add more than one header. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/RespondWithDirectivesExamplesSpec.scala - :snippet: respondWithHeader-0 \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/respond-with-directives/respondWithHeaders.rst b/akka-docs/rst/scala/http/routing-dsl/directives/respond-with-directives/respondWithHeaders.rst deleted file mode 100644 index 8a2ee42efa..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/respond-with-directives/respondWithHeaders.rst +++ /dev/null @@ -1,27 +0,0 @@ -.. _-respondWithHeaders-: - -respondWithHeaders -================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/RespondWithDirectives.scala - :snippet: respondWithHeaders - - -Description ------------ -Adds the given HTTP headers to all responses coming back from its inner route. - -This directive transforms ``HttpResponse`` and ``ChunkedResponseStart`` messages coming back from its inner route by -adding the given ``HttpHeader`` instances to the headers list. - -See also :ref:`-respondWithHeader-` if you'd like to add just a single header. - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/RespondWithDirectivesExamplesSpec.scala - :snippet: respondWithHeaders-0 \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/route-directives/complete.rst b/akka-docs/rst/scala/http/routing-dsl/directives/route-directives/complete.rst deleted file mode 100644 index 780f23e87f..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/route-directives/complete.rst +++ /dev/null @@ -1,39 +0,0 @@ -.. _-complete-: - -complete -======== - -Signature ---------- - -:: - - def complete[T :ToResponseMarshaller](value: T): StandardRoute - def complete(response: HttpResponse): StandardRoute - def complete(status: StatusCode): StandardRoute - def complete[T :Marshaller](status: StatusCode, value: T): StandardRoute - def complete[T :Marshaller](status: Int, value: T): StandardRoute - def complete[T :Marshaller](status: StatusCode, headers: Seq[HttpHeader], value: T): StandardRoute - def complete[T :Marshaller](status: Int, headers: Seq[HttpHeader], value: T): StandardRoute - -The signature shown is simplified, the real signature uses magnets. [1]_ - -.. [1] See `The Magnet Pattern `_ for an explanation of magnet-based overloading. - - -Description ------------ - -Completes the request using the given argument(s). - -``complete`` uses the given arguments to construct a ``Route`` which simply calls ``complete`` on the ``RequestContext`` -with the respective ``HttpResponse`` instance. -Completing the request will send the response "back up" the route structure where all the logic runs that wrapping -directives have potentially chained into the :ref:`RouteResult` future transformation chain. - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/RouteDirectivesExamplesSpec.scala - :snippet: complete-examples \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/route-directives/failWith.rst b/akka-docs/rst/scala/http/routing-dsl/directives/route-directives/failWith.rst deleted file mode 100644 index 72614c8a13..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/route-directives/failWith.rst +++ /dev/null @@ -1,36 +0,0 @@ -.. _-failWith-: - -failWith -======== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/RouteDirectives.scala - :snippet: failWith - - -Description ------------ -Bubbles up the given error through the route structure where it is dealt with by the closest ``handleExceptions`` -directive and its :class:`ExceptionHandler`. - -``failWith`` explicitly raises an exception that gets bubbled up through the route structure to be picked up by the -nearest ``handleExceptions`` directive. Using ``failWith`` rather than simply throwing an exception enables the route -structure's :ref:`exception-handling-scala` mechanism to deal with the exception even if the current route is executed -asynchronously on another thread (e.g. in a ``Future`` or separate actor). - -If no ``handleExceptions`` is present above the respective location in the -route structure the top-level routing logic will handle the exception and translate it into a corresponding -``HttpResponse`` using the in-scope ``ExceptionHandler`` (see also the :ref:`exception-handling-scala` chapter). - -There is one notable special case: If the given exception is a ``RejectionError`` exception it is *not* bubbled up, -but rather the wrapped exception is unpacked and "executed". This allows the "tunneling" of a rejection via an -exception. - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/RouteDirectivesExamplesSpec.scala - :snippet: failwith-examples \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/route-directives/index.rst b/akka-docs/rst/scala/http/routing-dsl/directives/route-directives/index.rst deleted file mode 100644 index 64e541110b..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/route-directives/index.rst +++ /dev/null @@ -1,21 +0,0 @@ -.. _RouteDirectives: - -RouteDirectives -=============== - -The ``RouteDirectives`` have a special role in akka-http's routing DSL. Contrary to all other directives (except most -:ref:`FileAndResourceDirectives`) they do not produce instances of type ``Directive[L <: HList]`` but rather "plain" -routes of type ``Route``. -The reason is that the ``RouteDirectives`` are not meant for wrapping an inner route (like most other directives, as -intermediate-level elements of a route structure, do) but rather form the leaves of the actual route structure **leaves**. - -So in most cases the inner-most element of a route structure branch is one of the ``RouteDirectives`` (or -:ref:`FileAndResourceDirectives`): - -.. toctree:: - :maxdepth: 1 - - complete - failWith - redirect - reject \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/route-directives/redirect.rst b/akka-docs/rst/scala/http/routing-dsl/directives/route-directives/redirect.rst deleted file mode 100644 index 38ce11ae54..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/route-directives/redirect.rst +++ /dev/null @@ -1,28 +0,0 @@ -.. _-redirect-: - -redirect -======== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/RouteDirectives.scala - :snippet: redirect - - -Description ------------ -Completes the request with a redirection response to a given targer URI and of a given redirection type (status code). - -``redirect`` is a convenience helper for completing the request with a redirection response. -It is equivalent to this snippet relying on the ``complete`` method on ``RequestContext`` (a directive is also available): - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/RequestContextImpl.scala - :snippet: red-impl - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/RouteDirectivesExamplesSpec.scala - :snippet: redirect-examples diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/route-directives/reject.rst b/akka-docs/rst/scala/http/routing-dsl/directives/route-directives/reject.rst deleted file mode 100644 index d925b0b765..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/route-directives/reject.rst +++ /dev/null @@ -1,31 +0,0 @@ -.. _-reject-: - -reject -====== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/RouteDirectives.scala - :snippet: reject - - -Description ------------ -Explicitly rejects the request optionally using the given rejection(s). - -``reject`` uses the given rejection instances (which might be the empty ``Seq``) to construct a ``Route`` which simply -calls ``requestContext.reject``. See the chapter on :ref:`rejections-scala` for more information on what this means. - -After the request has been rejected at the respective point it will continue to flow through the routing structure in -the search for a route that is able to complete it. - -The explicit ``reject`` directive is used mostly when building :ref:`Custom Directives`, e.g. inside of a ``flatMap`` -modifier for "filtering out" certain cases. - - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/RouteDirectivesExamplesSpec.scala - :snippet: reject-examples \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/scheme-directives/extractScheme.rst b/akka-docs/rst/scala/http/routing-dsl/directives/scheme-directives/extractScheme.rst deleted file mode 100644 index 398d474f3a..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/scheme-directives/extractScheme.rst +++ /dev/null @@ -1,22 +0,0 @@ -.. _-extractScheme-: - -extractScheme -============= - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/SchemeDirectives.scala - :snippet: extractScheme - -Description ------------ -Extracts the Uri scheme (i.e. "``http``", "``https``", etc.) for an incoming request. - -For rejecting a request if it doesn't match a specified scheme name, see the :ref:`-scheme-` directive. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/SchemeDirectivesExamplesSpec.scala - :snippet: example-1 diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/scheme-directives/index.rst b/akka-docs/rst/scala/http/routing-dsl/directives/scheme-directives/index.rst deleted file mode 100644 index b9328f3e4b..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/scheme-directives/index.rst +++ /dev/null @@ -1,13 +0,0 @@ -.. _SchemeDirectives: - -SchemeDirectives -================ - -Scheme directives can be used to extract the Uri scheme (i.e. "http", "https", etc.) -from requests or to reject any request that does not match a specified scheme name. - -.. toctree:: - :maxdepth: 1 - - extractScheme - scheme diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/scheme-directives/scheme.rst b/akka-docs/rst/scala/http/routing-dsl/directives/scheme-directives/scheme.rst deleted file mode 100644 index b41437a4d2..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/scheme-directives/scheme.rst +++ /dev/null @@ -1,30 +0,0 @@ -.. _-scheme-: - -scheme -====== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/SchemeDirectives.scala - :snippet: scheme - -Description ------------ -Rejects a request if its Uri scheme does not match a given one. - -The ``scheme`` directive can be used to match requests by their Uri scheme, only passing -through requests that match the specified scheme and rejecting all others. - -A typical use case for the ``scheme`` directive would be to reject requests coming in over -http instead of https, or to redirect such requests to the matching https URI with a -``MovedPermanently``. - -For simply extracting the scheme name, see the :ref:`-extractScheme-` directive. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/SchemeDirectivesExamplesSpec.scala - :snippet: example-2 - diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authenticateBasic.rst b/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authenticateBasic.rst deleted file mode 100644 index 52f97b4cdf..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authenticateBasic.rst +++ /dev/null @@ -1,41 +0,0 @@ -.. _-authenticateBasic-: - -authenticateBasic -================= - -Signature ---------- - -.. includecode:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/SecurityDirectives.scala#authenticator - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/SecurityDirectives.scala - :snippet: authenticateBasic - -Description ------------ -Wraps the inner route with Http Basic authentication support using a given ``Authenticator[T]``. - -Provides support for handling `HTTP Basic Authentication`_. - -Given a function returning ``Some[T]`` upon successful authentication and ``None`` otherwise, -respectively applies the inner route or rejects the request with a :class:`AuthenticationFailedRejection` rejection, -which by default is mapped to an ``401 Unauthorized`` response. - -Longer-running authentication tasks (like looking up credentials in a database) should use the :ref:`-authenticateBasicAsync-` -variant of this directive which allows it to run without blocking routing layer of Akka HTTP, freeing it for other requests. - -Standard HTTP-based authentication which uses the ``WWW-Authenticate`` header containing challenge data and -``Authorization`` header for receiving credentials is implemented in subclasses of ``HttpAuthenticator``. - -See :ref:`credentials-and-timing-attacks-scala` for details about verifying the secret. - -.. warning:: - Make sure to use basic authentication only over SSL/TLS because credentials are transferred in plaintext. - -.. _HTTP Basic Authentication: https://en.wikipedia.org/wiki/Basic_auth - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/SecurityDirectivesExamplesSpec.scala - :snippet: authenticateBasic-0 diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authenticateBasicAsync.rst b/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authenticateBasicAsync.rst deleted file mode 100644 index 1f7b42afef..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authenticateBasicAsync.rst +++ /dev/null @@ -1,39 +0,0 @@ -.. _-authenticateBasicAsync-: - -authenticateBasicAsync -====================== - -Signature ---------- - -.. includecode:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/SecurityDirectives.scala#async-authenticator - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/SecurityDirectives.scala - :snippet: authenticateBasicAsync - -Description ------------ -Wraps the inner route with Http Basic authentication support using a given ``AsyncAuthenticator[T]``. - -This variant of the :ref:`-authenticateBasic-` directive returns a ``Future[Option[T]]`` which allows freeing up the routing -layer of Akka HTTP, freeing it for other requests. It should be used whenever an authentication is expected to take -a longer amount of time (e.g. looking up the user in a database). - -In case the returned option is ``None`` the request is rejected with a :class:`AuthenticationFailedRejection`, -which by default is mapped to an ``401 Unauthorized`` response. - -Standard HTTP-based authentication which uses the ``WWW-Authenticate`` header containing challenge data and -``Authorization`` header for receiving credentials is implemented in subclasses of ``HttpAuthenticator``. - -See :ref:`credentials-and-timing-attacks-scala` for details about verifying the secret. - -.. warning:: - Make sure to use basic authentication only over SSL/TLS because credentials are transferred in plaintext. - -.. _HTTP Basic Authentication: https://en.wikipedia.org/wiki/Basic_auth - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/SecurityDirectivesExamplesSpec.scala - :snippet: authenticateBasicAsync-0 diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authenticateBasicPF.rst b/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authenticateBasicPF.rst deleted file mode 100644 index 0643ecbc5d..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authenticateBasicPF.rst +++ /dev/null @@ -1,39 +0,0 @@ -.. _-authenticateBasicPF-: - -authenticateBasicPF -=================== - -Signature ---------- - -.. includecode:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/SecurityDirectives.scala#authenticator-pf - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/SecurityDirectives.scala - :snippet: authenticateBasicPF - -Description ------------ -Wraps the inner route with Http Basic authentication support using a given ``AuthenticatorPF[T]``. - -Provides support for handling `HTTP Basic Authentication`_. - -Refer to :ref:`-authenticateBasic-` for a detailed description of this directive. - -Its semantics are equivalent to ``authenticateBasicPF`` 's, where not handling a case in the Partial Function (PF) -leaves the request to be rejected with a :class:`AuthenticationFailedRejection` rejection. - -Longer-running authentication tasks (like looking up credentials in a database) should use :ref:`-authenticateBasicAsync-` -or :ref:`-authenticateBasicPFAsync-` if you prefer to use the ``PartialFunction`` syntax. - -See :ref:`credentials-and-timing-attacks-scala` for details about verifying the secret. - -.. warning:: - Make sure to use basic authentication only over SSL/TLS because credentials are transferred in plaintext. - -.. _HTTP Basic Authentication: https://en.wikipedia.org/wiki/Basic_auth - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/SecurityDirectivesExamplesSpec.scala - :snippet: authenticateBasicPF-0 diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authenticateBasicPFAsync.rst b/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authenticateBasicPFAsync.rst deleted file mode 100644 index dce48aee22..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authenticateBasicPFAsync.rst +++ /dev/null @@ -1,36 +0,0 @@ -.. _-authenticateBasicPFAsync-: - -authenticateBasicPFAsync -======================== - -Signature ---------- - -.. includecode:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/SecurityDirectives.scala#async-authenticator-pf - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/SecurityDirectives.scala - :snippet: authenticateBasicPFAsync - -Description ------------ -Wraps the inner route with Http Basic authentication support using a given ``AsyncAuthenticatorPF[T]``. - -Provides support for handling `HTTP Basic Authentication`_. - -Refer to :ref:`-authenticateBasic-` for a detailed description of this directive. - -Its semantics are equivalent to ``authenticateBasicPF`` 's, where not handling a case in the Partial Function (PF) -leaves the request to be rejected with a :class:`AuthenticationFailedRejection` rejection. - -See :ref:`credentials-and-timing-attacks-scala` for details about verifying the secret. - -.. warning:: - Make sure to use basic authentication only over SSL/TLS because credentials are transferred in plaintext. - -.. _HTTP Basic Authentication: https://en.wikipedia.org/wiki/Basic_auth - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/SecurityDirectivesExamplesSpec.scala - :snippet: authenticateBasicPFAsync-0 diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authenticateOAuth2.rst b/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authenticateOAuth2.rst deleted file mode 100644 index f37fb275b3..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authenticateOAuth2.rst +++ /dev/null @@ -1,42 +0,0 @@ -.. _-authenticateOAuth2-: - -authenticateOAuth2 -================== - -Signature ---------- - -.. includecode:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/SecurityDirectives.scala#authenticator - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/SecurityDirectives.scala - :snippet: authenticateOAuth2 - -Description ------------ -Wraps the inner route with OAuth Bearer Token authentication support using a given ``AuthenticatorPF[T]`` - -Provides support for extracting the so-called "*Bearer Token*" from the :class:`Authorization` HTTP Header, -which is used to initiate an OAuth2 authorization. - -.. warning:: - This directive does not implement the complete OAuth2 protocol, but instead enables implementing it, - by extracting the needed token from the HTTP headers. - -Given a function returning ``Some[T]`` upon successful authentication and ``None`` otherwise, -respectively applies the inner route or rejects the request with a :class:`AuthenticationFailedRejection` rejection, -which by default is mapped to an ``401 Unauthorized`` response. - -Longer-running authentication tasks (like looking up credentials in a database) should use the :ref:`-authenticateOAuth2Async-` -variant of this directive which allows it to run without blocking routing layer of Akka HTTP, freeing it for other requests. - -See :ref:`credentials-and-timing-attacks-scala` for details about verifying the secret. - -For more information on how OAuth2 works see `RFC 6750`_. - -.. _RFC 6750: https://tools.ietf.org/html/rfc6750 - -Example -------- - -Usage in code is exactly the same as :ref:`-authenticateBasic-`, -with the difference that one must validate the token as OAuth2 dictates (which is currently not part of Akka HTTP itself). diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authenticateOAuth2Async.rst b/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authenticateOAuth2Async.rst deleted file mode 100644 index 7268269a2c..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authenticateOAuth2Async.rst +++ /dev/null @@ -1,42 +0,0 @@ -.. _-authenticateOAuth2Async-: - -authenticateOAuth2Async -======================= - -Signature ---------- - -.. includecode:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/SecurityDirectives.scala#async-authenticator - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/SecurityDirectives.scala - :snippet: authenticateOAuth2Async - -Description ------------ -Wraps the inner route with OAuth Bearer Token authentication support using a given ``AsyncAuthenticator[T]``. - -Provides support for extracting the so-called "*Bearer Token*" from the :class:`Authorization` HTTP Header, -which is used to initiate an OAuth2 authorization. - -.. warning:: - This directive does not implement the complete OAuth2 protocol, but instead enables implementing it, - by extracting the needed token from the HTTP headers. - -Given a function returning ``Some[T]`` upon successful authentication and ``None`` otherwise, -respectively applies the inner route or rejects the request with a :class:`AuthenticationFailedRejection` rejection, -which by default is mapped to an ``401 Unauthorized`` response. - -See also :ref:`-authenticateOAuth2-` if the authorization operation is rather quick, and does not have to execute asynchronously. - -See :ref:`credentials-and-timing-attacks-scala` for details about verifying the secret. - -For more information on how OAuth2 works see `RFC 6750`_. - -.. _RFC 6750: https://tools.ietf.org/html/rfc6750 - - -Example -------- - -Usage in code is exactly the same as :ref:`-authenticateBasicAsync-`, -with the difference that one must validate the token as OAuth2 dictates (which is currently not part of Akka HTTP itself). diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authenticateOAuth2PF.rst b/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authenticateOAuth2PF.rst deleted file mode 100644 index cf3e65f000..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authenticateOAuth2PF.rst +++ /dev/null @@ -1,43 +0,0 @@ -.. _-authenticateOAuth2PF-: - -authenticateOAuth2PF -==================== - -Signature ---------- - -.. includecode:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/SecurityDirectives.scala#authenticator - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/SecurityDirectives.scala - :snippet: authenticateOAuth2PF - -Description ------------ -Wraps the inner route with OAuth Bearer Token authentication support using a given ``AuthenticatorPF[T]``. - -Provides support for extracting the so-called "*Bearer Token*" from the :class:`Authorization` HTTP Header, -which is used to initiate an OAuth2 authorization. - -.. warning:: - This directive does not implement the complete OAuth2 protocol, but instead enables implementing it, - by extracting the needed token from the HTTP headers. - -Refer to :ref:`-authenticateOAuth2-` for a detailed description of this directive. - -Its semantics are equivalent to ``authenticateOAuth2PF`` 's, where not handling a case in the Partial Function (PF) -leaves the request to be rejected with a :class:`AuthenticationFailedRejection` rejection. - -Longer-running authentication tasks (like looking up credentials in a database) should use the :ref:`-authenticateOAuth2Async-` -variant of this directive which allows it to run without blocking routing layer of Akka HTTP, freeing it for other requests. - -See :ref:`credentials-and-timing-attacks-scala` for details about verifying the secret. - -For more information on how OAuth2 works see `RFC 6750`_. - -.. _RFC 6750: https://tools.ietf.org/html/rfc6750 - -Example -------- - -Usage in code is exactly the same as :ref:`-authenticateBasicPF-`, -with the difference that one must validate the token as OAuth2 dictates (which is currently not part of Akka HTTP itself). diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authenticateOAuth2PFAsync.rst b/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authenticateOAuth2PFAsync.rst deleted file mode 100644 index 44d0284225..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authenticateOAuth2PFAsync.rst +++ /dev/null @@ -1,44 +0,0 @@ -.. _-authenticateOAuth2PFAsync-: - -authenticateOAuth2PFAsync -========================= - -Wraps the inner route with OAuth Bearer Token authentication support using a given ``AsyncAuthenticatorPF[T]``. - -Signature ---------- - -.. includecode:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/SecurityDirectives.scala#authenticator - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/SecurityDirectives.scala - :snippet: authenticateOAuth2PFAsync - -Description ------------ - -Provides support for extracting the so-called "*Bearer Token*" from the :class:`Authorization` HTTP Header, -which is used to initiate an OAuth2 authorization. - -.. warning:: - This directive does not implement the complete OAuth2 protocol, but instead enables implementing it, - by extracting the needed token from the HTTP headers. - -Refer to :ref:`-authenticateOAuth2-` for a detailed description of this directive. - -Its semantics are equivalent to ``authenticateOAuth2PF`` 's, where not handling a case in the Partial Function (PF) -leaves the request to be rejected with a :class:`AuthenticationFailedRejection` rejection. - -See also :ref:`-authenticateOAuth2PF-` if the authorization operation is rather quick, and does not have to execute asynchronously. - -See :ref:`credentials-and-timing-attacks-scala` for details about verifying the secret. - -For more information on how OAuth2 works see `RFC 6750`_. - -.. _RFC 6750: https://tools.ietf.org/html/rfc6750 - - -Example -------- - -Usage in code is exactly the same as :ref:`-authenticateBasicPFAsync-`, -with the difference that one must validate the token as OAuth2 dictates (which is currently not part of Akka HTTP itself). diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authenticateOrRejectWithChallenge.rst b/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authenticateOrRejectWithChallenge.rst deleted file mode 100644 index 2f099b5572..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authenticateOrRejectWithChallenge.rst +++ /dev/null @@ -1,30 +0,0 @@ -.. _-authenticateOrRejectWithChallenge-: - -authenticateOrRejectWithChallenge -================================= - -Signature ---------- - -.. includecode:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/SecurityDirectives.scala#authentication-result - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/SecurityDirectives.scala - :snippet: authenticateOrRejectWithChallenge - -Description ------------ -Lifts an authenticator function into a directive. - -This directive allows implementing the low level challange-response type of authentication that some services may require. - -More details about challenge-response authentication are available in the `RFC 2617`_, `RFC 7616`_ and `RFC 7617`_. - -.. _RFC 2617: http://tools.ietf.org/html/rfc2617 -.. _RFC 7616: http://tools.ietf.org/html/rfc7616 -.. _RFC 7617: http://tools.ietf.org/html/rfc7617 - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/SecurityDirectivesExamplesSpec.scala - :snippet: authenticateOrRejectWithChallenge-0 diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authorize.rst b/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authorize.rst deleted file mode 100644 index e5717d4340..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authorize.rst +++ /dev/null @@ -1,36 +0,0 @@ -.. _-authorize-: - -authorize -========= - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/SecurityDirectives.scala - :snippet: authorize - -Description ------------ -Applies the given authorization check to the request. - -The user-defined authorization check can either be supplied as a ``=> Boolean`` value which is calculated -just from information out of the lexical scope, or as a function ``RequestContext => Boolean`` which can also -take information from the request itself into account. - -If the check returns ``true`` the request is passed on to the inner route unchanged, otherwise an -``AuthorizationFailedRejection`` is created, triggering a ``403 Forbidden`` response by default -(the same as in the case of an ``AuthenticationFailedRejection``). - -In a common use-case you would check if a user (e.g. supplied by any of the ``authenticate*`` family of directives, -e.g. :ref:`-authenticateBasic-`) is allowed to access the inner routes, e.g. by checking if the user has the needed permissions. - -See also :ref:`-authorizeAsync-` for the asynchronous version of this directive. - -.. note:: - See also :ref:`authentication-vs-authorization-scala` to understand the differences between those. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/SecurityDirectivesExamplesSpec.scala - :snippet: 0authorize-0 diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authorizeAsync.rst b/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authorizeAsync.rst deleted file mode 100644 index 57597b1e2d..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/authorizeAsync.rst +++ /dev/null @@ -1,36 +0,0 @@ -.. _-authorizeAsync-: - -authorizeAsync -============== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/SecurityDirectives.scala - :snippet: authorizeAsync - -Description ------------ -Applies the given authorization check to the request. - -The user-defined authorization check can either be supplied as a ``=> Future[Boolean]`` value which is calculated -just from information out of the lexical scope, or as a function ``RequestContext => Future[Boolean]`` which can also -take information from the request itself into account. - -If the check returns ``true`` or the ``Future`` is failed the request is passed on to the inner route unchanged, -otherwise an ``AuthorizationFailedRejection`` is created, triggering a ``403 Forbidden`` response by default -(the same as in the case of an ``AuthenticationFailedRejection``). - -In a common use-case you would check if a user (e.g. supplied by any of the ``authenticate*`` family of directives, -e.g. :ref:`-authenticateBasic-`) is allowed to access the inner routes, e.g. by checking if the user has the needed permissions. - -See also :ref:`-authorize-` for the synchronous version of this directive. - -.. note:: - See also :ref:`authentication-vs-authorization-scala` to understand the differences between those. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/SecurityDirectivesExamplesSpec.scala - :snippet: 0authorizeAsync diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/extractCredentials.rst b/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/extractCredentials.rst deleted file mode 100644 index 682d510a28..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/extractCredentials.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. _-extractCredentials-: - -extractCredentials -================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/SecurityDirectives.scala - :snippet: extractCredentials - -Description ------------ - -Extracts the potentially present ``HttpCredentials`` provided with the request's ``Authorization`` header, -which can be then used to implement some custom authentication or authorization logic. - -See :ref:`credentials-and-timing-attacks-scala` for details about verifying the secret. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/SecurityDirectivesExamplesSpec.scala - :snippet: 0extractCredentials diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/index.rst b/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/index.rst deleted file mode 100644 index 809d3d7e3c..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/security-directives/index.rst +++ /dev/null @@ -1,84 +0,0 @@ -.. _SecurityDirectives: - -SecurityDirectives -================== - -.. toctree:: - :maxdepth: 1 - - authenticateBasic - authenticateBasicAsync - authenticateBasicPF - authenticateBasicPFAsync - authenticateOrRejectWithChallenge - authenticateOAuth2 - authenticateOAuth2Async - authenticateOAuth2PF - authenticateOAuth2PFAsync - authenticateOrRejectWithChallenge - authorize - authorizeAsync - extractCredentials - - -.. _authentication-vs-authorization-scala: - -Authentication vs. Authorization --------------------------------- - -**Authentication** is the process of establishing a known identity for the user, whereby 'identity' is defined in the -context of the application. This may be done with a username/password combination, a cookie, a pre-defined IP or some -other mechanism. After authentication the system believes that it knows who the user is. - -**Authorization** is the process of determining, whether a given user is allowed access to a given resource or not. In -most cases, in order to be able to authorize a user (i.e. allow access to some part of the system) the users identity -must already have been established, i.e. he/she must have been authenticated. Without prior authentication the -authorization would have to be very crude, e.g. "allow access for *all* users" or "allow access for *noone*". Only after -authentication will it be possible to, e.g., "allow access to the statistics resource for *admins*, but not for regular -*members*". - -Authentication and authorization may happen at the same time, e.g. when everyone who can properly be authenticated is -also allowed access (which is often a very simple and somewhat implicit authorization logic). In other cases the -system might have one mechanism for authentication (e.g. establishing user identity via an LDAP lookup) and another one -for authorization (e.g. a database lookup for retrieving user access rights). - - -Authentication and Authorization in HTTP ----------------------------------------- - -HTTP provides a general framework for access control and authentication, via an extensible set of challenge-response -authentication schemes, which can be used by a server to challenge a client request and by a client to provide -authentication information. The general mechanism is defined in `RFC 7235`_. - -The "HTTP Authentication Scheme Registry" defines the namespace for the authentication schemes in challenges and -credentials. You can see the currently registered schemes at http://www.iana.org/assignments/http-authschemes. - -At this point Akka HTTP only implements the "'Basic' HTTP Authentication Scheme" whose most current specification can be -found here: https://datatracker.ietf.org/doc/draft-ietf-httpauth-basicauth-update/. - -.. _RFC 7235: http://tools.ietf.org/html/rfc7235 - -Low-level OAuth2 "Bearer Token" directives ------------------------------------------- -The OAuth2 directives currently provided in Akka HTTP are not a full OAuth2 protocol implementation, -they are only a means of extracting the so called ``Bearer Token`` from the ``Authorization`` HTTP Header, -as defined in `RFC 6750`_, and allow users to validate and complete the protocol. - -.. _RFC 6750: https://tools.ietf.org/html/rfc6750 - - -.. _credentials-and-timing-attacks-scala: - -Credentials and password timing attacks ---------------------------------------- - -When transforming request ``Credentials`` into an application specific user identifier the naive solution for -checking the secret (password) would be a regular string comparison, but doing this would open up the application to -timing attacks. See for example `Timing Attacks Explained`_ for an explanation of the problem. - -.. _Timing Attacks Explained: http://emerose.com/timing-attacks-explained - -To protect users of the library from that mistake the secret is not available through the API, instead the method -``Credentials.Provided.verify(String)`` should be used. It does a constant time comparison rather than returning early -upon finding the first non-equal character. - diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/timeout-directives/index.rst b/akka-docs/rst/scala/http/routing-dsl/directives/timeout-directives/index.rst deleted file mode 100644 index e19f58c365..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/timeout-directives/index.rst +++ /dev/null @@ -1,11 +0,0 @@ -.. _TimeoutDirectives: - -TimeoutDirectives -================= - -.. toctree:: - :maxdepth: 1 - - withRequestTimeout - withoutRequestTimeout - withRequestTimeoutResponse \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/timeout-directives/withRequestTimeout.rst b/akka-docs/rst/scala/http/routing-dsl/directives/timeout-directives/withRequestTimeout.rst deleted file mode 100644 index 337cacf5ca..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/timeout-directives/withRequestTimeout.rst +++ /dev/null @@ -1,49 +0,0 @@ -.. _-withRequestTimeout-: - -withRequestTimeout -================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/TimeoutDirectives.scala - :snippet: withRequestTimeout - -Description ------------ - -This directive enables "late" (during request processing) control over the :ref:`request-timeout-scala` feature in Akka HTTP. - -The timeout can be either loosened or made more tight using this directive, however one should be aware that it is -inherently racy (which may especially show with very tight timeouts) since a timeout may already have been triggered -when this directive executes. - -In case of pipelined HTTP requests (multiple requests being accepted on the same connection before sending the first response) -a the request timeout failure of the ``n-th`` request *will shut down the connection* causing the already enqueued requests -to be dropped. This is by-design, as the request timeout feature serves as a "safety net" in case of programming errors -(e.g. a Future that never completes thus potentially blocking the entire connection forever) or malicious attacks on the server. - -Optionally, a timeout handler may be provided in which is called when a time-out is triggered and must produce an -``HttpResponse`` that will be sent back to the client instead of the "too late" response (in case it'd ever arrive). -See also :ref:`-withRequestTimeoutResponse-` if only looking to customise the timeout response without changing the timeout itself. - -.. warning:: - Please note that setting the timeout from within a directive is inherently racy (as the "point in time from which - we're measuring the timeout" is already in the past (the moment we started handling the request), so if the existing - timeout already was triggered before your directive had the chance to change it, an timeout may still be logged. - - It is recommended to use a larger statically configured timeout (think of it as a "safety net" against programming errors - or malicious attackers) and if needed tighten it using the directives – not the other way around. - -For more information about various timeouts in Akka HTTP see :ref:`http-timeouts-scala`. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/TimeoutDirectivesExamplesSpec.scala - :snippet: withRequestTimeout-plain - -With setting the handler at the same time: - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/TimeoutDirectivesExamplesSpec.scala - :snippet: withRequestTimeout-with-handler diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/timeout-directives/withRequestTimeoutResponse.rst b/akka-docs/rst/scala/http/routing-dsl/directives/timeout-directives/withRequestTimeoutResponse.rst deleted file mode 100644 index 028d75e55a..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/timeout-directives/withRequestTimeoutResponse.rst +++ /dev/null @@ -1,34 +0,0 @@ -.. _-withRequestTimeoutResponse-: - -withRequestTimeoutResponse -========================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/TimeoutDirectives.scala - :snippet: withRequestTimeoutResponse - -Description ------------ - -Allows customising the ``HttpResponse`` that will be sent to clients in case of a :ref:`request-timeout-scala`. - -See also :ref:`-withRequestTimeout-` or :ref:`-withoutRequestTimeout-` if interested in dynamically changing the timeout -for a given route instead. - -.. warning:: - Please note that setting handler is inherently racy as the timeout is measured from starting to handle the request - to its deadline, thus if the timeout triggers before the ``withRequestTimeoutResponse`` executed it would have emitted - the default timeout HttpResponse. - - In practice this can only be a problem with very tight timeouts, so with default settings - of request timeouts being measured in seconds it shouldn't be a problem in reality (though certainly a possibility still). - -To learn more about various timeouts in Akka HTTP and how to configure them see :ref:`http-timeouts-scala`. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/TimeoutDirectivesExamplesSpec.scala - :snippet: withRequestTimeoutResponse diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/timeout-directives/withoutRequestTimeout.rst b/akka-docs/rst/scala/http/routing-dsl/directives/timeout-directives/withoutRequestTimeout.rst deleted file mode 100644 index 48d936f995..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/timeout-directives/withoutRequestTimeout.rst +++ /dev/null @@ -1,31 +0,0 @@ -.. _-withoutRequestTimeout-: - -withoutRequestTimeout -===================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/TimeoutDirectives.scala - :snippet: withoutRequestTimeout - -Description ------------ - -This directive enables "late" (during request processing) control over the :ref:`request-timeout-scala` feature in Akka HTTP. - -It is not recommended to turn off request timeouts using this method as it is inherently racy and disabling request timeouts -basically turns off the safety net against programming mistakes that it provides. - -.. warning:: - Please note that setting the timeout from within a directive is inherently racy (as the "point in time from which - we're measuring the timeout" is already in the past (the moment we started handling the request), so if the existing - timeout already was triggered before your directive had the chance to change it, an timeout may still be logged. - -For more information about various timeouts in Akka HTTP see :ref:`http-timeouts-scala`. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/TimeoutDirectivesExamplesSpec.scala - :snippet: withoutRequestTimeout diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/websocket-directives/handleWebSocketMessages.rst b/akka-docs/rst/scala/http/routing-dsl/directives/websocket-directives/handleWebSocketMessages.rst deleted file mode 100644 index 517a21cff3..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/websocket-directives/handleWebSocketMessages.rst +++ /dev/null @@ -1,27 +0,0 @@ -.. _-handleWebSocketMessages-: - -handleWebSocketMessages -======================= - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/WebSocketDirectives.scala - :snippet: handleWebSocketMessages - -Description ------------ - -The directive first checks if the request was a valid WebSocket handshake request and if yes, it completes the request -with the passed handler. Otherwise, the request is rejected with an ``ExpectedWebSocketRequestRejection``. - -WebSocket subprotocols offered in the ``Sec-WebSocket-Protocol`` header of the request are ignored. If you want to -support several protocols use the :ref:`-handleWebSocketMessagesForProtocol-` directive, instead. - -For more information about the WebSocket support, see :ref:`server-side-websocket-support-scala`. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/WebSocketDirectivesExamplesSpec.scala - :snippet: greeter-service diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/websocket-directives/handleWebSocketMessagesForProtocol.rst b/akka-docs/rst/scala/http/routing-dsl/directives/websocket-directives/handleWebSocketMessagesForProtocol.rst deleted file mode 100644 index 4fd55bd88e..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/websocket-directives/handleWebSocketMessagesForProtocol.rst +++ /dev/null @@ -1,31 +0,0 @@ -.. _-handleWebSocketMessagesForProtocol-: - -handleWebSocketMessagesForProtocol -================================== - -Signature ---------- - -.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/WebSocketDirectives.scala - :snippet: handleWebSocketMessagesForProtocol - -Description ------------ -Handles WebSocket requests with the given handler if the given subprotocol is offered in the ``Sec-WebSocket-Protocol`` -header of the request and rejects other requests with an ``ExpectedWebSocketRequestRejection`` or an -``UnsupportedWebSocketSubprotocolRejection``. - -The directive first checks if the request was a valid WebSocket handshake request and if the request offers the passed -subprotocol name. If yes, the directive completes the request with the passed handler. Otherwise, the request is -either rejected with an ``ExpectedWebSocketRequestRejection`` or an ``UnsupportedWebSocketSubprotocolRejection``. - -To support several subprotocols, for example at the same path, several instances of ``handleWebSocketMessagesForProtocol`` can -be chained using ``~`` as you can see in the below example. - -For more information about the WebSocket support, see :ref:`server-side-websocket-support-scala`. - -Example -------- - -.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/WebSocketDirectivesExamplesSpec.scala - :snippet: handle-multiple-protocols diff --git a/akka-docs/rst/scala/http/routing-dsl/directives/websocket-directives/index.rst b/akka-docs/rst/scala/http/routing-dsl/directives/websocket-directives/index.rst deleted file mode 100644 index 06113c1bfd..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/directives/websocket-directives/index.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. _WebSocketDirectives: - -WebSocketDirectives -=================== - -.. toctree:: - :maxdepth: 1 - - handleWebSocketMessages - handleWebSocketMessagesForProtocol \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/exception-handling.rst b/akka-docs/rst/scala/http/routing-dsl/exception-handling.rst deleted file mode 100644 index c47257ebc4..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/exception-handling.rst +++ /dev/null @@ -1,45 +0,0 @@ -.. _exception-handling-scala: - -Exception Handling -================== - -Exceptions thrown during route execution bubble up through the route structure to the next enclosing -:ref:`-handleExceptions-` directive or the top of your route structure. - -Similarly to the way that :ref:`rejections-scala` are handled the :ref:`-handleExceptions-` directive delegates the actual job -of converting an exception to its argument, an ExceptionHandler__, which is defined like this:: - - trait ExceptionHandler extends PartialFunction[Throwable, Route] - -__ @github@/akka-http/src/main/scala/akka/http/scaladsl/server/ExceptionHandler.scala - -Since an ``ExceptionHandler`` is a partial function it can choose, which exceptions it would like to handle and -which not. Unhandled exceptions will simply continue to bubble up in the route structure. -At the root of the route tree any still unhandled exception will be dealt with by the top-level handler which always -handles *all* exceptions. - -``Route.seal`` internally wraps its argument route with the :ref:`-handleExceptions-` directive in order to "catch" and -handle any exception. - -So, if you'd like to customize the way certain exceptions are handled you need to write a custom ``ExceptionHandler``. -Once you have defined your custom ``ExceptionHandler`` you have two options for "activating" it: - -1. Bring it into implicit scope at the top-level. -2. Supply it as argument to the :ref:`-handleExceptions-` directive. - -In the first case your handler will be "sealed" (which means that it will receive the default handler as a fallback for -all cases your handler doesn't handle itself) and used for all exceptions that are not handled within the route -structure itself. - -The second case allows you to restrict the applicability of your handler to certain branches of your route structure. - -Here is an example for wiring up a custom handler via :ref:`-handleExceptions-`: - -.. includecode2:: ../../code/docs/http/scaladsl/server/ExceptionHandlerExamplesSpec.scala - :snippet: explicit-handler-example - - -And this is how to do it implicitly: - -.. includecode2:: ../../code/docs/http/scaladsl/server/ExceptionHandlerExamplesSpec.scala - :snippet: implicit-handler-example \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/index.rst b/akka-docs/rst/scala/http/routing-dsl/index.rst deleted file mode 100644 index 10942ef517..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/index.rst +++ /dev/null @@ -1,109 +0,0 @@ -.. _http-high-level-server-side-api: - -High-level Server-Side API -========================== - -In addition to the :ref:`http-low-level-server-side-api` Akka HTTP provides a very flexible "Routing DSL" for elegantly -defining RESTful web services. It picks up where the low-level API leaves off and offers much of the higher-level -functionality of typical web servers or frameworks, like deconstruction of URIs, content negotiation or -static content serving. - -.. note:: - It is recommended to read the :ref:`implications-of-streaming-http-entities` section, - as it explains the underlying full-stack streaming concepts, which may be unexpected when coming - from a background with non-"streaming first" HTTP Servers. - -.. toctree:: - :maxdepth: 1 - - overview - routes - directives/index - rejections - exception-handling - path-matchers - case-class-extraction - source-streaming-support - testkit - websocket-support - - -Minimal Example ---------------- - -This is a complete, very basic Akka HTTP application relying on the Routing DSL: - -.. includecode2:: ../../code/docs/http/scaladsl/HttpServerExampleSpec.scala - :snippet: minimal-routing-example - -It starts an HTTP Server on localhost and replies to GET requests to ``/hello`` with a simple response. - - -.. _Long Example: - -Longer Example --------------- - -The following is an Akka HTTP route definition that tries to show off a few features. The resulting service does -not really do anything useful but its definition should give you a feel for what an actual API definition with -the Routing DSL will look like: - -.. includecode2:: ../../code/docs/http/scaladsl/HttpServerExampleSpec.scala - :snippet: long-routing-example - -.. _handling-http-server-failures-high-level-scala: - -Handling HTTP Server failures in the High-Level API ---------------------------------------------------- -There are various situations when failure may occur while initialising or running an Akka HTTP server. -Akka by default will log all these failures, however sometimes one may want to react to failures in addition -to them just being logged, for example by shutting down the actor system, or notifying some external monitoring -end-point explicitly. - -Bind failures -^^^^^^^^^^^^^ -For example the server might be unable to bind to the given port. For example when the port -is already taken by another application, or if the port is privileged (i.e. only usable by ``root``). -In this case the "binding future" will fail immediately, and we can react to if by listening on the Future's completion: - -.. includecode2:: ../../code/docs/http/scaladsl/HttpServerExampleSpec.scala - :snippet: binding-failure-high-level-example - - -.. note:: - For a more low-level overview of the kinds of failures that can happen and also more fine-grained control over them - refer to the :ref:`handling-http-server-failures-low-level-scala` documentation. - -Failures and exceptions inside the Routing DSL -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Exception handling within the Routing DSL is done by providing :class:`ExceptionHandler` s which are documented in-depth -in the :ref:`exception-handling-scala` section of the documtnation. You can use them to transform exceptions into -:class:`HttpResponse` s with apropriate error codes and human-readable failure descriptions. - -File uploads -^^^^^^^^^^^^ -For high level directives to handle uploads see the :ref:`FileUploadDirectives`. - -Handling a simple file upload from for example a browser form with a `file` input can be done -by accepting a `Multipart.FormData` entity, note that the body parts are `Source` rather than -all available right away, and so is the individual body part payload so you will need to consume -those streams both for the file and for the form fields. - -Here is a simple example which just dumps the uploaded file into a temporary file on disk, collects -some form fields and saves an entry to a fictive database: - -.. includecode2:: ../../code/docs/http/scaladsl/server/FileUploadExamplesSpec.scala - :snippet: simple-upload - -You can transform the uploaded files as they arrive rather than storing then in a temporary file as -in the previous example. In this example we accept any number of ``.csv`` files, parse those into lines -and split each line before we send it to an actor for further processing: - -.. includecode2:: ../../code/docs/http/scaladsl/server/FileUploadExamplesSpec.scala - :snippet: stream-csv-upload - -Configuring Server-side HTTPS ------------------------------ - -For detailed documentation about configuring and using HTTPS on the server-side refer to :ref:`serverSideHTTPS-scala`. diff --git a/akka-docs/rst/scala/http/routing-dsl/overview.rst b/akka-docs/rst/scala/http/routing-dsl/overview.rst deleted file mode 100644 index 178f744061..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/overview.rst +++ /dev/null @@ -1,50 +0,0 @@ -Routing DSL Overview -==================== - -The Akka HTTP :ref:`http-low-level-server-side-api` provides a ``Flow``- or ``Function``-level interface that allows -an application to respond to incoming HTTP requests by simply mapping requests to responses: - -.. includecode2:: ../../code/docs/http/scaladsl/HttpServerExampleSpec.scala - :snippet: low-level-server-example - -While it'd be perfectly possible to define a complete REST API service purely by pattern-matching against the incoming -``HttpRequest`` (maybe with the help of a few extractors in the way of `Unfiltered`_) this approach becomes somewhat -unwieldy for larger services due to the amount of syntax "ceremony" required. Also, it doesn't help in keeping your -service definition as DRY_ as you might like. - -As an alternative Akka HTTP provides a flexible DSL for expressing your service behavior as a structure of -composable elements (called :ref:`Directives`) in a concise and readable way. Directives are assembled into a so called -*route structure* which, at its top-level, forms a handler ``Flow`` (or, alternatively, an async handler function) that -can be directly supplied to a ``bind`` call. The conversion from ``Route`` to flow can either be invoked explicitly -using ``Route.handlerFlow`` or, otherwise, the conversion is also provided implicitly by -``RouteResult.route2HandlerFlow`` [1]_. - -For example, the service definition from above, written using the routing DSL, would look like this: - -.. includecode2:: ../../code/docs/http/scaladsl/HttpServerExampleSpec.scala - :snippet: high-level-server-example - -The core of the Routing DSL becomes available with a single import:: - - import akka.http.scaladsl.server.Directives._ - -This example also relies on the pre-defined support for Scala XML with:: - - import akka.http.scaladsl.marshallers.xml.ScalaXmlSupport._ - -The very short example shown here is certainly not the best for illustrating the savings in "ceremony" and improvements -in conciseness and readability that the Routing DSL promises. The :ref:`Long Example` might do a better job in this -regard. - -For learning how to work with the Routing DSL you should first understand the concept of :ref:`Routes`. - - -.. [1] To be picked up automatically, the implicit conversion needs to be provided in the companion object of the source - type. However, as ``Route`` is just a type alias for ``RequestContext => Future[RouteResult]``, there's no - companion object for ``Route``. Fortunately, the `implicit scope`_ for finding an implicit conversion also - includes all types that are "associated with any part" of the source type which in this case means that the - implicit conversion will also be picked up from ``RouteResult.route2HandlerFlow`` automatically. - -.. _Unfiltered: http://unfiltered.databinder.net/ -.. _DRY: http://en.wikipedia.org/wiki/Don%27t_repeat_yourself -.. _implicit scope: http://www.scala-lang.org/files/archive/spec/2.11/07-implicits.html#implicit-parameters \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/path-matchers.rst b/akka-docs/rst/scala/http/routing-dsl/path-matchers.rst deleted file mode 100644 index 758330d075..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/path-matchers.rst +++ /dev/null @@ -1,196 +0,0 @@ -.. _pathmatcher-dsl: - -The PathMatcher DSL -=================== - -For being able to work with the :ref:`PathDirectives` effectively you should have some understanding of the -``PathMatcher`` mini-DSL that Akka HTTP provides for elegantly defining URI matching behavior. - -Overview --------- - -When a request (or rather the respective ``RequestContext`` instance) enters the route structure it has an -"unmatched path" that is identical to the ``request.uri.path``. As it descends the routing tree and passes through one -or more :ref:`-pathPrefix-` or :ref:`-path-` directives the "unmatched path" progressively gets "eaten into" from the -left until, in most cases, it eventually has been consumed completely. - -What exactly gets matched and consumed as well as extracted from the unmatched path in each directive is defined with -the patch matching DSL, which is built around these types:: - - trait PathMatcher[L: Tuple] - type PathMatcher0 = PathMatcher[Unit] - type PathMatcher1[T] = PathMatcher[Tuple1[T]] - -The number and types of the values extracted by a ``PathMatcher`` instance is represented by the ``L`` type -parameter which needs to be one of Scala's TupleN types or ``Unit`` (which is designated by the ``Tuple`` context bound). -The convenience alias ``PathMatcher0`` can be used for all matchers which don't extract anything while ``PathMatcher1[T]`` -defines a matcher which only extracts a single value of type ``T``. - -Here is an example of a more complex ``PathMatcher`` expression: - -.. includecode2:: ../../code/docs/http/scaladsl/server/directives/PathDirectivesExamplesSpec.scala - :snippet: path-matcher - -This will match paths like ``foo/bar/X42/edit`` or ``foo/bar/X/create``. - -.. note:: The path matching DSL describes what paths to accept **after** URL decoding. This is why the path-separating - slashes have special status and cannot simply be specified as part of a string! The string "foo/bar" would match - the raw URI path "foo%2Fbar", which is most likely not what you want! - - -Basic PathMatchers ------------------- - -A complex ``PathMatcher`` can be constructed by combining or modifying more basic ones. Here are the basic matchers -that Akka HTTP already provides for you: - -String - You can use a ``String`` instance as a ``PathMatcher0``. Strings simply match themselves and extract no value. - Note that strings are interpreted as the decoded representation of the path, so if they include a '/' character - this character will match "%2F" in the encoded raw URI! - -Regex - You can use a ``Regex`` instance as a ``PathMatcher1[String]``, which matches whatever the regex matches and extracts - one ``String`` value. A ``PathMatcher`` created from a regular expression extracts either the complete match (if the - regex doesn't contain a capture group) or the capture group (if the regex contains exactly one capture group). - If the regex contains more than one capture group an ``IllegalArgumentException`` will be thrown. - -Map[String, T] - You can use a ``Map[String, T]`` instance as a ``PathMatcher1[T]``, which matches any of the keys and extracts the - respective map value for it. - -Slash: PathMatcher0 - Matches exactly one path-separating slash (``/``) character and extracts nothing. - -Segment: PathMatcher1[String] - Matches if the unmatched path starts with a path segment (i.e. not a slash). - If so the path segment is extracted as a ``String`` instance. - -PathEnd: PathMatcher0 - Matches the very end of the path, similar to ``$`` in regular expressions and extracts nothing. - -Remaining: PathMatcher1[String] - Matches and extracts the complete remaining unmatched part of the request's URI path as an (encoded!) String. - If you need access to the remaining *decoded* elements of the path use ``RemainingPath`` instead. - -RemainingPath: PathMatcher1[Path] - Matches and extracts the complete remaining, unmatched part of the request's URI path. - -IntNumber: PathMatcher1[Int] - Efficiently matches a number of decimal digits (unsigned) and extracts their (non-negative) ``Int`` value. The matcher - will not match zero digits or a sequence of digits that would represent an ``Int`` value larger than ``Int.MaxValue``. - -LongNumber: PathMatcher1[Long] - Efficiently matches a number of decimal digits (unsigned) and extracts their (non-negative) ``Long`` value. The matcher - will not match zero digits or a sequence of digits that would represent an ``Long`` value larger than ``Long.MaxValue``. - -HexIntNumber: PathMatcher1[Int] - Efficiently matches a number of hex digits and extracts their (non-negative) ``Int`` value. The matcher will not match - zero digits or a sequence of digits that would represent an ``Int`` value larger than ``Int.MaxValue``. - -HexLongNumber: PathMatcher1[Long] - Efficiently matches a number of hex digits and extracts their (non-negative) ``Long`` value. The matcher will not - match zero digits or a sequence of digits that would represent an ``Long`` value larger than ``Long.MaxValue``. - -DoubleNumber: PathMatcher1[Double] - Matches and extracts a ``Double`` value. The matched string representation is the pure decimal, - optionally signed form of a double value, i.e. without exponent. - -JavaUUID: PathMatcher1[UUID] - Matches and extracts a ``java.util.UUID`` instance. - -Neutral: PathMatcher0 - A matcher that always matches, doesn't consume anything and extracts nothing. - Serves mainly as a neutral element in ``PathMatcher`` composition. - -Segments: PathMatcher1[List[String]] - Matches all remaining segments as a list of strings. Note that this can also be "no segments" resulting in the empty - list. If the path has a trailing slash this slash will *not* be matched, i.e. remain unmatched and to be consumed by - potentially nested directives. - -separateOnSlashes(string: String): PathMatcher0 - Converts a path string containing slashes into a ``PathMatcher0`` that interprets slashes as - path segment separators. This means that a matcher matching "%2F" cannot be constructed with this helper. - -provide[L: Tuple](extractions: L): PathMatcher[L] - Always matches, consumes nothing and extracts the given ``TupleX`` of values. - -PathMatcher[L: Tuple](prefix: Path, extractions: L): PathMatcher[L] - Matches and consumes the given path prefix and extracts the given list of extractions. - If the given prefix is empty the returned matcher matches always and consumes nothing. - - -Combinators ------------ - -Path matchers can be combined with these combinators to form higher-level constructs: - -Tilde Operator (``~``) - The tilde is the most basic combinator. It simply concatenates two matchers into one, i.e if the first one matched - (and consumed) the second one is tried. The extractions of both matchers are combined type-safely. - For example: ``"foo" ~ "bar"`` yields a matcher that is identical to ``"foobar"``. - -Slash Operator (``/``) - This operator concatenates two matchers and inserts a ``Slash`` matcher in between them. - For example: ``"foo" / "bar"`` is identical to ``"foo" ~ Slash ~ "bar"``. - -Pipe Operator (``|``) - This operator combines two matcher alternatives in that the second one is only tried if the first one did *not* match. - The two sub-matchers must have compatible types. - For example: ``"foo" | "bar"`` will match either "foo" *or* "bar". - - -Modifiers ---------- - -Path matcher instances can be transformed with these modifier methods: - -/ - The slash operator cannot only be used as combinator for combining two matcher instances, it can also be used as - a postfix call. ``matcher /`` is identical to ``matcher ~ Slash`` but shorter and easier to read. - -? - By postfixing a matcher with ``?`` you can turn any ``PathMatcher`` into one that always matches, optionally consumes - and potentially extracts an ``Option`` of the underlying matchers extraction. The result type depends on the type - of the underlying matcher: - - =========================== ============================= - If a ``matcher`` is of type then ``matcher.?`` is of type - =========================== ============================= - ``PathMatcher0`` ``PathMatcher0`` - ``PathMatcher1[T]`` ``PathMatcher1[Option[T]`` - ``PathMatcher[L: Tuple]`` ``PathMatcher[Option[L]]`` - =========================== ============================= - - -repeat(separator: PathMatcher0 = PathMatchers.Neutral) - By postfixing a matcher with ``repeat(separator)`` you can turn any ``PathMatcher`` into one that always matches, - consumes zero or more times (with the given separator) and potentially extracts a ``List`` of the underlying matcher's - extractions. The result type depends on the type of the underlying matcher: - - =========================== ======================================= - If a ``matcher`` is of type then ``matcher.repeat(...)`` is of type - =========================== ======================================= - ``PathMatcher0`` ``PathMatcher0`` - ``PathMatcher1[T]`` ``PathMatcher1[List[T]`` - ``PathMatcher[L: Tuple]`` ``PathMatcher[List[L]]`` - =========================== ======================================= - - -``unary_!`` - By prefixing a matcher with ``!`` it can be turned into a ``PathMatcher0`` that only matches if the underlying matcher - does *not* match and vice versa. - - -transform / (h)flatMap / (h)map - These modifiers allow you to append your own "post-application" logic to another matcher in order to form a custom - one. You can map over the extraction(s), turn mismatches into matches or vice-versa or do anything else with the - results of the underlying matcher. Take a look at the method signatures and implementations for more guidance as to - how to use them. - - -Examples --------- - -.. includecode2:: ../../code/docs/http/scaladsl/server/directives/PathDirectivesExamplesSpec.scala - :snippet: path-dsl \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/rejections.rst b/akka-docs/rst/scala/http/routing-dsl/rejections.rst deleted file mode 100644 index 6a88da53cd..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/rejections.rst +++ /dev/null @@ -1,138 +0,0 @@ -.. _rejections-scala: - -Rejections -========== - -In the chapter about constructing :ref:`Routes` the ``~`` operator was introduced, which connects two routes in a way -that allows a second route to get a go at a request if the first route "rejected" it. The concept of "rejections" is -used by Akka HTTP for maintaining a more functional overall architecture and in order to be able to properly -handle all kinds of error scenarios. - -When a filtering directive, like the :ref:`-get-` directive, cannot let the request pass through to its inner route because -the filter condition is not satisfied (e.g. because the incoming request is not a GET request) the directive doesn't -immediately complete the request with an error response. Doing so would make it impossible for other routes chained in -after the failing filter to get a chance to handle the request. -Rather, failing filters "reject" the request in the same way as by explicitly calling ``requestContext.reject(...)``. - -After having been rejected by a route the request will continue to flow through the routing structure and possibly find -another route that can complete it. If there are more rejections all of them will be picked up and collected. - -If the request cannot be completed by (a branch of) the route structure an enclosing :ref:`-handleRejections-` directive -can be used to convert a set of rejections into an ``HttpResponse`` (which, in most cases, will be an error response). -``Route.seal`` internally wraps its argument route with the :ref:`-handleRejections-` directive in order to "catch" -and handle any rejection. - - -Predefined Rejections ---------------------- - -A rejection encapsulates a specific reason why a route was not able to handle a request. It is modeled as an object of -type ``Rejection``. Akka HTTP comes with a set of `predefined rejections`__, which are used by the many -:ref:`predefined directives `. - -Rejections are gathered up over the course of a Route evaluation and finally converted to ``HttpResponse`` replies by -the :ref:`-handleRejections-` directive if there was no way for the request to be completed. - -__ @github@/akka-http/src/main/scala/akka/http/scaladsl/server/Rejection.scala - - -.. _The RejectionHandler: - -The RejectionHandler --------------------- - -The :ref:`-handleRejections-` directive delegates the actual job of converting a list of rejections to its argument, a -RejectionHandler__, which is defined like this:: - - trait RejectionHandler extends (immutable.Seq[Rejection] ⇒ Option[Route]) - -__ @github@/akka-http/src/main/scala/akka/http/scaladsl/server/RejectionHandler.scala - -Since a ``RejectionHandler`` returns an ``Option[Route]`` it can choose whether it would like to handle the current set -of rejections or not. If it returns ``None`` the rejections will simply continue to flow through the route structure. - -The default ``RejectionHandler`` applied by the top-level glue code that turns a ``Route`` into a -``Flow`` or async handler function for the :ref:`low-level API ` (via -``Route.handlerFlow`` or ``Route.asyncHandler``) will handle *all* rejections that reach it. - - -Rejection Cancellation ----------------------- - -As you can see from its definition above the ``RejectionHandler`` doesn't handle single rejections but a whole list of -them. This is because some route structure produce several "reasons" why a request could not be handled. - -Take this route structure for example: - -.. includecode2:: ../../code/docs/http/scaladsl/server/RejectionHandlerExamplesSpec.scala - :snippet: example-1 - -For uncompressed POST requests this route structure would initially yield two rejections: - -- a ``MethodRejection`` produced by the :ref:`-get-` directive (which rejected because the request is not a GET request) -- an ``UnsupportedRequestEncodingRejection`` produced by the :ref:`-decodeRequestWith-` directive (which only accepts - gzip-compressed requests here) - -In reality the route even generates one more rejection, a ``TransformationRejection`` produced by the :ref:`-post-` -directive. It "cancels" all other potentially existing *MethodRejections*, since they are invalid after the -:ref:`-post-` directive allowed the request to pass (after all, the route structure *can* deal with POST requests). -These types of rejection cancellations are resolved *before* a ``RejectionHandler`` sees the rejection list. -So, for the example above the ``RejectionHandler`` will be presented with only a single-element rejection list, -containing nothing but the ``UnsupportedRequestEncodingRejection``. - - -.. _Empty Rejections: - -Empty Rejections ----------------- - -Since rejections are passed around in a list (or rather immutable ``Seq``) you might ask yourself what the semantics of -an empty rejection list are. In fact, empty rejection lists have well defined semantics. They signal that a request was -not handled because the respective resource could not be found. Akka HTTP reserves the special status of "empty -rejection" to this most common failure a service is likely to produce. - -So, for example, if the :ref:`-path-` directive rejects a request it does so with an empty rejection list. The -:ref:`-host-` directive behaves in the same way. - - -Customizing Rejection Handling ------------------------------- - -If you'd like to customize the way certain rejections are handled you'll have to write a custom -:ref:`RejectionHandler `. Here is an example: - -.. includecode2:: ../../code/docs/http/scaladsl/server/RejectionHandlerExamplesSpec.scala - :snippet: custom-handler-example - -The easiest way to construct a ``RejectionHandler`` is via the ``RejectionHandler.Builder`` that Akka HTTP provides. -After having created a new ``Builder`` instance with ``RejectionHandler.newBuilder()`` -you can attach handling logic for certain types of rejections through three helper methods: - -handle - Handles certain rejections with the given partial function. The partial function simply produces a ``Route`` which is - run when the rejection is "caught". This makes the full power of the Routing DSL available for defining rejection - handlers and even allows for recursing back into the main route structure if required. - -handleAll[T <: Rejection] - Handles all rejections of a certain type at the same time. This is useful for cases where your need access to more - than the first rejection of a certain type, e.g. for producing the error message to an unsupported request method. - -handleNotFound - As described :ref:`above ` "Resource Not Found" is special as it is represented with an empty - rejection set. The ``handleNotFound`` helper let's you specify the "recovery route" for this case. - -Even though you could handle several different rejection types in a single partial function supplied to ``handle`` -it is recommended to split these up into distinct ``handle`` attachments instead. -This way the priority between rejections is properly defined via the order of your ``handle`` clauses rather than the -(sometimes hard to predict or control) order of rejections in the rejection set. - -Once you have defined your custom ``RejectionHandler`` you have two options for "activating" it: - -1. Bring it into implicit scope at the top-level. -2. Supply it as argument to the :ref:`-handleRejections-` directive. - -In the first case your handler will be "sealed" (which means that it will receive the default handler as a fallback for -all cases your handler doesn't handle itself) and used for all rejections that are not handled within the route structure -itself. - -The second case allows you to restrict the applicability of your handler to certain branches of your route structure. diff --git a/akka-docs/rst/scala/http/routing-dsl/routes.rst b/akka-docs/rst/scala/http/routing-dsl/routes.rst deleted file mode 100644 index 27b9ab1bdc..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/routes.rst +++ /dev/null @@ -1,120 +0,0 @@ -.. _Routes: - -Routes -====== - -The "Route" is the central concept of Akka HTTP's Routing DSL. All the structures you build with the DSL, no matter -whether they consists of a single line or span several hundred lines, are instances of this type:: - - type Route = RequestContext ⇒ Future[RouteResult] - -It's a simple alias for a function turning a ``RequestContext`` into a ``Future[RouteResult]``. - -Generally when a route receives a request (or rather a ``RequestContext`` for it) it can do one of these things: - -- Complete the request by returning the value of ``requestContext.complete(...)`` -- Reject the request by returning the value of ``requestContext.reject(...)`` (see :ref:`rejections-scala`) -- Fail the request by returning the value of ``requestContext.fail(...)`` or by just throwing an exception (see :ref:`exception-handling-scala`) -- Do any kind of asynchronous processing and instantly return a ``Future[RouteResult]`` to be eventually completed later - -The first case is pretty clear, by calling ``complete`` a given response is sent to the client as reaction to the -request. In the second case "reject" means that the route does not want to handle the request. You'll see further down -in the section about route composition what this is good for. - -A ``Route`` can be "sealed" using ``Route.seal``, which relies on the in-scope ``RejectionHandler`` and ``ExceptionHandler`` -instances to convert rejections and exceptions into appropriate HTTP responses for the client. - -Using ``Route.handlerFlow`` or ``Route.asyncHandler`` a ``Route`` can be lifted into a handler ``Flow`` or async handler -function to be used with a ``bindAndHandleXXX`` call from the :ref:`http-low-level-server-side-api`. - -Note: There is also an implicit conversion from ``Route`` to ``Flow[HttpRequest, HttpResponse, Unit]`` defined in the -``RouteResult`` companion, which relies on ``Route.handlerFlow``. - - -.. _RequestContext: - -RequestContext --------------- - -The request context wraps an ``HttpRequest`` instance to enrich it with additional information that are typically -required by the routing logic, like an ``ExecutionContext``, ``Materializer``, ``LoggingAdapter`` and the configured -``RoutingSettings``. It also contains the ``unmatchedPath``, a value that describes how much of the request URI has not -yet been matched by a :ref:`Path Directive `. - -The ``RequestContext`` itself is immutable but contains several helper methods which allow for convenient creation of -modified copies. - - -.. _RouteResult: - -RouteResult ------------ - -``RouteResult`` is a simple abstract data type (ADT) that models the possible non-error results of a ``Route``. -It is defined as such:: - - sealed trait RouteResult - - object RouteResult { - final case class Complete(response: HttpResponse) extends RouteResult - final case class Rejected(rejections: immutable.Seq[Rejection]) extends RouteResult - } - -Usually you don't create any ``RouteResult`` instances yourself, but rather rely on the pre-defined :ref:`RouteDirectives` -(like :ref:`-complete-`, :ref:`-reject-` or :ref:`-redirect-`) or the respective methods on the :ref:`RequestContext` -instead. - - -Composing Routes ----------------- - -There are three basic operations we need for building more complex routes from simpler ones: - -- Route transformation, which delegates processing to another, "inner" route but in the process changes some properties - of either the incoming request, the outgoing response or both -- Route filtering, which only lets requests satisfying a given filter condition pass and rejects all others -- Route chaining, which tries a second route if a given first one was rejected - -The last point is achieved with the concatenation operator ``~``, which is an extension method that becomes available -when you ``import akka.http.scaladsl.server.Directives._``. -The first two points are provided by so-called :ref:`Directives` of which a large number is already predefined by Akka -HTTP and which you can also easily create yourself. -:ref:`Directives` deliver most of Akka HTTP's power and flexibility. - - -.. _The Routing Tree: - -The Routing Tree ----------------- - -Essentially, when you combine directives and custom routes via nesting and the ``~`` operator, you build a routing -structure that forms a tree. When a request comes in it is injected into this tree at the root and flows down through -all the branches in a depth-first manner until either some node completes it or it is fully rejected. - -Consider this schematic example:: - - val route = - a { - b { - c { - ... // route 1 - } ~ - d { - ... // route 2 - } ~ - ... // route 3 - } ~ - e { - ... // route 4 - } - } - -Here five directives form a routing tree. - -- Route 1 will only be reached if directives ``a``, ``b`` and ``c`` all let the request pass through. -- Route 2 will run if ``a`` and ``b`` pass, ``c`` rejects and ``d`` passes. -- Route 3 will run if ``a`` and ``b`` pass, but ``c`` and ``d`` reject. - -Route 3 can therefore be seen as a "catch-all" route that only kicks in, if routes chained into preceding positions -reject. This mechanism can make complex filtering logic quite easy to implement: simply put the most -specific cases up front and the most general cases in the back. diff --git a/akka-docs/rst/scala/http/routing-dsl/source-streaming-support.rst b/akka-docs/rst/scala/http/routing-dsl/source-streaming-support.rst deleted file mode 100644 index 38baaa6bd5..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/source-streaming-support.rst +++ /dev/null @@ -1,136 +0,0 @@ -.. _json-streaming-scala: - -Source Streaming -================ - -Akka HTTP supports completing a request with an Akka ``Source[T, _]``, which makes it possible to easily build -and consume streaming end-to-end APIs which apply back-pressure throughout the entire stack. - -It is possible to complete requests with raw ``Source[ByteString, _]``, however often it is more convenient to -stream on an element-by-element basis, and allow Akka HTTP to handle the rendering internally - for example as a JSON array, -or CSV stream (where each element is separated by a new-line). - -In the following sections we investigate how to make use of the JSON Streaming infrastructure, -however the general hints apply to any kind of element-by-element streaming you could imagine. - -JSON Streaming -============== - -`JSON Streaming`_ is a term refering to streaming a (possibly infinite) stream of element as independent JSON -objects as a continuous HTTP request or response. The elements are most often separated using newlines, -however do not have to be. Concatenating elements side-by-side or emitting "very long" JSON array is also another -use case. - -In the below examples, we'll be refering to the ``Tweet`` and ``Measurement`` case classes as our model, which are defined as: - -.. includecode2:: ../../code/docs/http/scaladsl/server/directives/JsonStreamingExamplesSpec.scala - :snippet: models - -And as always with spray-json, we provide our (Un)Marshaller instances as implicit values using the ``jsonFormat##`` -method to generate them statically: - -.. includecode2:: ../../code/docs/http/scaladsl/server/directives/JsonStreamingExamplesSpec.scala - :snippet: formats - -.. _Json Streaming: https://en.wikipedia.org/wiki/JSON_Streaming - -Responding with JSON Streams ----------------------------- - -In this example we implement an API representing an infinite stream of tweets, very much like Twitter's `Streaming API`_. - -Firstly, we'll need to get some additional marshalling infrastructure set up, that is able to marshal to and from an -Akka Streams ``Source[T,_]``. One such trait, containing the needed marshallers is ``SprayJsonSupport``, which uses -spray-json (a high performance json parser library), and is shipped as part of Akka HTTP in the -``akka-http-spray-json-experimental`` module. - -Once the general infrastructure is prepared we import our model's marshallers, generated by spray-json (Step 1), -and enable JSON Streaming by making an implicit ``EntityStreamingSupport`` instance available (Step 2). -Akka HTTP pre-packages JSON and CSV entity streaming support, however it is simple to add your own, in case you'd -like to stream a different content type (for example plists or protobuf). - -The final step is simply completing a request using a Source of tweets, as simple as that: - -.. includecode2:: ../../code/docs/http/scaladsl/server/directives/JsonStreamingExamplesSpec.scala - :snippet: spray-json-response-streaming - -The reason the ``EntityStreamingSupport`` has to be enabled explicitly is that one might want to configure how the -stream should be rendered. We'll dicuss this in depth in the next section though. - -.. _Streaming API: https://dev.twitter.com/streaming/overview - -Customising response rendering mode -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Since it is not always possible to directly and confidently answer the question of how a stream of ``T`` should look on -the wire, the ``EntityStreamingSupport`` traits come into play and allow fine-tuning the streams rendered representation. - -For example, in case of JSON Streaming, there isn't really one standard about rendering the response. Some APIs prefer -to render multiple JSON objects in a line-by-line fashion (Twitter's streaming APIs for example), while others simply return -very large arrays, which could be streamed as well. - -Akka defaults to the second one (streaming a JSON Array), as it is correct JSON and clients not expecting -a streaming API would still be able to consume it in a naive way if they'd want to. - -The line-by-line aproach however is also pretty popular even though it is not valid JSON. It's relatively simplicity for -client-side parsing is a strong point in case to pick this format for your Streaming APIs. -Below we demonstrate how to reconfigure the support trait to render the JSON as - -.. includecode2:: ../../code/docs/http/scaladsl/server/directives/JsonStreamingExamplesSpec.scala - :snippet: line-by-line-json-response-streaming - -Another interesting feature is parallel marshalling. Since marshalling can potentially take much time, -it is possible to marshal multiple elements of the stream in parallel. This is simply a configuration -option on ``EntityStreamingSupport`` and is configurable like this: - -.. includecode2:: ../../code/docs/http/scaladsl/server/directives/JsonStreamingExamplesSpec.scala - :snippet: async-rendering - -The above shown mode perserves ordering of the Source's elements, which may sometimes be a required property, -for example when streaming a strictly ordered dataset. Sometimes the contept of strict-order does not apply to the -data being streamed though, which allows us to exploit this property and use an ``unordered`` rendering. - -This also is a configuration option and is used as shown below. Effectively this will allow Akka's marshalling infrastructure -to concurrently marshallup to ``parallelism`` elements and emit the first which is marshalled onto the ``HttpResponse``: - -.. includecode2:: ../../code/docs/http/scaladsl/server/directives/JsonStreamingExamplesSpec.scala - :snippet: async-unordered-rendering - -This allows us to _potentially_ render elements faster onto the HttpResponse, since it can avoid "head of line blocking", -in case one element in front of the stream takes a long time to marshall, yet others after it are very quick to marshall. - -Consuming JSON Streaming uploads --------------------------------- - -Sometimes the client may be sending a streaming request, for example an embedded device initiated a connection with -the server and is feeding it with one line of measurement data. - -In this example, we want to consume this data in a streaming fashion from the request entity, and also apply -back-pressure to the underlying TCP connection, if the server can not cope with the rate of incoming data (back-pressure -will be applied automatically thanks to using Akka HTTP/Streams). - - -.. includecode2:: ../../code/docs/http/scaladsl/server/directives/JsonStreamingExamplesSpec.scala - :snippet: spray-json-request-streaming - -Simple CSV streaming example ----------------------------- - -Akka HTTP provides another ``EntityStreamingSupport`` out of the box, namely ``csv`` (comma-separated values). -For completeness, we demonstrate its usage in the below snippet. As you'll notice, switching betweeen streaming -modes is fairly simple, one only has to make sure that an implicit ``Marshaller`` of the requested type is available, -and that the streaming support operates on the same ``Content-Type`` as the rendered values. Otherwise you'll see -an error during runtime that the marshaller did not expose the expected content type and thus we can not render -the streaming response). - -.. includecode2:: ../../code/docs/http/scaladsl/server/directives/JsonStreamingExamplesSpec.scala - :snippet: csv-example - -Implementing custom EntityStreamingSupport traits -------------------------------------------------- - -The ``EntityStreamingSupport`` infrastructure is open for extension and not bound to any single format, content type -or marshalling library. The provided JSON support does not rely on Spray JSON directly, but uses ``Marshaller[T, ByteString]`` -instances, which can be provided using any JSON marshalling library (such as Circe, Jawn or Play JSON). - -When implementing a custom support trait, one should simply extend the ``EntityStreamingSupport`` abstract class, -and implement all of it's methods. It's best to use the existing implementations as a guideline. diff --git a/akka-docs/rst/scala/http/routing-dsl/testkit.rst b/akka-docs/rst/scala/http/routing-dsl/testkit.rst deleted file mode 100644 index 35b90a1f80..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/testkit.rst +++ /dev/null @@ -1,110 +0,0 @@ -Route TestKit -============= - -One of Akka HTTP's design goals is good testability of the created services. -For services built with the Routing DSL Akka HTTP provides a dedicated testkit that makes efficient testing of -route logic easy and convenient. This "route test DSL" is made available with the *akka-http-testkit* module. -To use it include the following dependency:: - - "com.typesafe.akka" %% "akka-http-testkit" % "@version@" - - -Usage ------ - -Here is an example of what a simple test with the routing testkit might look like (using the built-in support for -scalatest_): - -.. includecode2:: ../../code/docs/http/scaladsl/server/FullTestKitExampleSpec.scala - :snippet: source-quote - -The basic structure of a test built with the testkit is this (expression placeholder in all-caps):: - - REQUEST ~> ROUTE ~> check { - ASSERTIONS - } - -In this template *REQUEST* is an expression evaluating to an ``HttpRequest`` instance. -In most cases your test will, in one way or another, extend from ``RouteTest`` which itself mixes in the -``akka.http.scaladsl.client.RequestBuilding`` trait, which gives you a concise and convenient way of constructing -test requests. [1]_ - -*ROUTE* is an expression evaluating to a :ref:`Route `. You can specify one inline or simply refer to the -route structure defined in your service. - -The final element of the ``~>`` chain is a ``check`` call, which takes a block of assertions as parameter. In this block -you define your requirements onto the result produced by your route after having processed the given request. Typically -you use one of the defined "inspectors" to retrieve a particular element of the routes response and express assertions -against it using the test DSL provided by your test framework. For example, with scalatest_, in order to verify that -your route responds to the request with a status 200 response, you'd use the ``status`` inspector and express an -assertion like this:: - - status shouldEqual 200 - -The following inspectors are defined: - -================================================ ======================================================================= -Inspector Description -================================================ ======================================================================= -``charset: HttpCharset`` Identical to ``contentType.charset`` -``chunks: Seq[HttpEntity.ChunkStreamPart]`` Returns the entity chunks produced by the route. If the entity is not - ``chunked`` returns ``Nil``. -``closingExtension: String`` Returns chunk extensions the route produced with its last response - chunk. If the response entity is unchunked returns the empty string. -``contentType: ContentType`` Identical to ``responseEntity.contentType`` -``definedCharset: Option[HttpCharset]`` Identical to ``contentType.definedCharset`` -``entityAs[T :FromEntityUnmarshaller]: T`` Unmarshals the response entity using the in-scope - ``FromEntityUnmarshaller`` for the given type. Any errors in the - process trigger a test failure. -``handled: Boolean`` Indicates whether the route produced an ``HttpResponse`` for the - request. If the route rejected the request ``handled`` evaluates to - ``false``. -``header(name: String): Option[HttpHeader]`` Returns the response header with the given name or ``None`` if no such - header is present in the response. -``header[T <: HttpHeader]: Option[T]`` Identical to ``response.header[T]`` -``headers: Seq[HttpHeader]`` Identical to ``response.headers`` -``mediaType: MediaType`` Identical to ``contentType.mediaType`` -``rejection: Rejection`` The rejection produced by the route. If the route did not produce - exactly one rejection a test failure is triggered. -``rejections: Seq[Rejection]`` The rejections produced by the route. If the route did not reject the - request a test failure is triggered. -``response: HttpResponse`` The ``HttpResponse`` returned by the route. If the route did not return - an ``HttpResponse`` instance (e.g. because it rejected the request) a - test failure is triggered. -``responseAs[T: FromResponseUnmarshaller]: T`` Unmarshals the response entity using the in-scope - ``FromResponseUnmarshaller`` for the given type. Any errors in the - process trigger a test failure. -``responseEntity: HttpEntity`` Returns the response entity. -``status: StatusCode`` Identical to ``response.status`` -``trailer: Seq[HttpHeader]`` Returns the list of trailer headers the route produced with its last - chunk. If the response entity is unchunked returns ``Nil``. -================================================ ======================================================================= - -.. [1] If the request URI is relative it will be made absolute using an implicitly available instance of - ``DefaultHostInfo`` whose value is "http://example.com" by default. This mirrors the behavior of akka-http-core - which always produces absolute URIs for incoming request based on the request URI and the ``Host``-header of - the request. You can customize this behavior by bringing a custom instance of ``DefaultHostInfo`` into scope. - - -Sealing Routes --------------- - -The section above describes how to test a "regular" branch of your route structure, which reacts to incoming requests -with HTTP response parts or rejections. Sometimes, however, you will want to verify that your service also translates -:ref:`rejections-scala` to HTTP responses in the way you expect. - -You do this by wrapping your route with the ``akka.http.scaladsl.server.Route.seal``. -The ``seal`` wrapper applies the logic of the in-scope :ref:`ExceptionHandler ` and -:ref:`RejectionHandler ` to all exceptions and rejections coming back from the route, -and translates them to the respective ``HttpResponse``. - - -Examples --------- - -A great pool of examples are the tests for all the predefined directives in Akka HTTP. -They can be found here__. - -__ @github@/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/ - -.. _scalatest: http://www.scalatest.org \ No newline at end of file diff --git a/akka-docs/rst/scala/http/routing-dsl/websocket-support.rst b/akka-docs/rst/scala/http/routing-dsl/websocket-support.rst deleted file mode 100644 index ff9da67297..0000000000 --- a/akka-docs/rst/scala/http/routing-dsl/websocket-support.rst +++ /dev/null @@ -1,119 +0,0 @@ -.. _server-side-websocket-support-scala: - -Server-Side WebSocket Support -============================= - -WebSocket is a protocol that provides a bi-directional channel between browser and webserver usually run over an -upgraded HTTP(S) connection. Data is exchanged in messages whereby a message can either be binary data or unicode text. - -Akka HTTP provides a stream-based implementation of the WebSocket protocol that hides the low-level details of the -underlying binary framing wire-protocol and provides a simple API to implement services using WebSocket. - - -Model ------ - -The basic unit of data exchange in the WebSocket protocol is a message. A message can either be binary message, -i.e. a sequence of octets or a text message, i.e. a sequence of unicode code points. - -Akka HTTP provides a straight-forward model for this abstraction: - -.. includecode:: /../../akka-http-core/src/main/scala/akka/http/scaladsl/model/ws/Message.scala - :include: message-model - -The data of a message is provided as a stream because WebSocket messages do not have a predefined size and could -(in theory) be infinitely long. However, only one message can be open per direction of the WebSocket connection, -so that many application level protocols will want to make use of the delineation into (small) messages to transport -single application-level data units like "one event" or "one chat message". - -Many messages are small enough to be sent or received in one go. As an opportunity for optimization, the model provides -a ``Strict`` subclass for each kind of message which contains data as a strict, i.e. non-streamed, ``ByteString`` or -``String``. - -When receiving data from the network connection the WebSocket implementation tries to create a ``Strict`` message whenever -possible, i.e. when the complete data was received in one chunk. However, the actual chunking of messages over a network -connection and through the various streaming abstraction layers is not deterministic from the perspective of the -application. Therefore, application code must be able to handle both streamed and strict messages and not expect -certain messages to be strict. (Particularly, note that tests against ``localhost`` will behave differently than tests -against remote peers where data is received over a physical network connection.) - -For sending data, use ``TextMessage.apply(text: String)`` to create a ``Strict`` message which is often the natural -choice when the complete message has already been assembled. Otherwise, use ``TextMessage.apply(textStream: Source[String, Any])`` -to create a streamed message from an Akka Stream source. - -Server API ----------- - -The entrypoint for the WebSocket API is the synthetic ``UpgradeToWebSocket`` header which is added to a request -if Akka HTTP encounters a WebSocket upgrade request. - -The WebSocket specification mandates that details of the WebSocket connection are negotiated by placing special-purpose -HTTP-headers into request and response of the HTTP upgrade. In Akka HTTP these HTTP-level details of the WebSocket -handshake are hidden from the application and don't need to be managed manually. - -Instead, the synthetic ``UpgradeToWebSocket`` represents a valid WebSocket upgrade request. An application can detect -a WebSocket upgrade request by looking for the ``UpgradeToWebSocket`` header. It can choose to accept the upgrade and -start a WebSocket connection by responding to that request with an ``HttpResponse`` generated by one of the -``UpgradeToWebSocket.handleMessagesWith`` methods. In its most general form this method expects two arguments: -first, a handler ``Flow[Message, Message, Any]`` that will be used to handle WebSocket messages on this connection. -Second, the application can optionally choose one of the proposed application-level sub-protocols by inspecting the -values of ``UpgradeToWebSocket.requestedProtocols`` and pass the chosen protocol value to ``handleMessages``. - -Handling Messages -+++++++++++++++++ - -A message handler is expected to be implemented as a ``Flow[Message, Message, Any]``. For typical request-response -scenarios this fits very well and such a ``Flow`` can be constructed from a simple function by using -``Flow[Message].map`` or ``Flow[Message].mapAsync``. - -There are other use-cases, e.g. in a server-push model, where a server message is sent spontaneously, or in a -true bi-directional scenario where input and output aren't logically connected. Providing the handler as a ``Flow`` in -these cases may not fit. Another method, ``UpgradeToWebSocket.handleMessagesWithSinkSource``, is provided -which allows to pass an output-generating ``Source[Message, Any]`` and an input-receiving ``Sink[Message, Any]`` independently. - -Note that a handler is required to consume the data stream of each message to make place for new messages. Otherwise, -subsequent messages may be stuck and message traffic in this direction will stall. - -Example -+++++++ - -Let's look at an example_. - -WebSocket requests come in like any other requests. In the example, requests to ``/greeter`` are expected to be -WebSocket requests: - -.. includecode:: ../../code/docs/http/scaladsl/server/WebSocketExampleSpec.scala - :include: websocket-request-handling - -It uses pattern matching on the path and then inspects the request to query for the ``UpgradeToWebSocket`` header. If -such a header is found, it is used to generate a response by passing a handler for WebSocket messages to the -``handleMessages`` method. If no such header is found a "400 Bad Request" response is generated. - -The passed handler expects text messages where each message is expected to contain (a person's) name -and then responds with another text message that contains a greeting: - -.. includecode:: ../../code/docs/http/scaladsl/server/WebSocketExampleSpec.scala - :include: websocket-handler - -.. note:: - Inactive WebSocket connections will be dropped according to the :ref:`idle-timeout settings `. - In case you need to keep inactive connections alive, you can either tweak your idle-timeout or inject - 'keep-alive' messages regularly. - -Routing support ---------------- - -The routing DSL provides the :ref:`-handleWebSocketMessages-` directive to install a WebSocket handler if the request -was a WebSocket request. Otherwise, the directive rejects the request. - -Here's the above simple request handler rewritten as a route: - -.. includecode2:: ../../code/docs/http/scaladsl/server/directives/WebSocketDirectivesExamplesSpec.scala - :snippet: greeter-service - -The example also includes code demonstrating the testkit support for WebSocket services. It allows to create WebSocket -requests to run against a route using `WS` which can be used to provide a mock WebSocket probe that allows manual -testing of the WebSocket handler's behavior if the request was accepted. - - -.. _example: @github@/akka-docs/rst/scala/code/docs/http/scaladsl/server/WebSocketExampleSpec.scala diff --git a/akka-docs/rst/scala/http/server-side-https-support.rst b/akka-docs/rst/scala/http/server-side-https-support.rst deleted file mode 100644 index 7fe02214ae..0000000000 --- a/akka-docs/rst/scala/http/server-side-https-support.rst +++ /dev/null @@ -1,127 +0,0 @@ -.. _serverSideHTTPS-scala: - -Server-Side HTTPS Support -========================= - -Akka HTTP supports TLS encryption on the server-side as well as on the :ref:`client-side `. - -The central vehicle for configuring encryption is the ``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/scaladsl/ConnectionContext.scala - :include: https-context-creation - -On the server-side the ``bind``, and ``bindAndHandleXXX`` methods of the `akka.http.scaladsl.Http`_ extension define an -optional ``httpsContext`` parameter, which can receive the HTTPS configuration in the form of an ``HttpsContext`` -instance. -If defined encryption is enabled on all accepted connections. Otherwise it is disabled (which is the default). - -For detailed documentation for client-side HTTPS support refer to :ref:`clientSideHTTPS`. - - -.. _akka.http.scaladsl.Http: https://github.com/akka/akka/blob/master/akka-http-core/src/main/scala/akka/http/scaladsl/Http.scala - - -.. _ssl-config-scala: - -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/ - -In order to use SSL-Config in Akka so it logs to the right ActorSystem-wise logger etc., the -``AkkaSSLConfig`` extension is provided. Obtaining it is as simple as: - -.. includecode2:: ../code/docs/http/scaladsl/server/HttpsServerExampleSpec.scala - :snippet: akka-ssl-config - -While typical usage, for example for configuring http client settings would be applied globally by configuring -ssl-config in ``application.conf``, it's possible to obtain the extension and ``copy`` it while modifying any -configuration that you might need to change and then use that specific ``AkkaSSLConfig`` instance while establishing -connections be it client or server-side. - -Obtaining SSL/TLS Certificates ------------------------------- -In order to run an HTTPS server a certificate has to be provided, which usually is either obtained from a signing -authority or created by yourself for local or staging environment purposes. - -Signing authorities often provide instructions on how to create a Java keystore (typically with reference to Tomcat -configuration). If you want to generate your own certificates, the official Oracle documentation on how to generate -keystores using the JDK keytool utility can be found `here `_. - -SSL-Config provides a more targeted guide on generating certificates, so we recommend you start with the guide -titled `Generating X.509 Certificates `_. - - -.. _using-https-scala: - -Using HTTPS ------------ - -Once you have obtained the server certificate, using it is as simple as preparing an ``HttpsConnectionContext`` -and either setting it as the default one to be used by all servers started by the given ``Http`` extension -or passing it in explicitly when binding the server: - - -.. includecode2:: ../code/docs/http/scaladsl/server/HttpsServerExampleSpec.scala - :snippet: imports - -.. includecode2:: ../code/docs/http/scaladsl/server/HttpsServerExampleSpec.scala - :snippet: low-level-default - -Once you configured the HTTPS context, you can set it as default: - -.. includecode2:: ../code/docs/http/scaladsl/server/HttpsServerExampleSpec.scala - :snippet: set-low-level-context-default - -It is also possible to pass in the context to specific ``bind...`` (or client) calls, like displayed below: - -.. includecode2:: ../code/docs/http/scaladsl/server/HttpsServerExampleSpec.scala - :snippet: bind-low-level-context - - -Running both HTTP and HTTPS ---------------------------- -If you want to run HTTP and HTTPS servers in a single application, you can call ``bind...`` methods twice, -one for HTTPS, and the other for HTTP. - -When configuring HTTPS, you can do it up like explained in the above :ref:`using-https-scala` section, - -.. includecode2:: ../code/docs/http/scaladsl/server/HttpsServerExampleSpec.scala - :snippet: low-level-default - -or via :ref:`ssl-config-scala` (not explained here though). - -Then, call ``bind...`` methods twice like below. The passed ``https`` context is from the above code snippet. - -.. includecode2:: ../code/docs/http/scaladsl/server/HttpsServerExampleSpec.scala - :snippet: both-https-and-http - -Further reading ---------------- - -The topic of properly configuring HTTPS for your web server is an always changing one, -thus we recommend staying up to date with various security breach news and of course -keep your JVM at the latest version possible, as the default settings are often updated by -Oracle in reaction to various security updates and known issues. - -We also recommend having a look at the `Play documentation about securing your app`_, -as well as the techniques described in the Play documentation about setting up a `reverse proxy to terminate TLS in -front of your application`_ instead of terminating TLS inside the JVM, and therefore Akka HTTP, itself. - -Other excellent articles on the subject: - -- `Oracle Java SE 8: Creating a Keystore using JSSE `_ -- `Java PKI Programmer's Guide `_ -- `Fixing X.509 Certificates `_ - -.. _Play documentation about securing your app: https://www.playframework.com/documentation/2.5.x/ConfiguringHttps#ssl-certificates -.. _reverse proxy to terminate TLS in front of your application: https://www.playframework.com/documentation/2.5.x/HTTPServer \ No newline at end of file diff --git a/akka-docs/rst/scala/stream/stream-io.rst b/akka-docs/rst/scala/stream/stream-io.rst index 0dd1201d68..c04c4d7109 100644 --- a/akka-docs/rst/scala/stream/stream-io.rst +++ b/akka-docs/rst/scala/stream/stream-io.rst @@ -9,11 +9,6 @@ While the general approach is very similar to the :ref:`Actor based TCP handling by using Akka Streams you are freed of having to manually react to back-pressure signals, as the library does it transparently for you. -.. note:: - If you are not familiar with Akka Streams basic concepts, like ``Source``, ``Sink`` and ``Flow``, - please refer to the :ref:`Streams section ` of the document. Also higher level APIs like ``bind`` are - used in Akka HTTP too. So you may get some hints from the Akka HTTP :ref:`introduction `. - Streaming TCP ============= diff --git a/akka-http-core/RunWebSocketAutobahnTestSuite.md b/akka-http-core/RunWebSocketAutobahnTestSuite.md deleted file mode 100644 index 9591870e75..0000000000 --- a/akka-http-core/RunWebSocketAutobahnTestSuite.md +++ /dev/null @@ -1,51 +0,0 @@ -# Test the client side - -Start up the testsuite with docker: - -``` -docker run -ti --rm=true -p 8080:8080 -p 9001:9001 jrudolph/autobahn-testsuite -``` - -Then in sbt, to run all tests, use - -``` -akka-http-core-experimental/test:run-main akka.http.impl.engine.ws.WSClientAutobahnTest -``` - -or, to run a single test, use - -``` -akka-http-core-experimental/test:run-main akka.http.impl.engine.ws.WSClientAutobahnTest 1.1.1 -``` - -After a run, you can access the results of the run at http://localhost:8080/cwd/reports/clients/index.html. - -You can supply a configuration file for autobahn by mounting a version of `fuzzingserver.json` to `/tmp/fuzzingserver.json` -of the container, e.g. using this docker option: - -``` --v /fullpath-on-host/my-fuzzingserver-config.json:/tmp/fuzzingserver.json -``` - -# Test the server side - -Start up the test server in sbt: - -``` -akka-http-core-experimental/test:run-main akka.http.impl.engine.ws.WSServerAutobahnTest -``` - -Then, run the test suite with docker: - -``` -docker run -ti --rm=true -v `pwd`/reports:/tmp/server-report jrudolph/autobahn-testsuite-client -``` - -This will put the result report into a `reports` directory in the current working directory on the host. - -You can supply a configuration file for autobahn by mounting a version of `fuzzingclient.json` to `/tmp/fuzzingclient.json` -of the container, e.g. using this docker option: - -``` --v /fullpath-on-host/my-fuzzingclient-config.json:/tmp/fuzzingclient.json -``` diff --git a/akka-http-core/build.sbt b/akka-http-core/build.sbt deleted file mode 100644 index 4db8b9cbea..0000000000 --- a/akka-http-core/build.sbt +++ /dev/null @@ -1,6 +0,0 @@ -import akka._ - -AkkaBuild.defaultSettings -Formatting.formatSettings -OSGi.httpCore -Dependencies.httpCore diff --git a/akka-http-core/src/main/java/akka/http/impl/util/Util.java b/akka-http-core/src/main/java/akka/http/impl/util/Util.java deleted file mode 100644 index ce73919d32..0000000000 --- a/akka-http-core/src/main/java/akka/http/impl/util/Util.java +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.util; - -import akka.http.impl.model.JavaUri; -import akka.http.javadsl.model.Uri; -import scala.compat.java8.OptionConverters; -import scala.None$; -import scala.collection.immutable.Map$; -import scala.collection.immutable.Seq; -import akka.stream.scaladsl.Source; - -import java.util.Arrays; -import java.util.Map; -import java.util.Optional; - -/** - * Contains internal helper methods. - */ -public abstract class Util { - @SuppressWarnings("unchecked") // no support for covariance of option in Java - // needed to provide covariant conversions that the Java interfaces don't provide automatically. - // The alternative would be having to cast around everywhere instead of doing it here in a central place. - public static Optional convertOption(scala.Option o) { - return (Optional)(Object) OptionConverters.toJava(o); - } - @SuppressWarnings("unchecked") // no support for covariance of Publisher in Java - // needed to provide covariant conversions that the Java interfaces don't provide automatically. - // The alternative would be having to cast around everywhere instead of doing it here in a central place. - public static Source convertPublisher(Source p) { - return (Source)(Object) p; - } - @SuppressWarnings("unchecked") - public static Source upcastSource(Source p) { - return (Source)(Object) p; - } - public static scala.collection.immutable.Map convertMapToScala(Map map) { - return emptyMap.$plus$plus(scala.collection.JavaConverters.mapAsScalaMapConverter(map).asScala()); - } - @SuppressWarnings("unchecked") // contains an upcast - public static scala.Option convertOptionalToScala(Optional o) { - return OptionConverters.toScala((Optional) o); - } - - public static final scala.collection.immutable.Map emptyMap = - Map$.MODULE$.empty(); - - public static final None$ noneValue = None$.MODULE$; - @SuppressWarnings("unchecked") - public static scala.Option scalaNone() { - return (scala.Option) noneValue; - } - - @SuppressWarnings("unchecked") - public static Seq convertIterable(Iterable els) { - return scala.collection.JavaConverters.iterableAsScalaIterableConverter((Iterable)els).asScala().toVector(); - } - public static Seq convertArray(T[] els) { - return Util.convertIterable(Arrays.asList(els)); - } - - public static akka.http.scaladsl.model.Uri convertUriToScala(Uri uri) { - return ((JavaUri) uri).uri(); - } - - public static Optional lookupInRegistry(ObjectRegistry registry, int key) { - return Util.convertOption(registry.getForKey(key)); - } - public static Optional lookupInRegistry(ObjectRegistry registry, String key) { - return Util.lookupInRegistry(registry, key); - } - public static Optional lookupInRegistry(ObjectRegistry registry, K key) { - return Util.convertOption(registry.getForKey(key)); - } - -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/TimeoutAccess.java b/akka-http-core/src/main/java/akka/http/javadsl/TimeoutAccess.java deleted file mode 100644 index 47e1ff07d2..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/TimeoutAccess.java +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl; - -import akka.http.javadsl.model.HttpRequest; -import akka.http.javadsl.model.HttpResponse; -import akka.japi.Function; -import scala.concurrent.duration.Duration; - -/** - * Enables programmatic access to the server-side request timeout logic. - */ -public interface TimeoutAccess { - - /** - * Tries to set a new timeout. - * The timeout period is measured as of the point in time that the end of the request has been received, - * which may be in the past or in the future! - * Use `Duration.Inf` to completely disable request timeout checking for this request. - * - * Due to the inherent raciness it is not guaranteed that the update will be applied before - * the previously set timeout has expired! - */ - void updateTimeout(Duration timeout); - - /** - * Tries to set a new timeout handler, which produces the timeout response for a - * given request. Note that the handler must produce the response synchronously and shouldn't block! - * - * Due to the inherent raciness it is not guaranteed that the update will be applied before - * the previously set timeout has expired! - */ - void updateHandler(Function handler); - - /** - * Tries to set a new timeout and handler at the same time. - * - * Due to the inherent raciness it is not guaranteed that the update will be applied before - * the previously set timeout has expired! - */ - void update(Duration timeout, Function handler); -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/BodyPartEntity.java b/akka-http-core/src/main/java/akka/http/javadsl/model/BodyPartEntity.java deleted file mode 100644 index 68db35c171..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/BodyPartEntity.java +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -/** Marker-interface for entity types that can be used in a body part */ -public interface BodyPartEntity extends HttpEntity {} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/ContentRange.java b/akka-http-core/src/main/java/akka/http/javadsl/model/ContentRange.java deleted file mode 100644 index 9f9ac8102b..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/ContentRange.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -import akka.http.scaladsl.model.ContentRange$; - -import java.util.Optional; -import java.util.OptionalLong; -import scala.compat.java8.OptionConverters; - -public abstract class ContentRange { - public abstract boolean isByteContentRange(); - public abstract boolean isSatisfiable(); - public abstract boolean isOther(); - - public abstract OptionalLong getSatisfiableFirst(); - public abstract OptionalLong getSatisfiableLast(); - - public abstract Optional getOtherValue(); - - public abstract OptionalLong getInstanceLength(); - - public static ContentRange create(long first, long last) { - return ContentRange$.MODULE$.apply(first, last); - } - public static ContentRange create(long first, long last, long instanceLength) { - return ContentRange$.MODULE$.apply(first, last, instanceLength); - } - @SuppressWarnings("unchecked") - public static ContentRange create(long first, long last, OptionalLong instanceLength) { - return ContentRange$.MODULE$.apply(first, last, OptionConverters.toScala(instanceLength)); - } - public static ContentRange createUnsatisfiable(long length) { - return new akka.http.scaladsl.model.ContentRange.Unsatisfiable(length); - } - public static ContentRange createOther(String value) { - return new akka.http.scaladsl.model.ContentRange.Other(value); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/ContentTypeRange.java b/akka-http-core/src/main/java/akka/http/javadsl/model/ContentTypeRange.java deleted file mode 100644 index d9d3056773..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/ContentTypeRange.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -/** - * A data structure that combines an acceptable media range and an acceptable charset range into - * one structure to be used with unmarshalling. - */ -public abstract class ContentTypeRange { - public abstract MediaRange mediaRange(); - - public abstract HttpCharsetRange charsetRange(); - - /** - * Returns true if this range includes the given content type. - */ - public abstract boolean matches(ContentType contentType); -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/ContentTypes.java b/akka-http-core/src/main/java/akka/http/javadsl/model/ContentTypes.java deleted file mode 100644 index 25c403ee63..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/ContentTypes.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.javadsl.model; - - -import akka.http.scaladsl.model.ContentType$; - -/** - * Contains the set of predefined content-types for convenience. - *

- * If the {@link ContentType} you're looking for is not pre-defined here, - * you can obtain it from a {@link MediaType} by using: {@code MediaTypes.TEXT_HTML.toContentType()} - */ -public final class ContentTypes { - private ContentTypes() { } - - public static final ContentType.WithFixedCharset APPLICATION_JSON = MediaTypes.APPLICATION_JSON.toContentType(); - public static final ContentType.Binary APPLICATION_OCTET_STREAM = MediaTypes.APPLICATION_OCTET_STREAM.toContentType(); - - public static final ContentType.WithCharset TEXT_PLAIN_UTF8 = - akka.http.scaladsl.model.ContentTypes.text$divplain$u0028UTF$minus8$u0029(); - public static final ContentType.WithCharset TEXT_HTML_UTF8 = - akka.http.scaladsl.model.ContentTypes.text$divhtml$u0028UTF$minus8$u0029(); - public static final ContentType.WithCharset TEXT_XML_UTF8 = - akka.http.scaladsl.model.ContentTypes.text$divxml$u0028UTF$minus8$u0029(); - - public static final ContentType.WithCharset TEXT_CSV_UTF8 = - akka.http.scaladsl.model.ContentTypes.text$divcsv$u0028UTF$minus8$u0029(); - - public static ContentType.Binary create(MediaType.Binary mediaType) { - return ContentType$.MODULE$.apply((akka.http.scaladsl.model.MediaType.Binary) mediaType); - } - - public static ContentType.WithFixedCharset create(MediaType.WithFixedCharset mediaType) { - return ContentType$.MODULE$.apply((akka.http.scaladsl.model.MediaType.WithFixedCharset) mediaType); - } - - public static ContentType.WithCharset create(MediaType.WithOpenCharset mediaType, HttpCharset charset) { - return ContentType$.MODULE$.apply((akka.http.scaladsl.model.MediaType.WithOpenCharset) mediaType, - (akka.http.scaladsl.model.HttpCharset) charset); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/DateTime.java b/akka-http-core/src/main/java/akka/http/javadsl/model/DateTime.java deleted file mode 100644 index 754c85d485..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/DateTime.java +++ /dev/null @@ -1,135 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -import akka.http.impl.util.Util; - -import java.util.Optional; - -/** - * Immutable, fast and efficient Date + Time implementation without any dependencies. - * Does not support TimeZones, all DateTime values are always GMT based. - * Note that this implementation discards milliseconds (i.e. rounds down to full seconds). - */ -public abstract class DateTime { - /** - * Returns the year of this instant in GMT. - */ - public abstract int year(); - - /** - * Returns the month of this instant in GMT. - */ - public abstract int month(); - - /** - * Returns the day of this instant in GMT. - */ - public abstract int day(); - - /** - * Returns the hour of this instant in GMT. - */ - public abstract int hour(); - - /** - * Returns the minute of this instant in GMT. - */ - public abstract int minute(); - - /** - * Returns the second of this instant in GMT. - */ - public abstract int second(); - - /** - * Returns the weekday of this instant in GMT. Sunday is 0, Monday is 1, etc. - */ - public abstract int weekday(); - - /** - * Returns this instant as "clicks", i.e. as milliseconds since January 1, 1970, 00:00:00 GMT - */ - public abstract long clicks(); - - /** - * Returns if this instant interpreted as a Date in GMT belongs to a leap year. - */ - public abstract boolean isLeapYear(); - - /** - * Returns the day of the week as a 3 letter abbreviation: - * `Sun`, `Mon`, `Tue`, `Wed`, `Thu`, `Fri` or `Sat` - */ - public abstract String weekdayStr(); - - /** - * Returns the month as a 3 letter abbreviation: - * `Jan`, `Feb`, `Mar`, `Apr`, `May`, `Jun`, `Jul`, `Aug`, `Sep`, `Oct`, `Nov` or `Dec` - */ - public abstract String monthStr(); - - /** - * Returns a String representation like this: `yyyy-mm-dd` - */ - public abstract String toIsoDateString(); - - /** - * Returns a String representation like this: `yyyy-mm-ddThh:mm:ss` - */ - public abstract String toIsoDateTimeString(); - - /** - * Returns a String representation like this: `yyyy-mm-dd hh:mm:ss` - */ - public abstract String toIsoLikeDateTimeString(); - - /** - * Returns an RFC1123 date string, e.g. `Sun, 06 Nov 1994 08:49:37 GMT` - */ - public abstract String toRfc1123DateTimeString(); - - /** - * Returns a new DateTime instance representing the current instant. - */ - public static DateTime now() { - return akka.http.scaladsl.model.DateTime.now(); - } - - /** - * Creates a new `DateTime` that represents the point in time the given number of ms earlier. - */ - public abstract DateTime minus(long millis); - - /** - * Creates a new `DateTime` that represents the point in time the given number of ms later. - */ - public abstract DateTime plus(long millis); - - /** - * Returns a new DateTime instance parsed from IsoDateTimeString as Some(dateTime). Returns None if - * parsing has failed. - */ - public static Optional fromIsoDateTimeString(String isoDateTimeString) { - return Util.convertOption(akka.http.scaladsl.model.DateTime.fromIsoDateTimeString(isoDateTimeString)); - } - - /** - * Returns a new DateTime instance representing the instant as defined by "clicks" - * i.e. from the number of milliseconds since the start of "the epoch", namely - * January 1, 1970, 00:00:00 GMT. - * Note that this implementation discards milliseconds (i.e. rounds down to full seconds). - */ - public static DateTime create(long clicks) { - return akka.http.scaladsl.model.DateTime.apply(clicks); - } - - /** - * Returns a new DateTime instance by interpreting the given date/time components as an instant in GMT. - */ - public static DateTime create(int year, int month, int day, int hour, int minute, int second) { - return akka.http.scaladsl.model.DateTime.apply(year, month, day, hour, minute, second); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/FormData.java b/akka-http-core/src/main/java/akka/http/javadsl/model/FormData.java deleted file mode 100644 index 656407b0f8..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/FormData.java +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -import java.util.Map; - -import akka.japi.Pair; - -/** - * Simple model for `application/x-www-form-urlencoded` form data. - */ -public final class FormData { - - private final Query fields; - - public FormData(Query fields) { - this.fields = fields; - } - - /** - * Converts this FormData to a RequestEntity using UTF8 encoding. - */ - public RequestEntity toEntity() { - return toEntity(HttpCharsets.UTF_8); - } - - /** - * Converts this FormData to a RequestEntity using the given encoding. - */ - public RequestEntity toEntity(HttpCharset charset) { - return HttpEntities.create(ContentTypes.create(MediaTypes.APPLICATION_X_WWW_FORM_URLENCODED, charset), fields.render(charset)); - } - - /** - * Returns empty FormData. - */ - public static final FormData EMPTY = new FormData(Query.EMPTY); - - /** - * Creates the FormData from the given parameters. - */ - @SafeVarargs - public static FormData create(Pair... params) { - return new FormData(Query.create(params)); - } - - /** - * Creates the FormData from the given parameters. - */ - public static FormData create(Map params) { - return new FormData(Query.create(params)); - } - - /** - * Creates a FormData from the given parameters. - */ - public static FormData create(Iterable> params) { - return new FormData(Query.create(params)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/Host.java b/akka-http-core/src/main/java/akka/http/javadsl/model/Host.java deleted file mode 100644 index a3af912a9d..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/Host.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -import akka.http.scaladsl.model.Uri; -import akka.http.scaladsl.model.UriJavaAccessor; - -import java.net.InetAddress; -import java.nio.charset.Charset; - -/** - * Represents a host in a URI or a Host header. The host can either be empty or be represented - * by an IPv4 or IPv6 address or by a host name. - */ -public abstract class Host { - /** - * Returns a String representation of the address. - */ - public abstract String address(); - public abstract boolean isEmpty(); - public abstract boolean isIPv4(); - public abstract boolean isIPv6(); - public abstract boolean isNamedHost(); - - /** - * Returns an Iterable of InetAddresses represented by this Host. If this Host is empty the - * returned Iterable will be empty. If this is an IP address the Iterable will contain this address. - * If this Host is represented by a host name, the name will be looked up and return all found - * addresses for this name. - */ - public abstract Iterable getInetAddresses(); - - /** - * The constant representing an empty Host. - */ - public static final Host EMPTY = UriJavaAccessor.emptyHost(); - - /** - * Parse the given Host string using the default charset and parsing-mode. - */ - public static Host create(String string) { - return UriJavaAccessor.hostApply(string); - } - - /** - * Parse the given Host string using the given charset and the default parsing-mode. - */ - public static Host create(String string, Uri.ParsingMode parsingMode) { - return UriJavaAccessor.hostApply(string, parsingMode); - } - - /** - * Parse the given Host string using the given charset and parsing-mode. - */ - public static Host create(String string, Charset charset, Uri.ParsingMode parsingMode) { - return akka.http.scaladsl.model.Uri.Host$.MODULE$.apply(string, charset, parsingMode); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpCharset.java b/akka-http-core/src/main/java/akka/http/javadsl/model/HttpCharset.java deleted file mode 100644 index 500fea2c68..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpCharset.java +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -import java.nio.charset.Charset; - -/** - * Represents a charset in Http. See {@link HttpCharsets} for a set of predefined charsets and - * static constructors to create custom charsets. - * - * @see HttpCharsets for convenience access to often used values. - */ -public abstract class HttpCharset { - /** - * Returns the name of this charset. - */ - public abstract String value(); - - /** - * Creates a range from this charset with qValue = 1. - */ - public HttpCharsetRange toRange() { - return withQValue(1f); - } - - /** - * Creates a range from this charset with the given qValue. - */ - public HttpCharsetRange toRange(float qValue) { - return withQValue(qValue); - } - - /** - * An alias for toRange(float). - */ - public abstract HttpCharsetRange withQValue(float qValue); - - /** - * Returns the predefined alias names for this charset. - */ - public abstract Iterable getAliases(); - - /** - * Returns the Charset for this charset if available or throws an exception otherwise. - */ - public abstract Charset nioCharset(); -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpCharsetRange.java b/akka-http-core/src/main/java/akka/http/javadsl/model/HttpCharsetRange.java deleted file mode 100644 index c21713bc12..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpCharsetRange.java +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -/** - * Represents an Http charset range. This can either be `*` which matches all charsets or a specific - * charset. {@link HttpCharsetRanges} contains static constructors for HttpCharsetRanges. - * - * @see HttpCharsetRanges for convenience access to often used values. - */ -public abstract class HttpCharsetRange { - - /** - * The qValue for this range. - */ - public abstract float qValue(); - - /** - * Returns if the given charset matches this range. - */ - public abstract boolean matches(HttpCharset charset); - - /** - * Returns a copy of this range with the given qValue. - */ - public abstract HttpCharsetRange withQValue(float qValue); -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpCharsetRanges.java b/akka-http-core/src/main/java/akka/http/javadsl/model/HttpCharsetRanges.java deleted file mode 100644 index d8b9dfe647..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpCharsetRanges.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -/** - * Contains constructors to create a HttpCharsetRange. - */ -public final class HttpCharsetRanges { - private HttpCharsetRanges() {} - - /** - * A constant representing the range that matches all charsets. - */ - public static final HttpCharsetRange ALL = akka.http.scaladsl.model.HttpCharsetRange.$times$.MODULE$; -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpCharsets.java b/akka-http-core/src/main/java/akka/http/javadsl/model/HttpCharsets.java deleted file mode 100644 index c532ae78d9..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpCharsets.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -import akka.http.impl.util.Util; -import akka.http.scaladsl.model.HttpCharsets$; - -import java.util.Optional; - -/** - * Contains a set of predefined charsets. - */ -public final class HttpCharsets { - private HttpCharsets() {} - - public static final HttpCharset US_ASCII = akka.http.scaladsl.model.HttpCharsets.US$minusASCII(); - public static final HttpCharset ISO_8859_1 = akka.http.scaladsl.model.HttpCharsets.ISO$minus8859$minus1(); - public static final HttpCharset UTF_8 = akka.http.scaladsl.model.HttpCharsets.UTF$minus8(); - public static final HttpCharset UTF_16 = akka.http.scaladsl.model.HttpCharsets.UTF$minus16(); - public static final HttpCharset UTF_16BE = akka.http.scaladsl.model.HttpCharsets.UTF$minus16BE(); - public static final HttpCharset UTF_16LE = akka.http.scaladsl.model.HttpCharsets.UTF$minus16LE(); - - /** - * Create and return a custom charset. - */ - public static HttpCharset custom(String value, String... aliases) { - return akka.http.scaladsl.model.HttpCharset.custom(value, Util.convertArray(aliases)); - } - - /** - * Returns Some(charset) if the charset with the given name was found and None otherwise. - */ - public static Optional lookup(String name) { - return Util.lookupInRegistry(HttpCharsets$.MODULE$, name); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpEntities.java b/akka-http-core/src/main/java/akka/http/javadsl/model/HttpEntities.java deleted file mode 100644 index 9a236faad5..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpEntities.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -import java.io.File; -import java.nio.file.Path; - -import akka.http.impl.util.JavaAccessors; -import akka.http.scaladsl.model.HttpEntity$; -import akka.util.ByteString; -import akka.stream.javadsl.Source; - -/** Constructors for HttpEntity instances */ -public final class HttpEntities { - private HttpEntities() {} - - public static final HttpEntity.Strict EMPTY = HttpEntity$.MODULE$.Empty(); - - public static HttpEntity.Strict create(String string) { - return HttpEntity$.MODULE$.apply(string); - } - - public static HttpEntity.Strict create(byte[] bytes) { - return HttpEntity$.MODULE$.apply(bytes); - } - - public static HttpEntity.Strict create(ByteString bytes) { - return HttpEntity$.MODULE$.apply(bytes); - } - - public static HttpEntity.Strict create(ContentType.NonBinary contentType, String string) { - return HttpEntity$.MODULE$.apply((akka.http.scaladsl.model.ContentType.NonBinary) contentType, string); - } - - public static HttpEntity.Strict create(ContentType contentType, byte[] bytes) { - return HttpEntity$.MODULE$.apply((akka.http.scaladsl.model.ContentType) contentType, bytes); - } - - public static HttpEntity.Strict create(ContentType contentType, ByteString bytes) { - return HttpEntity$.MODULE$.apply((akka.http.scaladsl.model.ContentType) contentType, bytes); - } - - /** - * @deprecated Will be removed in Akka 3.x, use {@link #create(ContentType, Path)} instead. - */ - @Deprecated - public static UniversalEntity create(ContentType contentType, File file) { - return JavaAccessors.HttpEntity(contentType, file); - } - - public static UniversalEntity create(ContentType contentType, Path file) { - return JavaAccessors.HttpEntity(contentType, file); - } - - /** - * @deprecated Will be removed in Akka 3.x, use {@link #create(ContentType, Path, int)} instead. - */ - @Deprecated - public static UniversalEntity create(ContentType contentType, File file, int chunkSize) { - return HttpEntity$.MODULE$.apply((akka.http.scaladsl.model.ContentType) contentType, file, chunkSize); - } - - public static UniversalEntity create(ContentType contentType, Path file, int chunkSize) { - return HttpEntity$.MODULE$.fromPath((akka.http.scaladsl.model.ContentType) contentType, file, chunkSize); - } - - public static HttpEntity.Default create(ContentType contentType, long contentLength, Source data) { - return new akka.http.scaladsl.model.HttpEntity.Default((akka.http.scaladsl.model.ContentType) contentType, contentLength, toScala(data)); - } - - public static HttpEntity.Chunked create(ContentType contentType, Source data) { - return akka.http.scaladsl.model.HttpEntity.Chunked$.MODULE$.fromData((akka.http.scaladsl.model.ContentType) contentType, toScala(data)); - } - - public static HttpEntity.CloseDelimited createCloseDelimited(ContentType contentType, Source data) { - return new akka.http.scaladsl.model.HttpEntity.CloseDelimited((akka.http.scaladsl.model.ContentType) contentType, toScala(data)); - } - - public static HttpEntity.IndefiniteLength createIndefiniteLength(ContentType contentType, Source data) { - return new akka.http.scaladsl.model.HttpEntity.IndefiniteLength((akka.http.scaladsl.model.ContentType) contentType, toScala(data)); - } - - public static HttpEntity.Chunked createChunked(ContentType contentType, Source data) { - return akka.http.scaladsl.model.HttpEntity.Chunked$.MODULE$.fromData( - (akka.http.scaladsl.model.ContentType) contentType, - toScala(data)); - } - - private static akka.stream.scaladsl.Source toScala(Source javaSource) { - return (akka.stream.scaladsl.Source)javaSource.asScala(); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpEntity.java b/akka-http-core/src/main/java/akka/http/javadsl/model/HttpEntity.java deleted file mode 100644 index 0582d29b74..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpEntity.java +++ /dev/null @@ -1,270 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -import akka.Done; -import akka.http.impl.util.Util; -import akka.stream.Materializer; -import akka.stream.javadsl.Source; -import akka.util.ByteString; -import scala.concurrent.Future; - -import java.util.OptionalLong; -import java.util.concurrent.CompletionStage; - -/** - * Represents the entity of an Http message. An entity consists of the content-type of the data - * and the actual data itself. Some subtypes of HttpEntity also define the content-length of the - * data. - * - * An HttpEntity can be of several kinds: - * - * - HttpEntity.Empty: the statically known empty entity - * - HttpEntity.Strict: an entity containing already evaluated ByteString data - * - HttpEntity.Default: the default entity which has a known length and which contains - * a stream of ByteStrings. - * - HttpEntity.Chunked: represents an entity that is delivered using `Transfer-Encoding: chunked` - * - HttpEntity.CloseDelimited: an entity which doesn't have a fixed length but which is delimited by - * closing the connection. - * - HttpEntity.IndefiniteLength: an entity which doesn't have a fixed length which can be used to construct BodyParts - * with indefinite length - * - * Marker-interfaces denote which subclasses can be used in which context: - * - RequestEntity: an entity type that can be used in an HttpRequest - * - ResponseEntity: an entity type that can be used in an HttpResponse - * - BodyPartEntity: an entity type that can be used in a BodyPart - * - UniversalEntity: an entity type that can be used in every context - * - * Use the static constructors in HttpEntities to construct instances. - * - * @see HttpEntities for javadsl convenience methods. - */ -public interface HttpEntity { - /** - * Returns the content-type of this entity - */ - ContentType getContentType(); - - /** - * The empty entity. - * - * @deprecated Will be removed in Akka 3.x, use {@link HttpEntities#EMPTY} instead. - */ - @Deprecated - // FIXME: Remove in Akka 3.0 - HttpEntity.Strict EMPTY = HttpEntities.EMPTY; - - /** - * Returns if this entity is known to be empty. Open-ended entity types like - * HttpEntityChunked and HttpCloseDelimited will always return false here. - */ - boolean isKnownEmpty(); - - /** - * Returns if this entity is a subtype of HttpEntityChunked. - */ - boolean isChunked(); - - /** - * Returns if this entity is a subtype of HttpEntityDefault. - */ - boolean isDefault(); - - /** - * Returns if this entity is a subtype of HttpEntityCloseDelimited. - */ - boolean isCloseDelimited(); - - /** - * Returns if this entity is a subtype of HttpEntityIndefiniteLength. - */ - boolean isIndefiniteLength(); - - /** - * Returns Some(contentLength) if the length is defined and none otherwise. - */ - OptionalLong getContentLengthOption(); - - /** - * Returns a stream of data bytes this entity consists of. - */ - Source getDataBytes(); - - /** - * Apply the given size limit to this entity by returning a new entity instance which automatically verifies that the - * data stream encapsulated by this instance produces at most `maxBytes` data bytes. In case this verification fails - * the respective stream will be terminated with an `EntityStreamException` either directly at materialization - * time (if the Content-Length is known) or whenever more data bytes than allowed have been read. - * - * When called on `Strict` entities the method will return the entity itself if the length is within the bound, - * otherwise a `Default` entity with a single element data stream. This allows for potential refinement of the - * entity size limit at a later point (before materialization of the data stream). - * - * By default all message entities produced by the HTTP layer automatically carry the limit that is defined in the - * application's `max-content-length` config setting. If the entity is transformed in a way that changes the - * Content-Length and then another limit is applied then this new limit will be evaluated against the new - * Content-Length. If the entity is transformed in a way that changes the Content-Length and no new limit is applied - * then the previous limit will be applied against the previous Content-Length. - * - * Note that the size limit applied via this method will only have any effect if the `Source` instance contained - * in this entity has been appropriately modified via the `HttpEntity.limitable` method. For all entities created - * by the HTTP layer itself this is always the case, but if you create entities yourself and would like them to - * properly respect limits defined via this method you need to make sure to apply `HttpEntity.limitable` yourself. - */ - HttpEntity withSizeLimit(long maxBytes); - - /** - * Lift the size limit from this entity by returning a new entity instance which skips the size verification. - * - * By default all message entities produced by the HTTP layer automatically carry the limit that is defined in the - * application's `max-content-length` config setting. It is recommended to always keep an upper limit on accepted - * entities to avoid potential attackers flooding you with too large requests/responses, so use this method with caution. - * - * Note that the size limit applied via this method will only have any effect if the `Source` instance contained - * in this entity has been appropriately modified via the `HttpEntity.limitable` method. For all entities created - * by the HTTP layer itself this is always the case, but if you create entities yourself and would like them to - * properly respect limits defined via this method you need to make sure to apply `HttpEntity.limitable` yourself. - * - * See [[withSizeLimit]] for more details. - */ - HttpEntity withoutSizeLimit(); - - /** - * Returns a future of a strict entity that contains the same data as this entity - * which is only completed when the complete entity has been collected. As the - * duration of receiving the complete entity cannot be predicted, a timeout needs to - * be specified to guard the process against running and keeping resources infinitely. - * - * Use getDataBytes and stream processing instead if the expected data is big or - * is likely to take a long time. - */ - CompletionStage toStrict(long timeoutMillis, Materializer materializer); - - /** - * Discards the entities data bytes by running the {@code dataBytes} Source contained in this entity. - * - * Note: It is crucial that entities are either discarded, or consumed by running the underlying [[Source]] - * as otherwise the lack of consuming of the data will trigger back-pressure to the underlying TCP connection - * (as designed), however possibly leading to an idle-timeout that will close the connection, instead of - * just having ignored the data. - * - * Warning: It is not allowed to discard and/or consume the {@code dataBytes} more than once - * as the stream is directly attached to the "live" incoming data source from the underlying TCP connection. - * Allowing it to be consumable twice would require buffering the incoming data, thus defeating the purpose - * of its streaming nature. If the dataBytes source is materialized a second time, it will fail with an - * "stream can cannot be materialized more than once" exception. - * - * In future versions, more automatic ways to warn or resolve these situations may be introduced, see issue #18716. - */ - HttpMessage.DiscardedEntity discardBytes(Materializer materializer); - - - /** - * Represents the currently being-drained HTTP Entity which triggers completion of the contained - * Future once the entity has been drained for the given HttpMessage completely. - */ - interface DiscardedEntity { - /** - * This future completes successfully once the underlying entity stream has been - * successfully drained (and fails otherwise). - */ - Future future(); - - /** - * This future completes successfully once the underlying entity stream has been - * successfully drained (and fails otherwise). - */ - CompletionStage completionStage(); - } - - /** - * The entity type which consists of a predefined fixed ByteString of data. - */ - interface Strict extends UniversalEntity { - ByteString getData(); - } - - /** - * The default entity type which has a predetermined length and a stream of data bytes. - */ - interface Default extends UniversalEntity { - long getContentLength(); - } - - /** - * Represents an entity without a predetermined content-length. Its length is implicitly - * determined by closing the underlying connection. Therefore, this entity type is only - * available for Http responses. - */ - interface CloseDelimited extends ResponseEntity { - } - - /** - * Represents an entity transferred using `Transfer-Encoding: chunked`. It consists of a - * stream of {@link ChunkStreamPart}. - */ - interface Chunked extends RequestEntity, ResponseEntity { - Source getChunks(); - } - - /** - * Represents an entity without a predetermined content-length to use in a BodyParts. - */ - interface IndefiniteLength extends BodyPartEntity { - } - - /** - * A part of a stream of incoming data for `Transfer-Encoding: chunked` messages. - */ - abstract class ChunkStreamPart { - /** - * Returns the byte data of this chunk. Will be non-empty for every regular - * chunk. Will be empty for the last chunk. - */ - public abstract ByteString data(); - - /** - * Returns extensions data for this chunk. - */ - public abstract String extension(); - - /** - * Returns if this is the last chunk - */ - public abstract boolean isLastChunk(); - - /** - * If this is the last chunk, this will return an Iterable of the trailer headers. Otherwise, - * it will be empty. - */ - public abstract Iterable getTrailerHeaders(); - - /** - * Creates a chunk from data and extension. - */ - public static ChunkStreamPart create(ByteString data, String extension) { - return new akka.http.scaladsl.model.HttpEntity.Chunk(data, extension); - } - - /** - * Creates a chunk from data with an empty extension. - */ - public static ChunkStreamPart create(ByteString data) { - return create(data, ""); - } - - /** - * The default last ChunkStreamPart that has no extension and no trailer headers. - */ - public static final ChunkStreamPart LAST = akka.http.scaladsl.model.HttpEntity.LastChunk$.MODULE$; - - /** - * Creates a last chunk with extension and headers. - */ - public static ChunkStreamPart createLast(String extension, Iterable trailerHeaders){ - return new akka.http.scaladsl.model.HttpEntity.LastChunk(extension, Util.convertIterable(trailerHeaders)); - } - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpHeader.java b/akka-http-core/src/main/java/akka/http/javadsl/model/HttpHeader.java deleted file mode 100644 index 94dbd65e03..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpHeader.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -/** - * The base type representing Http headers. All actual header values will be instances - * of one of the subtypes defined in the `headers` packages. Unknown headers will be subtypes - * of {@link akka.http.javadsl.model.headers.RawHeader}. - */ -public abstract class HttpHeader { - /** - * Returns the name of the header. - */ - public abstract String name(); - - /** - * Returns the String representation of the value of the header. - */ - public abstract String value(); - - /** - * Returns the lower-cased name of the header. - */ - public abstract String lowercaseName(); - - /** - * Returns true iff nameInLowerCase.equals(lowercaseName()). - */ - public abstract boolean is(String nameInLowerCase); - - /** - * Returns !is(nameInLowerCase). - */ - public abstract boolean isNot(String nameInLowerCase); - - /** - * Returns true iff the header is to be rendered in requests. - */ - public abstract boolean renderInRequests(); - - /** - * Returns true iff the header is to be rendered in responses. - */ - public abstract boolean renderInResponses(); -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpMessage.java b/akka-http-core/src/main/java/akka/http/javadsl/model/HttpMessage.java deleted file mode 100644 index c78808b99e..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpMessage.java +++ /dev/null @@ -1,161 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -import akka.stream.Materializer; -import akka.http.javadsl.model.headers.HttpCredentials; -import akka.util.ByteString; - -import java.io.File; -import java.nio.file.Path; -import java.util.Optional; - -/** - * The base type for an Http message (request or response). - * - * INTERNAL API: this trait will be changed in binary-incompatible ways for classes that are derived from it! - * Do not implement this interface outside the Akka code base! - * - * Binary compatibility is only maintained for callers of this trait’s interface. - */ -public interface HttpMessage { - /** - * Is this instance a request. - */ - boolean isRequest(); - - /** - * Is this instance a response. - */ - boolean isResponse(); - - /** - * The protocol of this message. - */ - HttpProtocol protocol(); - - /** - * An iterable containing the headers of this message. - */ - Iterable getHeaders(); - - /** - * Try to find the first header with the given name (case-insensitive) and return - * Some(header), otherwise this method returns None. - */ - Optional getHeader(String headerName); - - /** - * Try to find the first header of the given class and return - * Some(header), otherwise this method returns None. - */ - Optional getHeader(Class headerClass); - - /** - * The entity of this message. - */ - ResponseEntity entity(); - - /** - * Discards the entities data bytes by running the {@code dataBytes} Source contained by the {@code entity} - * of this HTTP message. - * - * Note: It is crucial that entities are either discarded, or consumed by running the underlying [[Source]] - * as otherwise the lack of consuming of the data will trigger back-pressure to the underlying TCP connection - * (as designed), however possibly leading to an idle-timeout that will close the connection, instead of - * just having ignored the data. - * - * Warning: It is not allowed to discard and/or consume the {@code entity.dataBytes} more than once - * as the stream is directly attached to the "live" incoming data source from the underlying TCP connection. - * Allowing it to be consumable twice would require buffering the incoming data, thus defeating the purpose - * of its streaming nature. If the dataBytes source is materialized a second time, it will fail with an - * "stream can cannot be materialized more than once" exception. - * - * In future versions, more automatic ways to warn or resolve these situations may be introduced, see issue #18716. - */ - DiscardedEntity discardEntityBytes(Materializer materializer); - - /** - * Represents the currently being-drained HTTP Entity which triggers completion of the contained - * Future once the entity has been drained for the given HttpMessage completely. - */ - interface DiscardedEntity extends HttpEntity.DiscardedEntity { - } - - interface MessageTransformations { - /** - * Returns a copy of this message with a new protocol. - */ - Self withProtocol(HttpProtocol protocol); - - /** - * Returns a copy of this message with the given header added to the list of headers. - */ - Self addHeader(HttpHeader header); - - /** - * Returns a copy of this message with the given headers added to the list of headers. - */ - Self addHeaders(Iterable headers); - - /** - * Returns a copy of this message with the given http credential header added to the list of headers. - */ - Self addCredentials(HttpCredentials credentials); - - /** - * Returns a copy of this message with all headers of the given name (case-insensitively) removed. - */ - Self removeHeader(String headerName); - - /** - * Returns a copy of this message with a new entity. - */ - Self withEntity(String string); - - /** - * Returns a copy of Self message with a new entity. - */ - Self withEntity(byte[] bytes); - - /** - * Returns a copy of Self message with a new entity. - */ - Self withEntity(ByteString bytes); - - /** - * Returns a copy of Self message with a new entity. - */ - Self withEntity(ContentType.NonBinary type, String string); - - /** - * Returns a copy of Self message with a new entity. - */ - Self withEntity(ContentType type, byte[] bytes); - - /** - * Returns a copy of Self message with a new entity. - */ - Self withEntity(ContentType type, ByteString bytes); - - /** - * Returns a copy of Self message with a new entity. - * - * @deprecated Will be removed in Akka 3.x, use {@link #withEntity(ContentType, Path)} instead. - */ - @Deprecated - Self withEntity(ContentType type, File file); - - /** - * Returns a copy of Self message with a new entity. - */ - Self withEntity(ContentType type, Path file); - - /** - * Returns a copy of Self message with a new entity. - */ - Self withEntity(RequestEntity entity); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpMethod.java b/akka-http-core/src/main/java/akka/http/javadsl/model/HttpMethod.java deleted file mode 100644 index 69f95a050f..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpMethod.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -/** - * Represents an HTTP request method. See {@link HttpMethods} for a set of predefined methods - * and static constructors to create custom ones. - * - * @see HttpMethods for convenience access to often used values. - */ -public abstract class HttpMethod { - - /** - * Returns the name of the method, always equal to [[value]]. - */ - public final String name() { - return value(); - } - /** - * Returns the name of the method. - */ - public abstract String value(); - - /** - * Returns if this method is "safe" as defined in - * http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-26#section-4.2.1 - */ - public abstract boolean isSafe(); - - /** - * Returns if this method is "idempotent" as defined in - * http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-26#section-4.2.2 - */ - public abstract boolean isIdempotent(); - - /** - * Returns if requests with this method may contain an entity. - */ - public abstract boolean isEntityAccepted(); - - /** - * Returns the entity acceptance level for this method. - * @deprecated Use {@link #getRequestEntityAcceptance} instead, which returns {@link akka.http.javadsl.model.RequestEntityAcceptance}. - */ - @Deprecated - public abstract akka.http.scaladsl.model.RequestEntityAcceptance requestEntityAcceptance(); - - /** - * Java API: Returns the entity acceptance level for this method. - */ - // TODO: Rename it to requestEntityAcceptance() in Akka 3.0 - public abstract akka.http.javadsl.model.RequestEntityAcceptance getRequestEntityAcceptance(); -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpMethods.java b/akka-http-core/src/main/java/akka/http/javadsl/model/HttpMethods.java deleted file mode 100644 index 0085398a83..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpMethods.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -import akka.http.impl.util.Util; -import akka.http.scaladsl.model.HttpMethods$; - -import java.util.Optional; - -/** - * Contains static constants for predefined method types. - */ -public final class HttpMethods { - private HttpMethods() {} - - public static final HttpMethod CONNECT = akka.http.scaladsl.model.HttpMethods.CONNECT(); - public static final HttpMethod DELETE = akka.http.scaladsl.model.HttpMethods.DELETE(); - public static final HttpMethod GET = akka.http.scaladsl.model.HttpMethods.GET(); - public static final HttpMethod HEAD = akka.http.scaladsl.model.HttpMethods.HEAD(); - public static final HttpMethod OPTIONS = akka.http.scaladsl.model.HttpMethods.OPTIONS(); - public static final HttpMethod PATCH = akka.http.scaladsl.model.HttpMethods.PATCH(); - public static final HttpMethod POST = akka.http.scaladsl.model.HttpMethods.POST(); - public static final HttpMethod PUT = akka.http.scaladsl.model.HttpMethods.PUT(); - public static final HttpMethod TRACE = akka.http.scaladsl.model.HttpMethods.TRACE(); - - /** - * Create a custom method type. - * @deprecated Use {@link #createCustom} instead. - */ - @Deprecated - public static HttpMethod custom(String value, boolean safe, boolean idempotent, akka.http.scaladsl.model.RequestEntityAcceptance requestEntityAcceptance) { - return akka.http.scaladsl.model.HttpMethod.custom(value, safe, idempotent, requestEntityAcceptance); - } - - /** - * Create a custom method type. - */ - // TODO: Rename it to custom() in Akka 3.0 - public static HttpMethod createCustom(String value, boolean safe, boolean idempotent, akka.http.javadsl.model.RequestEntityAcceptance requestEntityAcceptance) { - //This cast is safe as implementation of RequestEntityAcceptance only exists in Scala - akka.http.scaladsl.model.RequestEntityAcceptance scalaRequestEntityAcceptance - = (akka.http.scaladsl.model.RequestEntityAcceptance) requestEntityAcceptance; - return akka.http.scaladsl.model.HttpMethod.custom(value, safe, idempotent, scalaRequestEntityAcceptance); - } - - /** - * Looks up a predefined HTTP method with the given name. - */ - public static Optional lookup(String name) { - return Util.lookupInRegistry(HttpMethods$.MODULE$, name); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpProtocol.java b/akka-http-core/src/main/java/akka/http/javadsl/model/HttpProtocol.java deleted file mode 100644 index cc69fa4ba1..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpProtocol.java +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -/** - * Represents an Http protocol (currently only HTTP/1.0 or HTTP/1.1). See {@link HttpProtocols} - * for the predefined constants for the supported protocols. - * - * @see HttpProtocols for convenience access to often used values. - */ -public abstract class HttpProtocol { - /** - * Returns the String representation of this protocol. - */ - public abstract String value(); -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpProtocols.java b/akka-http-core/src/main/java/akka/http/javadsl/model/HttpProtocols.java deleted file mode 100644 index 6136b17981..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpProtocols.java +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -/** - * Contains constants of the supported Http protocols. - */ -public final class HttpProtocols { - private HttpProtocols() {} - - public final static HttpProtocol HTTP_1_0 = akka.http.scaladsl.model.HttpProtocols.HTTP$div1$u002E0(); - public final static HttpProtocol HTTP_1_1 = akka.http.scaladsl.model.HttpProtocols.HTTP$div1$u002E1(); -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpRequest.java b/akka-http-core/src/main/java/akka/http/javadsl/model/HttpRequest.java deleted file mode 100644 index b22acb29ca..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpRequest.java +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -import akka.Done; -import akka.http.impl.util.JavaAccessors; -import akka.stream.Materializer; -import akka.stream.javadsl.Sink; - -import java.util.concurrent.CompletionStage; - -/** - * Represents an Http request. - */ -public abstract class HttpRequest implements HttpMessage, HttpMessage.MessageTransformations { - /** - * Returns the Http method of this request. - */ - public abstract HttpMethod method(); - - /** - * Returns the Uri of this request. - */ - public abstract Uri getUri(); - - /** - * Returns the entity of this request. - */ - public abstract RequestEntity entity(); - - /** - * Returns a copy of this instance with a new method. - */ - public abstract HttpRequest withMethod(HttpMethod method); - - /** - * Returns a copy of this instance with a new Uri. - */ - public abstract HttpRequest withUri(Uri relativeUri); - - /** - * Returns a copy of this instance with a new Uri. - */ - public abstract HttpRequest withUri(String path); - - /** - * Returns a copy of this instance with a new entity. - */ - public abstract HttpRequest withEntity(RequestEntity entity); - - /** - * Returns a default request to be modified using the `withX` methods. - */ - public static HttpRequest create() { - return JavaAccessors.HttpRequest(); - } - - /** - * Returns a default request to the specified URI to be modified using the `withX` methods. - */ - public static HttpRequest create(String uri) { - return JavaAccessors.HttpRequest(uri); - } - - /** - * A default GET request to be modified using the `withX` methods. - */ - public static HttpRequest GET(String uri) { - return create(uri); - } - - /** - * A default POST request to be modified using the `withX` methods. - */ - public static HttpRequest POST(String uri) { - return create(uri).withMethod(HttpMethods.POST); - } - - /** - * A default PUT request to be modified using the `withX` methods. - */ - public static HttpRequest PUT(String uri) { - return create(uri).withMethod(HttpMethods.PUT); - } - - /** - * A default DELETE request to be modified using the `withX` methods. - */ - public static HttpRequest DELETE(String uri) { - return create(uri).withMethod(HttpMethods.DELETE); - } - - /** - * A default HEAD request to be modified using the `withX` methods. - */ - public static HttpRequest HEAD(String uri) { - return create(uri).withMethod(HttpMethods.HEAD); - } - - /** - * A default PATCH request to be modified using the `withX` methods. - */ - public static HttpRequest PATCH(String uri) { - return create(uri).withMethod(HttpMethods.PATCH); - } - - /** - * A default OPTIONS request to be modified using the `withX` methods. - */ - public static HttpRequest OPTIONS(String uri) { - return create(uri).withMethod(HttpMethods.OPTIONS); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpResponse.java b/akka-http-core/src/main/java/akka/http/javadsl/model/HttpResponse.java deleted file mode 100644 index 11d5dd45fb..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpResponse.java +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -import akka.Done; -import akka.http.impl.util.JavaAccessors; -import akka.stream.Materializer; -import akka.stream.javadsl.Sink; - -import java.util.concurrent.CompletionStage; - -/** - * Represents an Http response. - */ -public abstract class HttpResponse implements HttpMessage, HttpMessage.MessageTransformations { - /** - * Returns the status-code of this response. - */ - public abstract StatusCode status(); - - /** - * Returns the entity of this response. - */ - public abstract ResponseEntity entity(); - - /** - * Returns a copy of this instance with a new status-code. - */ - public abstract HttpResponse withStatus(StatusCode statusCode); - - /** - * Returns a copy of this instance with a new status-code. - */ - public abstract HttpResponse withStatus(int statusCode); - - /** - * Returns a copy of this instance with a new entity. - */ - public abstract HttpResponse withEntity(ResponseEntity entity); - - /** - * Returns a default response to be changed using the `withX` methods. - */ - public static HttpResponse create() { - return JavaAccessors.HttpResponse(); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/MediaRange.java b/akka-http-core/src/main/java/akka/http/javadsl/model/MediaRange.java deleted file mode 100644 index 784902a2fa..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/MediaRange.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -import java.util.Map; - -/** - * Represents an Http media-range. A media-range either matches a single media-type - * or it matches all media-types of a given main-type. Each range can specify a qValue - * or other parameters. - */ -public abstract class MediaRange { - /** - * Returns the main-type this media-range matches. - */ - public abstract String mainType(); - - /** - * Returns the qValue of this media-range. - */ - public abstract float qValue(); - - /** - * Checks if this range matches a given media-type. - */ - public abstract boolean matches(MediaType mediaType); - - /** - * Returns a Map of the parameters of this media-range. - */ - public abstract Map getParams(); - - /** - * Returns a copy of this instance with a changed qValue. - */ - public abstract MediaRange withQValue(float qValue); -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/MediaRanges.java b/akka-http-core/src/main/java/akka/http/javadsl/model/MediaRanges.java deleted file mode 100644 index 17d180f8a9..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/MediaRanges.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -import akka.http.impl.util.Util; - -import java.util.Map; - -/** - * Contains a set of predefined media-ranges and static methods to create custom ones. - */ -public final class MediaRanges { - private MediaRanges() {} - - public static final MediaRange ALL = akka.http.scaladsl.model.MediaRanges.$times$div$times(); - public static final MediaRange ALL_APPLICATION = akka.http.scaladsl.model.MediaRanges.application$div$times(); - public static final MediaRange ALL_AUDIO = akka.http.scaladsl.model.MediaRanges.audio$div$times(); - public static final MediaRange ALL_IMAGE = akka.http.scaladsl.model.MediaRanges.image$div$times(); - public static final MediaRange ALL_MESSAGE = akka.http.scaladsl.model.MediaRanges.message$div$times(); - public static final MediaRange ALL_MULTIPART = akka.http.scaladsl.model.MediaRanges.multipart$div$times(); - public static final MediaRange ALL_TEXT = akka.http.scaladsl.model.MediaRanges.text$div$times(); - public static final MediaRange ALL_VIDEO = akka.http.scaladsl.model.MediaRanges.video$div$times(); - - /** - * Creates a custom universal media-range for a given main-type. - */ - public static MediaRange create(MediaType mediaType) { - return akka.http.scaladsl.model.MediaRange.apply((akka.http.scaladsl.model.MediaType) mediaType); - } - - /** - * Creates a custom universal media-range for a given main-type and a Map of parameters. - */ - public static MediaRange custom(String mainType, Map parameters) { - return akka.http.scaladsl.model.MediaRange.custom(mainType, Util.convertMapToScala(parameters), 1.0f); - } - - /** - * Creates a custom universal media-range for a given main-type and qValue. - */ - public static MediaRange create(MediaType mediaType, float qValue) { - return akka.http.scaladsl.model.MediaRange.apply((akka.http.scaladsl.model.MediaType) mediaType, qValue); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/MediaTypes.java b/akka-http-core/src/main/java/akka/http/javadsl/model/MediaTypes.java deleted file mode 100644 index ba6a91545c..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/MediaTypes.java +++ /dev/null @@ -1,369 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -import akka.http.impl.util.Util; -import akka.http.scaladsl.model.MediaTypes$; -import java.util.Optional; - -/** - * Contains the set of predefined media-types. - */ -public final class MediaTypes { - private MediaTypes() { } - - public static final MediaType.WithOpenCharset APPLICATION_ATOM_XML = akka.http.scaladsl.model.MediaTypes.application$divatom$plusxml(); - public static final MediaType.WithOpenCharset APPLICATION_BASE64 = akka.http.scaladsl.model.MediaTypes.application$divbase64(); - public static final MediaType.Binary APPLICATION_EXCEL = akka.http.scaladsl.model.MediaTypes.application$divexcel(); - public static final MediaType.Binary APPLICATION_FONT_WOFF = akka.http.scaladsl.model.MediaTypes.application$divfont$minuswoff(); - public static final MediaType.Binary APPLICATION_GNUTAR = akka.http.scaladsl.model.MediaTypes.application$divgnutar(); - public static final MediaType.Binary APPLICATION_JAVA_ARCHIVE = akka.http.scaladsl.model.MediaTypes.application$divjava$minusarchive(); - public static final MediaType.WithOpenCharset APPLICATION_JAVASCRIPT = akka.http.scaladsl.model.MediaTypes.application$divjavascript(); - public static final MediaType.WithFixedCharset APPLICATION_JSON = akka.http.scaladsl.model.MediaTypes.application$divjson(); - public static final MediaType.WithFixedCharset APPLICATION_JSON_PATCH_JSON = akka.http.scaladsl.model.MediaTypes.application$divjson$minuspatch$plusjson(); - public static final MediaType.Binary APPLICATION_LHA = akka.http.scaladsl.model.MediaTypes.application$divlha(); - public static final MediaType.Binary APPLICATION_LZX = akka.http.scaladsl.model.MediaTypes.application$divlzx(); - public static final MediaType.Binary APPLICATION_MSPOWERPOINT = akka.http.scaladsl.model.MediaTypes.application$divmspowerpoint(); - public static final MediaType.Binary APPLICATION_MSWORD = akka.http.scaladsl.model.MediaTypes.application$divmsword(); - public static final MediaType.Binary APPLICATION_OCTET_STREAM = akka.http.scaladsl.model.MediaTypes.application$divoctet$minusstream(); - public static final MediaType.Binary APPLICATION_PDF = akka.http.scaladsl.model.MediaTypes.application$divpdf(); - public static final MediaType.Binary APPLICATION_POSTSCRIPT = akka.http.scaladsl.model.MediaTypes.application$divpostscript(); - public static final MediaType.WithOpenCharset APPLICATION_RSS_XML = akka.http.scaladsl.model.MediaTypes.application$divrss$plusxml(); - public static final MediaType.WithOpenCharset APPLICATION_SOAP_XML = akka.http.scaladsl.model.MediaTypes.application$divsoap$plusxml(); - public static final MediaType.WithFixedCharset APPLICATION_VND_API_JSON = akka.http.scaladsl.model.MediaTypes.application$divvnd$u002Eapi$plusjson(); - public static final MediaType.WithOpenCharset APPLICATION_VND_GOOGLE_EARTH_KML_XML = akka.http.scaladsl.model.MediaTypes.application$divvnd$u002Egoogle$minusearth$u002Ekml$plusxml(); - public static final MediaType.Binary APPLICATION_VND_GOOGLE_EARTH_KMZ = akka.http.scaladsl.model.MediaTypes.application$divvnd$u002Egoogle$minusearth$u002Ekmz(); - public static final MediaType.Binary APPLICATION_VND_MS_FONTOBJECT = akka.http.scaladsl.model.MediaTypes.application$divvnd$u002Ems$minusfontobject(); - public static final MediaType.Binary APPLICATION_VND_OASIS_OPENDOCUMENT_CHART = akka.http.scaladsl.model.MediaTypes.application$divvnd$u002Eoasis$u002Eopendocument$u002Echart(); - public static final MediaType.Binary APPLICATION_VND_OASIS_OPENDOCUMENT_DATABASE = akka.http.scaladsl.model.MediaTypes.application$divvnd$u002Eoasis$u002Eopendocument$u002Edatabase(); - public static final MediaType.Binary APPLICATION_VND_OASIS_OPENDOCUMENT_FORMULA = akka.http.scaladsl.model.MediaTypes.application$divvnd$u002Eoasis$u002Eopendocument$u002Eformula(); - public static final MediaType.Binary APPLICATION_VND_OASIS_OPENDOCUMENT_GRAPHICS = akka.http.scaladsl.model.MediaTypes.application$divvnd$u002Eoasis$u002Eopendocument$u002Egraphics(); - public static final MediaType.Binary APPLICATION_VND_OASIS_OPENDOCUMENT_IMAGE = akka.http.scaladsl.model.MediaTypes.application$divvnd$u002Eoasis$u002Eopendocument$u002Eimage(); - public static final MediaType.Binary APPLICATION_VND_OASIS_OPENDOCUMENT_PRESENTATION = akka.http.scaladsl.model.MediaTypes.application$divvnd$u002Eoasis$u002Eopendocument$u002Epresentation(); - public static final MediaType.Binary APPLICATION_VND_OASIS_OPENDOCUMENT_SPREADSHEET = akka.http.scaladsl.model.MediaTypes.application$divvnd$u002Eoasis$u002Eopendocument$u002Espreadsheet(); - public static final MediaType.Binary APPLICATION_VND_OASIS_OPENDOCUMENT_TEXT = akka.http.scaladsl.model.MediaTypes.application$divvnd$u002Eoasis$u002Eopendocument$u002Etext(); - public static final MediaType.Binary APPLICATION_VND_OASIS_OPENDOCUMENT_TEXT_MASTER = akka.http.scaladsl.model.MediaTypes.application$divvnd$u002Eoasis$u002Eopendocument$u002Etext$minusmaster(); - public static final MediaType.Binary APPLICATION_VND_OASIS_OPENDOCUMENT_TEXT_WEB = akka.http.scaladsl.model.MediaTypes.application$divvnd$u002Eoasis$u002Eopendocument$u002Etext$minusweb(); - public static final MediaType.Binary APPLICATION_VND_OPENXMLFORMATS_OFFICEDOCUMENT_PRESENTATIONML_PRESENTATION = akka.http.scaladsl.model.MediaTypes.application$divvnd$u002Eopenxmlformats$minusofficedocument$u002Epresentationml$u002Epresentation(); - public static final MediaType.Binary APPLICATION_VND_OPENXMLFORMATS_OFFICEDOCUMENT_PRESENTATIONML_SLIDE = akka.http.scaladsl.model.MediaTypes.application$divvnd$u002Eopenxmlformats$minusofficedocument$u002Epresentationml$u002Eslide(); - public static final MediaType.Binary APPLICATION_VND_OPENXMLFORMATS_OFFICEDOCUMENT_PRESENTATIONML_SLIDESHOW = akka.http.scaladsl.model.MediaTypes.application$divvnd$u002Eopenxmlformats$minusofficedocument$u002Epresentationml$u002Eslideshow(); - public static final MediaType.Binary APPLICATION_VND_OPENXMLFORMATS_OFFICEDOCUMENT_PRESENTATIONML_TEMPLATE = akka.http.scaladsl.model.MediaTypes.application$divvnd$u002Eopenxmlformats$minusofficedocument$u002Epresentationml$u002Etemplate(); - public static final MediaType.Binary APPLICATION_VND_OPENXMLFORMATS_OFFICEDOCUMENT_SPREADSHEETML_SHEET = akka.http.scaladsl.model.MediaTypes.application$divvnd$u002Eopenxmlformats$minusofficedocument$u002Espreadsheetml$u002Esheet(); - public static final MediaType.Binary APPLICATION_VND_OPENXMLFORMATS_OFFICEDOCUMENT_SPREADSHEETML_TEMPLATE = akka.http.scaladsl.model.MediaTypes.application$divvnd$u002Eopenxmlformats$minusofficedocument$u002Espreadsheetml$u002Etemplate(); - public static final MediaType.Binary APPLICATION_VND_OPENXMLFORMATS_OFFICEDOCUMENT_WORDPROCESSINGML_DOCUMENT = akka.http.scaladsl.model.MediaTypes.application$divvnd$u002Eopenxmlformats$minusofficedocument$u002Ewordprocessingml$u002Edocument(); - public static final MediaType.Binary APPLICATION_VND_OPENXMLFORMATS_OFFICEDOCUMENT_WORDPROCESSINGML_TEMPLATE = akka.http.scaladsl.model.MediaTypes.application$divvnd$u002Eopenxmlformats$minusofficedocument$u002Ewordprocessingml$u002Etemplate(); - public static final MediaType.Binary APPLICATION_X_7Z_COMPRESSED = akka.http.scaladsl.model.MediaTypes.application$divx$minus7z$minuscompressed(); - public static final MediaType.Binary APPLICATION_X_ACE_COMPRESSED = akka.http.scaladsl.model.MediaTypes.application$divx$minusace$minuscompressed(); - public static final MediaType.Binary APPLICATION_X_APPLE_DISKIMAGE = akka.http.scaladsl.model.MediaTypes.application$divx$minusapple$minusdiskimage(); - public static final MediaType.Binary APPLICATION_X_ARC_COMPRESSED = akka.http.scaladsl.model.MediaTypes.application$divx$minusarc$minuscompressed(); - public static final MediaType.Binary APPLICATION_X_BZIP = akka.http.scaladsl.model.MediaTypes.application$divx$minusbzip(); - public static final MediaType.Binary APPLICATION_X_BZIP2 = akka.http.scaladsl.model.MediaTypes.application$divx$minusbzip2(); - public static final MediaType.Binary APPLICATION_X_CHROME_EXTENSION = akka.http.scaladsl.model.MediaTypes.application$divx$minuschrome$minusextension(); - public static final MediaType.Binary APPLICATION_X_COMPRESS = akka.http.scaladsl.model.MediaTypes.application$divx$minuscompress(); - public static final MediaType.Binary APPLICATION_X_COMPRESSED = akka.http.scaladsl.model.MediaTypes.application$divx$minuscompressed(); - public static final MediaType.Binary APPLICATION_X_DEBIAN_PACKAGE = akka.http.scaladsl.model.MediaTypes.application$divx$minusdebian$minuspackage(); - public static final MediaType.Binary APPLICATION_X_DVI = akka.http.scaladsl.model.MediaTypes.application$divx$minusdvi(); - public static final MediaType.Binary APPLICATION_X_FONT_TRUETYPE = akka.http.scaladsl.model.MediaTypes.application$divx$minusfont$minustruetype(); - public static final MediaType.Binary APPLICATION_X_FONT_OPENTYPE = akka.http.scaladsl.model.MediaTypes.application$divx$minusfont$minusopentype(); - public static final MediaType.Binary APPLICATION_X_GTAR = akka.http.scaladsl.model.MediaTypes.application$divx$minusgtar(); - public static final MediaType.Binary APPLICATION_X_GZIP = akka.http.scaladsl.model.MediaTypes.application$divx$minusgzip(); - public static final MediaType.WithOpenCharset APPLICATION_X_LATEX = akka.http.scaladsl.model.MediaTypes.application$divx$minuslatex(); - public static final MediaType.Binary APPLICATION_X_RAR_COMPRESSED = akka.http.scaladsl.model.MediaTypes.application$divx$minusrar$minuscompressed(); - public static final MediaType.Binary APPLICATION_X_REDHAT_PACKAGE_MANAGER = akka.http.scaladsl.model.MediaTypes.application$divx$minusredhat$minuspackage$minusmanager(); - public static final MediaType.Binary APPLICATION_X_SHOCKWAVE_FLASH = akka.http.scaladsl.model.MediaTypes.application$divx$minusshockwave$minusflash(); - public static final MediaType.Binary APPLICATION_X_TAR = akka.http.scaladsl.model.MediaTypes.application$divx$minustar(); - public static final MediaType.Binary APPLICATION_X_TEX = akka.http.scaladsl.model.MediaTypes.application$divx$minustex(); - public static final MediaType.Binary APPLICATION_X_TEXINFO = akka.http.scaladsl.model.MediaTypes.application$divx$minustexinfo(); - public static final MediaType.WithOpenCharset APPLICATION_X_VRML = akka.http.scaladsl.model.MediaTypes.application$divx$minusvrml(); - public static final MediaType.WithOpenCharset APPLICATION_X_WWW_FORM_URLENCODED = akka.http.scaladsl.model.MediaTypes.application$divx$minuswww$minusform$minusurlencoded(); - public static final MediaType.Binary APPLICATION_X_X509_CA_CERT = akka.http.scaladsl.model.MediaTypes.application$divx$minusx509$minusca$minuscert(); - public static final MediaType.Binary APPLICATION_X_XPINSTALL = akka.http.scaladsl.model.MediaTypes.application$divx$minusxpinstall(); - public static final MediaType.WithOpenCharset APPLICATION_XHTML_XML = akka.http.scaladsl.model.MediaTypes.application$divxhtml$plusxml(); - public static final MediaType.WithOpenCharset APPLICATION_XML_DTD = akka.http.scaladsl.model.MediaTypes.application$divxml$minusdtd(); - public static final MediaType.WithOpenCharset APPLICATION_XML = akka.http.scaladsl.model.MediaTypes.application$divxml(); - public static final MediaType.Binary APPLICATION_ZIP = akka.http.scaladsl.model.MediaTypes.application$divzip(); - - public static final MediaType.Binary AUDIO_AIFF = akka.http.scaladsl.model.MediaTypes.audio$divaiff(); - public static final MediaType.Binary AUDIO_BASIC = akka.http.scaladsl.model.MediaTypes.audio$divbasic(); - public static final MediaType.Binary AUDIO_MIDI = akka.http.scaladsl.model.MediaTypes.audio$divmidi(); - public static final MediaType.Binary AUDIO_MOD = akka.http.scaladsl.model.MediaTypes.audio$divmod(); - public static final MediaType.Binary AUDIO_MPEG = akka.http.scaladsl.model.MediaTypes.audio$divmpeg(); - public static final MediaType.Binary AUDIO_OGG = akka.http.scaladsl.model.MediaTypes.audio$divogg(); - public static final MediaType.Binary AUDIO_VOC = akka.http.scaladsl.model.MediaTypes.audio$divvoc(); - public static final MediaType.Binary AUDIO_VORBIS = akka.http.scaladsl.model.MediaTypes.audio$divvorbis(); - public static final MediaType.Binary AUDIO_VOXWARE = akka.http.scaladsl.model.MediaTypes.audio$divvoxware(); - public static final MediaType.Binary AUDIO_WAV = akka.http.scaladsl.model.MediaTypes.audio$divwav(); - public static final MediaType.Binary AUDIO_X_REALAUDIO = akka.http.scaladsl.model.MediaTypes.audio$divx$minusrealaudio(); - public static final MediaType.Binary AUDIO_X_PSID = akka.http.scaladsl.model.MediaTypes.audio$divx$minuspsid(); - public static final MediaType.Binary AUDIO_XM = akka.http.scaladsl.model.MediaTypes.audio$divxm(); - public static final MediaType.Binary AUDIO_WEBM = akka.http.scaladsl.model.MediaTypes.audio$divwebm(); - - public static final MediaType.Binary IMAGE_GIF = akka.http.scaladsl.model.MediaTypes.image$divgif(); - public static final MediaType.Binary IMAGE_JPEG = akka.http.scaladsl.model.MediaTypes.image$divjpeg(); - public static final MediaType.Binary IMAGE_PICT = akka.http.scaladsl.model.MediaTypes.image$divpict(); - public static final MediaType.Binary IMAGE_PNG = akka.http.scaladsl.model.MediaTypes.image$divpng(); - public static final MediaType.Binary IMAGE_SVG_XML = akka.http.scaladsl.model.MediaTypes.image$divsvg$plusxml(); - public static final MediaType.Binary IMAGE_TIFF = akka.http.scaladsl.model.MediaTypes.image$divtiff(); - public static final MediaType.Binary IMAGE_X_ICON = akka.http.scaladsl.model.MediaTypes.image$divx$minusicon(); - public static final MediaType.Binary IMAGE_X_MS_BMP = akka.http.scaladsl.model.MediaTypes.image$divx$minusms$minusbmp(); - public static final MediaType.Binary IMAGE_X_PCX = akka.http.scaladsl.model.MediaTypes.image$divx$minuspcx(); - public static final MediaType.Binary IMAGE_X_PICT = akka.http.scaladsl.model.MediaTypes.image$divx$minuspict(); - public static final MediaType.Binary IMAGE_X_QUICKTIME = akka.http.scaladsl.model.MediaTypes.image$divx$minusquicktime(); - public static final MediaType.Binary IMAGE_X_RGB = akka.http.scaladsl.model.MediaTypes.image$divx$minusrgb(); - public static final MediaType.Binary IMAGE_X_XBITMAP = akka.http.scaladsl.model.MediaTypes.image$divx$minusxbitmap(); - public static final MediaType.Binary IMAGE_X_XPIXMAP = akka.http.scaladsl.model.MediaTypes.image$divx$minusxpixmap(); - public static final MediaType.Binary IMAGE_WEBP = akka.http.scaladsl.model.MediaTypes.image$divwebp(); - - public static final MediaType.Binary MESSAGE_HTTP = akka.http.scaladsl.model.MediaTypes.message$divhttp(); - public static final MediaType.Binary MESSAGE_DELIVERY_STATUS = akka.http.scaladsl.model.MediaTypes.message$divdelivery$minusstatus(); - public static final MediaType.Binary MESSAGE_RFC822 = akka.http.scaladsl.model.MediaTypes.message$divrfc822(); - - public static final MediaType.WithOpenCharset MULTIPART_MIXED = akka.http.scaladsl.model.MediaTypes.multipart$divmixed(); - public static final MediaType.WithOpenCharset MULTIPART_ALTERNATIVE = akka.http.scaladsl.model.MediaTypes.multipart$divalternative(); - public static final MediaType.WithOpenCharset MULTIPART_RELATED = akka.http.scaladsl.model.MediaTypes.multipart$divrelated(); - public static final MediaType.WithOpenCharset MULTIPART_FORM_DATA = akka.http.scaladsl.model.MediaTypes.multipart$divform$minusdata(); - public static final MediaType.WithOpenCharset MULTIPART_SIGNED = akka.http.scaladsl.model.MediaTypes.multipart$divsigned(); - public static final MediaType.WithOpenCharset MULTIPART_ENCRYPTED = akka.http.scaladsl.model.MediaTypes.multipart$divencrypted(); - public static final MediaType.WithOpenCharset MULTIPART_BYTERANGES = akka.http.scaladsl.model.MediaTypes.multipart$divbyteranges(); - - public static final MediaType.WithOpenCharset TEXT_ASP = akka.http.scaladsl.model.MediaTypes.text$divasp(); - public static final MediaType.WithOpenCharset TEXT_CACHE_MANIFEST = akka.http.scaladsl.model.MediaTypes.text$divcache$minusmanifest(); - public static final MediaType.WithOpenCharset TEXT_CALENDAR = akka.http.scaladsl.model.MediaTypes.text$divcalendar(); - public static final MediaType.WithOpenCharset TEXT_CSS = akka.http.scaladsl.model.MediaTypes.text$divcss(); - public static final MediaType.WithOpenCharset TEXT_CSV = akka.http.scaladsl.model.MediaTypes.text$divcsv(); - public static final MediaType.WithOpenCharset TEXT_HTML = akka.http.scaladsl.model.MediaTypes.text$divhtml(); - public static final MediaType.WithOpenCharset TEXT_MARKDOWN = akka.http.scaladsl.model.MediaTypes.text$divmarkdown(); - public static final MediaType.WithOpenCharset TEXT_MCF = akka.http.scaladsl.model.MediaTypes.text$divmcf(); - public static final MediaType.WithOpenCharset TEXT_PLAIN = akka.http.scaladsl.model.MediaTypes.text$divplain(); - public static final MediaType.WithOpenCharset TEXT_RICHTEXT = akka.http.scaladsl.model.MediaTypes.text$divrichtext(); - public static final MediaType.WithOpenCharset TEXT_TAB_SEPARATED_VALUES = akka.http.scaladsl.model.MediaTypes.text$divtab$minusseparated$minusvalues(); - public static final MediaType.WithOpenCharset TEXT_URI_LIST = akka.http.scaladsl.model.MediaTypes.text$divuri$minuslist(); - public static final MediaType.WithOpenCharset TEXT_VND_WAP_WML = akka.http.scaladsl.model.MediaTypes.text$divvnd$u002Ewap$u002Ewml(); - public static final MediaType.WithOpenCharset TEXT_VND_WAP_WMLSCRIPT = akka.http.scaladsl.model.MediaTypes.text$divvnd$u002Ewap$u002Ewmlscript(); - public static final MediaType.WithOpenCharset TEXT_X_ASM = akka.http.scaladsl.model.MediaTypes.text$divx$minusasm(); - public static final MediaType.WithOpenCharset TEXT_X_C = akka.http.scaladsl.model.MediaTypes.text$divx$minusc(); - public static final MediaType.WithOpenCharset TEXT_X_COMPONENT = akka.http.scaladsl.model.MediaTypes.text$divx$minuscomponent(); - public static final MediaType.WithOpenCharset TEXT_X_H = akka.http.scaladsl.model.MediaTypes.text$divx$minush(); - public static final MediaType.WithOpenCharset TEXT_X_JAVA_SOURCE = akka.http.scaladsl.model.MediaTypes.text$divx$minusjava$minussource(); - public static final MediaType.WithOpenCharset TEXT_X_PASCAL = akka.http.scaladsl.model.MediaTypes.text$divx$minuspascal(); - public static final MediaType.WithOpenCharset TEXT_X_SCRIPT = akka.http.scaladsl.model.MediaTypes.text$divx$minusscript(); - public static final MediaType.WithOpenCharset TEXT_X_SCRIPTCSH = akka.http.scaladsl.model.MediaTypes.text$divx$minusscriptcsh(); - public static final MediaType.WithOpenCharset TEXT_X_SCRIPTELISP = akka.http.scaladsl.model.MediaTypes.text$divx$minusscriptelisp(); - public static final MediaType.WithOpenCharset TEXT_X_SCRIPTKSH = akka.http.scaladsl.model.MediaTypes.text$divx$minusscriptksh(); - public static final MediaType.WithOpenCharset TEXT_X_SCRIPTLISP = akka.http.scaladsl.model.MediaTypes.text$divx$minusscriptlisp(); - public static final MediaType.WithOpenCharset TEXT_X_SCRIPTPERL = akka.http.scaladsl.model.MediaTypes.text$divx$minusscriptperl(); - public static final MediaType.WithOpenCharset TEXT_X_SCRIPTPERL_MODULE = akka.http.scaladsl.model.MediaTypes.text$divx$minusscriptperl$minusmodule(); - public static final MediaType.WithOpenCharset TEXT_X_SCRIPTPHYTON = akka.http.scaladsl.model.MediaTypes.text$divx$minusscriptphyton(); - public static final MediaType.WithOpenCharset TEXT_X_SCRIPTREXX = akka.http.scaladsl.model.MediaTypes.text$divx$minusscriptrexx(); - public static final MediaType.WithOpenCharset TEXT_X_SCRIPTSCHEME = akka.http.scaladsl.model.MediaTypes.text$divx$minusscriptscheme(); - public static final MediaType.WithOpenCharset TEXT_X_SCRIPTSH = akka.http.scaladsl.model.MediaTypes.text$divx$minusscriptsh(); - public static final MediaType.WithOpenCharset TEXT_X_SCRIPTTCL = akka.http.scaladsl.model.MediaTypes.text$divx$minusscripttcl(); - public static final MediaType.WithOpenCharset TEXT_X_SCRIPTTCSH = akka.http.scaladsl.model.MediaTypes.text$divx$minusscripttcsh(); - public static final MediaType.WithOpenCharset TEXT_X_SCRIPTZSH = akka.http.scaladsl.model.MediaTypes.text$divx$minusscriptzsh(); - public static final MediaType.WithOpenCharset TEXT_X_SERVER_PARSED_HTML = akka.http.scaladsl.model.MediaTypes.text$divx$minusserver$minusparsed$minushtml(); - public static final MediaType.WithOpenCharset TEXT_X_SETEXT = akka.http.scaladsl.model.MediaTypes.text$divx$minussetext(); - public static final MediaType.WithOpenCharset TEXT_X_SGML = akka.http.scaladsl.model.MediaTypes.text$divx$minussgml(); - public static final MediaType.WithOpenCharset TEXT_X_SPEECH = akka.http.scaladsl.model.MediaTypes.text$divx$minusspeech(); - public static final MediaType.WithOpenCharset TEXT_X_UUENCODE = akka.http.scaladsl.model.MediaTypes.text$divx$minusuuencode(); - public static final MediaType.WithOpenCharset TEXT_X_VCALENDAR = akka.http.scaladsl.model.MediaTypes.text$divx$minusvcalendar(); - public static final MediaType.WithOpenCharset TEXT_X_VCARD = akka.http.scaladsl.model.MediaTypes.text$divx$minusvcard(); - public static final MediaType.WithOpenCharset TEXT_XML = akka.http.scaladsl.model.MediaTypes.text$divxml(); - - public static final MediaType.Binary VIDEO_AVS_VIDEO = akka.http.scaladsl.model.MediaTypes.video$divavs$minusvideo(); - public static final MediaType.Binary VIDEO_DIVX = akka.http.scaladsl.model.MediaTypes.video$divdivx(); - public static final MediaType.Binary VIDEO_GL = akka.http.scaladsl.model.MediaTypes.video$divgl(); - public static final MediaType.Binary VIDEO_MP4 = akka.http.scaladsl.model.MediaTypes.video$divmp4(); - public static final MediaType.Binary VIDEO_MPEG = akka.http.scaladsl.model.MediaTypes.video$divmpeg(); - public static final MediaType.Binary VIDEO_OGG = akka.http.scaladsl.model.MediaTypes.video$divogg(); - public static final MediaType.Binary VIDEO_QUICKTIME = akka.http.scaladsl.model.MediaTypes.video$divquicktime(); - public static final MediaType.Binary VIDEO_X_DV = akka.http.scaladsl.model.MediaTypes.video$divx$minusdv(); - public static final MediaType.Binary VIDEO_X_FLV = akka.http.scaladsl.model.MediaTypes.video$divx$minusflv(); - public static final MediaType.Binary VIDEO_X_MOTION_JPEG = akka.http.scaladsl.model.MediaTypes.video$divx$minusmotion$minusjpeg(); - public static final MediaType.Binary VIDEO_X_MS_ASF = akka.http.scaladsl.model.MediaTypes.video$divx$minusms$minusasf(); - public static final MediaType.Binary VIDEO_X_MSVIDEO = akka.http.scaladsl.model.MediaTypes.video$divx$minusmsvideo(); - public static final MediaType.Binary VIDEO_X_SGI_MOVIE = akka.http.scaladsl.model.MediaTypes.video$divx$minussgi$minusmovie(); - public static final MediaType.Binary VIDEO_WEBM = akka.http.scaladsl.model.MediaTypes.video$divwebm(); - - public static final MediaType.Compressibility COMPRESSIBLE = akka.http.scaladsl.model.MediaType.Compressible$.MODULE$; - public static final MediaType.Compressibility NOT_COMPRESSIBLE = akka.http.scaladsl.model.MediaType.NotCompressible$.MODULE$; - public static final MediaType.Compressibility GZIPPED = akka.http.scaladsl.model.MediaType.Gzipped$.MODULE$; - - public static MediaType.Binary applicationBinary(String subType, boolean compressible, String... fileExtensions) { - akka.http.scaladsl.model.MediaType.Compressibility comp = compressible ? - akka.http.scaladsl.model.MediaType.Compressible$.MODULE$ : akka.http.scaladsl.model.MediaType.NotCompressible$.MODULE$; - - scala.collection.immutable.Seq fileEx = akka.japi.Util.immutableSeq(java.util.Arrays.asList(fileExtensions)); - - return akka.http.scaladsl.model.MediaType.applicationBinary(subType, comp, fileEx); - } - - public static MediaType.Binary applicationBinary(String subType, MediaType.Compressibility compressibility, String... fileExtensions) { - akka.http.scaladsl.model.MediaType.Compressibility comp = (akka.http.scaladsl.model.MediaType.Compressibility) compressibility; - - scala.collection.immutable.Seq fileEx = akka.japi.Util.immutableSeq(java.util.Arrays.asList(fileExtensions)); - - return akka.http.scaladsl.model.MediaType.applicationBinary(subType, comp, fileEx); - } - - public static MediaType.WithFixedCharset applicationWithFixedCharset(String subType, HttpCharset charset, String... fileExtensions) { - akka.http.scaladsl.model.HttpCharset cs = (akka.http.scaladsl.model.HttpCharset) charset; - - scala.collection.immutable.Seq fileEx = akka.japi.Util.immutableSeq(java.util.Arrays.asList(fileExtensions)); - - return akka.http.scaladsl.model.MediaType.applicationWithFixedCharset(subType, cs, fileEx); - } - - public static MediaType.WithOpenCharset applicationWithOpenCharset(String subType, String... fileExtensions) { - scala.collection.immutable.Seq fileEx = akka.japi.Util.immutableSeq(java.util.Arrays.asList(fileExtensions)); - - return akka.http.scaladsl.model.MediaType.applicationWithOpenCharset(subType, fileEx); - } - - public static MediaType.Binary audio(String subType, boolean compressible, String... fileExtensions) { - akka.http.scaladsl.model.MediaType.Compressibility comp = compressible ? - akka.http.scaladsl.model.MediaType.Compressible$.MODULE$ : akka.http.scaladsl.model.MediaType.NotCompressible$.MODULE$; - - scala.collection.immutable.Seq fileEx = akka.japi.Util.immutableSeq(java.util.Arrays.asList(fileExtensions)); - - return akka.http.scaladsl.model.MediaType.audio(subType, comp, fileEx); - } - - public static MediaType.Binary audio(String subType, MediaType.Compressibility compressibility, String... fileExtensions) { - akka.http.scaladsl.model.MediaType.Compressibility comp = (akka.http.scaladsl.model.MediaType.Compressibility) compressibility; - - scala.collection.immutable.Seq fileEx = akka.japi.Util.immutableSeq(java.util.Arrays.asList(fileExtensions)); - - return akka.http.scaladsl.model.MediaType.audio(subType, comp, fileEx); - } - - public static MediaType.Binary image(String subType, boolean compressible, String... fileExtensions) { - akka.http.scaladsl.model.MediaType.Compressibility comp = compressible ? - akka.http.scaladsl.model.MediaType.Compressible$.MODULE$ : akka.http.scaladsl.model.MediaType.NotCompressible$.MODULE$; - - scala.collection.immutable.Seq fileEx = akka.japi.Util.immutableSeq(java.util.Arrays.asList(fileExtensions)); - - return akka.http.scaladsl.model.MediaType.image(subType, comp, fileEx); - } - - public static MediaType.Binary image(String subType, MediaType.Compressibility compressibility, String... fileExtensions) { - akka.http.scaladsl.model.MediaType.Compressibility comp = (akka.http.scaladsl.model.MediaType.Compressibility) compressibility; - - scala.collection.immutable.Seq fileEx = akka.japi.Util.immutableSeq(java.util.Arrays.asList(fileExtensions)); - - return akka.http.scaladsl.model.MediaType.image(subType, comp, fileEx); - } - - public static MediaType.Binary message(String subType, boolean compressible, String... fileExtensions) { - akka.http.scaladsl.model.MediaType.Compressibility comp = compressible ? - akka.http.scaladsl.model.MediaType.Compressible$.MODULE$ : akka.http.scaladsl.model.MediaType.NotCompressible$.MODULE$; - - scala.collection.immutable.Seq fileEx = akka.japi.Util.immutableSeq(java.util.Arrays.asList(fileExtensions)); - - return akka.http.scaladsl.model.MediaType.message(subType, comp, fileEx); - } - - public static MediaType.Binary message(String subType, MediaType.Compressibility compressibility, String... fileExtensions) { - akka.http.scaladsl.model.MediaType.Compressibility comp = (akka.http.scaladsl.model.MediaType.Compressibility) compressibility; - - scala.collection.immutable.Seq fileEx = akka.japi.Util.immutableSeq(java.util.Arrays.asList(fileExtensions)); - - return akka.http.scaladsl.model.MediaType.message(subType, comp, fileEx); - } - - public static MediaType.WithOpenCharset text(String subType, String... fileExtensions) { - scala.collection.immutable.Seq fileEx = akka.japi.Util.immutableSeq(java.util.Arrays.asList(fileExtensions)); - - return akka.http.scaladsl.model.MediaType.text(subType, fileEx); - } - - public static MediaType.Binary video(String subType, boolean compressible, String... fileExtensions) { - akka.http.scaladsl.model.MediaType.Compressibility comp = compressible ? - akka.http.scaladsl.model.MediaType.Compressible$.MODULE$ : akka.http.scaladsl.model.MediaType.NotCompressible$.MODULE$; - - scala.collection.immutable.Seq fileEx = akka.japi.Util.immutableSeq(java.util.Arrays.asList(fileExtensions)); - - return akka.http.scaladsl.model.MediaType.video(subType, comp, fileEx); - } - - public static MediaType.Binary video(String subType, MediaType.Compressibility compressibility, String... fileExtensions) { - akka.http.scaladsl.model.MediaType.Compressibility comp = (akka.http.scaladsl.model.MediaType.Compressibility) compressibility; - - scala.collection.immutable.Seq fileEx = akka.japi.Util.immutableSeq(java.util.Arrays.asList(fileExtensions)); - - return akka.http.scaladsl.model.MediaType.video(subType, comp, fileEx); - } - - // arguments have been reordered due to varargs having to be the last argument - // should we create multiple overloads of this function? - public static MediaType.Binary customBinary(String mainType, String subType, boolean compressible, java.util.Map params, boolean allowArbitrarySubtypes, String... fileExtensions) { - akka.http.scaladsl.model.MediaType.Compressibility comp = compressible ? - akka.http.scaladsl.model.MediaType.Compressible$.MODULE$ : akka.http.scaladsl.model.MediaType.NotCompressible$.MODULE$; - - scala.collection.immutable.List fileEx = akka.japi.Util.immutableSeq(java.util.Arrays.asList(fileExtensions)).toList(); - - scala.collection.immutable.Map p = Util.convertMapToScala(params); - - return akka.http.scaladsl.model.MediaType.customBinary(mainType, subType, comp, fileEx, p, allowArbitrarySubtypes); - } - - public static MediaType.Binary customBinary(String mainType, String subType, MediaType.Compressibility compressibility, java.util.Map params, boolean allowArbitrarySubtypes, String... fileExtensions) { - akka.http.scaladsl.model.MediaType.Compressibility comp = (akka.http.scaladsl.model.MediaType.Compressibility) compressibility; - - scala.collection.immutable.List fileEx = akka.japi.Util.immutableSeq(java.util.Arrays.asList(fileExtensions)).toList(); - - scala.collection.immutable.Map p = Util.convertMapToScala(params); - - return akka.http.scaladsl.model.MediaType.customBinary(mainType, subType, comp, fileEx, p, allowArbitrarySubtypes); - } - - public static MediaType.WithFixedCharset customWithFixedCharset(String mainType, String subType, HttpCharset charset, java.util.Map params, boolean allowArbitrarySubtypes, String... fileExtensions) { - akka.http.scaladsl.model.HttpCharset cs = (akka.http.scaladsl.model.HttpCharset) charset; - - scala.collection.immutable.List fileEx = akka.japi.Util.immutableSeq(java.util.Arrays.asList(fileExtensions)).toList(); - - scala.collection.immutable.Map p = Util.convertMapToScala(params); - - return akka.http.scaladsl.model.MediaType.customWithFixedCharset(mainType, subType, cs, fileEx, p, allowArbitrarySubtypes); - } - - public static MediaType.WithOpenCharset customWithOpenCharset(String mainType, String subType, java.util.Map params, boolean allowArbitrarySubtypes, String... fileExtensions) { - scala.collection.immutable.List fileEx = akka.japi.Util.immutableSeq(java.util.Arrays.asList(fileExtensions)).toList(); - - scala.collection.immutable.Map p = Util.convertMapToScala(params); - - return akka.http.scaladsl.model.MediaType.customWithOpenCharset(mainType, subType, fileEx, p, allowArbitrarySubtypes); - } - - public static MediaType.Multipart customMultipart(String subType, java.util.Map params) { - scala.collection.immutable.Map p = Util.convertMapToScala(params); - return akka.http.scaladsl.model.MediaType.customMultipart(subType, p); - } - - - /** - * Creates a custom media type. - */ - public static MediaType custom(String value, boolean binary, boolean compressible) { - akka.http.scaladsl.model.MediaType.Compressibility comp = compressible ? - akka.http.scaladsl.model.MediaType.Compressible$.MODULE$ : akka.http.scaladsl.model.MediaType.NotCompressible$.MODULE$; - - return akka.http.scaladsl.model.MediaType.custom(value, binary, comp , - akka.http.scaladsl.model.MediaType.custom$default$4()); - } - - public static MediaType custom(String value, boolean binary, MediaType.Compressibility compressibility) { - akka.http.scaladsl.model.MediaType.Compressibility comp = (akka.http.scaladsl.model.MediaType.Compressibility) compressibility; - - return akka.http.scaladsl.model.MediaType.custom(value, binary, comp , - akka.http.scaladsl.model.MediaType.custom$default$4()); - } - - /** - * Looks up a media-type with the given main-type and sub-type. - */ - public static Optional lookup(String mainType, String subType) { - return Util., MediaType, akka.http.scaladsl.model.MediaType>lookupInRegistry(MediaTypes$.MODULE$, new scala.Tuple2(mainType, subType)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/Multipart.java b/akka-http-core/src/main/java/akka/http/javadsl/model/Multipart.java deleted file mode 100644 index 1c8ffd7195..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/Multipart.java +++ /dev/null @@ -1,147 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.CompletionStage; - -import akka.http.javadsl.model.headers.ContentDisposition; -import akka.http.javadsl.model.headers.ContentDispositionType; -import akka.http.javadsl.model.headers.RangeUnit; -import akka.stream.Materializer; -import akka.stream.javadsl.Source; - -/** - * The model of multipart content for media-types `multipart/\*` (general multipart content), - * `multipart/form-data` and `multipart/byteranges`. - * - * The basic modelling classes for these media-types ([[akka.http.scaladsl.Multipart.General]], [[Multipart.FormData]] and - * [[akka.http.scaladsl.Multipart.ByteRanges]], respectively) are stream-based but each have a strict counterpart - * (namely [[akka.http.scaladsl.Multipart.General.Strict]], [[akka.http.scaladsl.Multipart.FormData.Strict]] and - * [[akka.http.scaladsl.Multipart.ByteRanges.Strict]]). - */ -public interface Multipart { - - MediaType.Multipart getMediaType(); - - Source getParts(); - - /** - * Converts this content into its strict counterpart. - * The given `timeout` denotes the max time that an individual part must be read in. - * The CompletionStage is failed with an TimeoutException if one part isn't read completely after the given timeout. - */ - CompletionStage toStrict(long timeoutMillis, Materializer materializer); - - /** - * Creates an entity from this multipart object. - */ - RequestEntity toEntity(HttpCharset charset, String boundary); - - interface Strict extends Multipart { - Source getParts(); - - Iterable getStrictParts(); - - HttpEntity.Strict toEntity(HttpCharset charset, String boundary); - } - - interface BodyPart { - BodyPartEntity getEntity(); - - Iterable getHeaders(); - - Optional getContentDispositionHeader(); - - Map getDispositionParams(); - - Optional getDispositionType(); - - CompletionStage toStrict(long timeoutMillis, Materializer materializer); - - interface Strict extends Multipart.BodyPart { - HttpEntity.Strict getEntity(); - } - } - - /** - * Basic model for multipart content as defined by http://tools.ietf.org/html/rfc2046. - */ - interface General extends Multipart { - Source getParts(); - - CompletionStage toStrict(long timeoutMillis, Materializer materializer); - - interface Strict extends Multipart.General, Multipart.Strict { - Source getParts(); - - Iterable getStrictParts(); - } - - interface BodyPart extends Multipart.BodyPart { - CompletionStage toStrict(long timeoutMillis, Materializer materializer); - - interface Strict extends Multipart.General.BodyPart, Multipart.BodyPart.Strict { - } - } - } - - /** - * Model for `multipart/form-data` content as defined in http://tools.ietf.org/html/rfc2388. - * All parts must have distinct names. (This is not verified!) - */ - interface FormData extends Multipart { - Source getParts(); - - CompletionStage toStrict(long timeoutMillis, Materializer materializer); - - interface Strict extends Multipart.FormData, Multipart.Strict { - Source getParts(); - - Iterable getStrictParts(); - } - - interface BodyPart extends Multipart.BodyPart { - String getName(); - Map getAdditionalDispositionParams(); - Iterable getAdditionalHeaders(); - Optional getFilename(); - - CompletionStage toStrict(long timeoutMillis, Materializer materializer); - - interface Strict extends Multipart.FormData.BodyPart, Multipart.BodyPart.Strict { - } - } - } - - /** - * Model for `multipart/byteranges` content as defined by - * https://tools.ietf.org/html/rfc7233#section-5.4.1 and https://tools.ietf.org/html/rfc7233#appendix-A - */ - interface ByteRanges extends Multipart { - Source getParts(); - - CompletionStage toStrict(long timeoutMillis, Materializer materializer); - - interface Strict extends Multipart.ByteRanges, Multipart.Strict { - Source getParts(); - - Iterable getStrictParts(); - } - - interface BodyPart extends Multipart.BodyPart { - ContentRange getContentRange(); - RangeUnit getRangeUnit(); - Iterable getAdditionalHeaders(); - akka.http.javadsl.model.headers.ContentRange getContentRangeHeader(); - - CompletionStage toStrict(long timeoutMillis, Materializer materializer); - - interface Strict extends Multipart.ByteRanges.BodyPart, Multipart.BodyPart.Strict { - } - } - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/Multiparts.java b/akka-http-core/src/main/java/akka/http/javadsl/model/Multiparts.java deleted file mode 100644 index 8f9dd0ea14..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/Multiparts.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2016-2016 Lightbend Inc. - */ -package akka.http.javadsl.model; - -import scala.collection.immutable.List; -import scala.collection.immutable.Nil$; - -import java.nio.file.Path; -import java.util.Collections; -import java.util.Map; - -import static akka.http.impl.util.Util.convertArray; -import static akka.http.impl.util.Util.convertMapToScala; -import static akka.http.impl.util.Util.emptyMap; - -/** - * Constructors for Multipart instances - */ -public final class Multiparts { - /** - * Constructor for `multipart/form-data` content as defined in http://tools.ietf.org/html/rfc2388. - * All parts must have distinct names. (This is not verified!) - */ - public static Multipart.FormData createFormDataFromParts(Multipart.FormData.BodyPart... parts) { - return akka.http.scaladsl.model.Multipart.FormData$.MODULE$.createNonStrict(convertArray(parts)); - } - - /** - * Constructor for `multipart/form-data` content as defined in http://tools.ietf.org/html/rfc2388. - * All parts must have distinct names. (This is not verified!) - */ - public static Multipart.FormData.Strict createStrictFormDataFromParts(Multipart.FormData.BodyPart.Strict... parts) { - return akka.http.scaladsl.model.Multipart.FormData$.MODULE$.createStrict(convertArray(parts)); - } - - /** - * Constructor for `multipart/form-data` content as defined in http://tools.ietf.org/html/rfc2388. - * All parts must have distinct names. (This is not verified!) - */ - public static Multipart.FormData.Strict createFormDataFromFields(Map fields) { - return akka.http.scaladsl.model.Multipart.FormData$.MODULE$.createStrict(toScalaMap(fields)); - } - - /** - * Creates a FormData instance that contains a single part backed by the given file. - * - * To create an instance with several parts or for multiple files, use - * `Multiparts.createFormDataFromParts(Multiparts.createFormDataPartFromPath("field1", ...), Multiparts.createFormDataPartFromPath("field2", ...)` - */ - public static Multipart.FormData createFormDataFromPath(String name, ContentType contentType, Path path, int chunkSize) { - return akka.http.scaladsl.model.Multipart.FormData$.MODULE$.fromPath(name, (akka.http.scaladsl.model.ContentType) contentType, path, chunkSize); - } - - /** - * Creates a FormData instance that contains a single part backed by the given file. - * - * To create an instance with several parts or for multiple files, use - * `Multiparts.createFormDataFromParts(Multiparts.createFormDataPartFromPath("field1", ...), Multiparts.createFormDataPartFromPath("field2", ...)` - */ - public static Multipart.FormData createFormDataFromPath(String name, ContentType contentType, Path path) { - return akka.http.scaladsl.model.Multipart.FormData$.MODULE$.fromPath(name, (akka.http.scaladsl.model.ContentType) contentType, path, -1); - } - - /** - * Creates a BodyPart backed by a file that will be streamed using a FileSource. - */ - public static Multipart.FormData.BodyPart createFormDataPartFromPath(String name, ContentType contentType, Path path, int chunkSize) { - return akka.http.scaladsl.model.Multipart$FormData$BodyPart$.MODULE$.fromPath(name, (akka.http.scaladsl.model.ContentType) contentType, path, chunkSize); - } - - /** - * Creates a BodyPart backed by a file that will be streamed using a FileSource. - */ - public static Multipart.FormData.BodyPart createFormDataPartFromPath(String name, ContentType contentType, Path path) { - return akka.http.scaladsl.model.Multipart$FormData$BodyPart$.MODULE$.fromPath(name, (akka.http.scaladsl.model.ContentType) contentType, path, -1); - } - - /** - * Creates a BodyPart. - */ - public static Multipart.FormData.BodyPart createFormDataBodyPart(String name, BodyPartEntity entity) { - List nil = Nil$.MODULE$; - Map additionalDispositionParams = Collections.emptyMap(); - return akka.http.scaladsl.model.Multipart$FormData$BodyPart$Builder$.MODULE$.create(name, (akka.http.scaladsl.model.BodyPartEntity) entity, - convertMapToScala(additionalDispositionParams), nil); - } - - /** - * Creates a BodyPart. - */ - public static Multipart.FormData.BodyPart createFormDataBodyPart(String name, BodyPartEntity entity, Map additionalDispositionParams) { - List nil = Nil$.MODULE$; - return akka.http.scaladsl.model.Multipart$FormData$BodyPart$Builder$.MODULE$.create(name, (akka.http.scaladsl.model.BodyPartEntity) entity, - convertMapToScala(additionalDispositionParams), nil); - } - - /** - * Creates a BodyPart. - */ - public static Multipart.FormData.BodyPart createFormDataBodyPart(String name, BodyPartEntity entity, Map additionalDispositionParams, java.util.List headers) { - return akka.http.scaladsl.model.Multipart$FormData$BodyPart$Builder$.MODULE$.create(name, (akka.http.scaladsl.model.BodyPartEntity) entity, - convertMapToScala(additionalDispositionParams), toScalaSeq(headers)); - } - - /** - * Creates a BodyPart.Strict. - */ - public static Multipart.FormData.BodyPart.Strict createFormDataBodyPartStrict(String name, HttpEntity.Strict entity) { - List nil = Nil$.MODULE$; - Map additionalDispositionParams = Collections.emptyMap(); - return akka.http.scaladsl.model.Multipart$FormData$BodyPart$StrictBuilder$.MODULE$.createStrict(name, (akka.http.scaladsl.model.HttpEntity.Strict) entity, - convertMapToScala(additionalDispositionParams), nil); - } - - /** - * Creates a BodyPart.Strict. - */ - public static Multipart.FormData.BodyPart.Strict createFormDataBodyPartStrict(String name, HttpEntity.Strict entity, Map additionalDispositionParams) { - List nil = Nil$.MODULE$; - return akka.http.scaladsl.model.Multipart$FormData$BodyPart$StrictBuilder$.MODULE$.createStrict(name, (akka.http.scaladsl.model.HttpEntity.Strict) entity, - convertMapToScala(additionalDispositionParams), nil); - } - - /** - * Creates a BodyPart.Strict. - */ - public static Multipart.FormData.BodyPart.Strict createFormDataBodyPartStrict(String name, HttpEntity.Strict entity, Map additionalDispositionParams, java.util.List headers) { - return akka.http.scaladsl.model.Multipart$FormData$BodyPart$StrictBuilder$.MODULE$.createStrict(name, (akka.http.scaladsl.model.HttpEntity.Strict) entity, - convertMapToScala(additionalDispositionParams), toScalaSeq(headers)); - } - - private static scala.collection.immutable.Map toScalaMap(Map map) { - return emptyMap.$plus$plus(scala.collection.JavaConverters.mapAsScalaMapConverter(map).asScala()); - } - - private static scala.collection.Iterable toScalaSeq(java.util.List _headers) { - return scala.collection.JavaConverters.collectionAsScalaIterableConverter(_headers).asScala(); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/Query.java b/akka-http-core/src/main/java/akka/http/javadsl/model/Query.java deleted file mode 100644 index 07741b87bc..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/Query.java +++ /dev/null @@ -1,119 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -import akka.http.impl.model.JavaQuery; -import akka.http.scaladsl.model.*; -import akka.japi.Pair; -import akka.parboiled2.CharPredicate; -import akka.parboiled2.ParserInput$; - -import java.nio.charset.Charset; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.StreamSupport; - -public abstract class Query { - /** - * Returns the value of the first parameter with the given key if it exists. - */ - public abstract Optional get(String key); - - /** - * Returns the value of the first parameter with the given key or the provided default value. - */ - public abstract String getOrElse(String key, String _default); - - /** - * Returns the value of all parameters with the given key. - */ - public abstract List getAll(String key); - - /** - * Returns a `List` of all parameters of this Query. Use the `toMap()` - * method to filter out entries with duplicated keys. - */ - public abstract List> toList(); - - /** - * Returns a key/value map of the parameters of this Query. Use - * the `toList()` method to return all parameters if keys may occur - * multiple times. - */ - public abstract Map toMap(); - - /** - * Returns a `Map` of all parameters of this Query. Use the `toMap()` - * method to filter out entries with duplicated keys. - */ - public abstract Map> toMultiMap(); - - /** - * Returns a copy of this instance with a query parameter added. - */ - public abstract Query withParam(String key, String value); - - /** - * Renders this Query into its string representation using the given charset. - */ - public abstract String render(HttpCharset charset); - - /** - * Renders this Query into its string representation using the given charset and char predicate. - */ - public abstract String render(HttpCharset charset, CharPredicate keep); - - /** - * Returns an empty Query. - */ - public static final Query EMPTY = new JavaQuery(UriJavaAccessor.emptyQuery()); - - /** - * Returns a Query created by parsing the given undecoded string representation. - */ - public static Query create(String rawQuery) { - return new JavaQuery(akka.http.scaladsl.model.Uri.Query$.MODULE$.apply(rawQuery)); - } - - /** - * Returns a Query created by parsing the given undecoded string representation with the provided parsing mode. - */ - public static Query create(String rawQuery, akka.http.scaladsl.model.Uri.ParsingMode parsingMode) { - return new JavaQuery(UriJavaAccessor.queryApply(rawQuery, parsingMode)); - } - - /** - * Returns a Query created by parsing the given undecoded string representation with the provided charset and parsing mode. - */ - public static Query create(String rawQuery, Charset charset, akka.http.scaladsl.model.Uri.ParsingMode parsingMode) { - return new JavaQuery(akka.http.scaladsl.model.Uri.Query$.MODULE$.apply(ParserInput$.MODULE$.apply(rawQuery), charset, parsingMode)); - } - - /** - * Returns a Query from the given parameters. - */ - @SafeVarargs - public static Query create(Pair... params) { - return new JavaQuery(UriJavaAccessor.queryApply(params)); - } - - /** - * Returns a Query from the given parameters. - */ - public static Query create(Iterable> params) { - @SuppressWarnings("unchecked") - final Pair[] paramsArray = - StreamSupport.stream(params.spliterator(), false).toArray(Pair[]::new); - return create(paramsArray); - } - - /** - * Returns a Query from the given parameters. - */ - public static Query create(Map params) { - return new JavaQuery(UriJavaAccessor.queryApply(params)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/RemoteAddress.java b/akka-http-core/src/main/java/akka/http/javadsl/model/RemoteAddress.java deleted file mode 100644 index eae1ce4626..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/RemoteAddress.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.util.Optional; - -import akka.http.javadsl.model.headers.HttpEncodingRanges; -import scala.compat.java8.OptionConverters; - -public abstract class RemoteAddress { - public abstract boolean isUnknown(); - - public abstract Optional getAddress(); - - /** - * Returns a port if defined or 0 otherwise. - */ - public abstract int getPort(); - - public static RemoteAddress create(InetAddress address) { - return akka.http.scaladsl.model.RemoteAddress.apply(address, OptionConverters.toScala(Optional.empty())); - } - public static RemoteAddress create(InetSocketAddress address) { - return akka.http.scaladsl.model.RemoteAddress.apply(address); - } - public static RemoteAddress create(byte[] address) { - return akka.http.scaladsl.model.RemoteAddress.apply(address); - } - - /** - * @deprecated because of troublesome initialisation order (with regards to scaladsl class implementing this class). - * In some edge cases this field could end up containing a null value. - * Will be removed in Akka 3.x, use {@link RemoteAddresses#UNKNOWN} instead. - */ - @Deprecated - // FIXME: Remove in Akka 3.0 - public static final RemoteAddress UNKNOWN = RemoteAddresses.UNKNOWN; -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/RemoteAddresses.java b/akka-http-core/src/main/java/akka/http/javadsl/model/RemoteAddresses.java deleted file mode 100644 index 069e165025..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/RemoteAddresses.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -public final class RemoteAddresses { - private RemoteAddresses() { } - - public static final RemoteAddress UNKNOWN = akka.http.scaladsl.model.RemoteAddress.Unknown$.MODULE$; -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/RequestEntity.java b/akka-http-core/src/main/java/akka/http/javadsl/model/RequestEntity.java deleted file mode 100644 index 8eca6befdd..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/RequestEntity.java +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -/** Marker-interface for entity types that can be used in a request */ -public interface RequestEntity extends ResponseEntity {} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/RequestEntityAcceptance.java b/akka-http-core/src/main/java/akka/http/javadsl/model/RequestEntityAcceptance.java deleted file mode 100644 index f2c8c69d63..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/RequestEntityAcceptance.java +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -/** - * @see RequestEntityAcceptances for convenience access to often used values. - * Do not extend this to a concrete Java class, - * as implementation of RequestEntityAcceptation should only exist in Scala - */ -public abstract class RequestEntityAcceptance { - public abstract boolean isEntityAccepted(); -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/RequestEntityAcceptances.java b/akka-http-core/src/main/java/akka/http/javadsl/model/RequestEntityAcceptances.java deleted file mode 100644 index 9063b17961..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/RequestEntityAcceptances.java +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - - -package akka.http.javadsl.model; - -public final class RequestEntityAcceptances { - private RequestEntityAcceptances() {} - - public static final RequestEntityAcceptance Expected = akka.http.scaladsl.model.RequestEntityAcceptance.Expected$.MODULE$; - public static final RequestEntityAcceptance Tolerated = akka.http.scaladsl.model.RequestEntityAcceptance.Tolerated$.MODULE$; - public static final RequestEntityAcceptance Disallowed = akka.http.scaladsl.model.RequestEntityAcceptance.Disallowed$.MODULE$; -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/ResponseEntity.java b/akka-http-core/src/main/java/akka/http/javadsl/model/ResponseEntity.java deleted file mode 100644 index afcedf9ed1..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/ResponseEntity.java +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -/** Marker-interface for entity types that can be used in a response */ -public interface ResponseEntity extends HttpEntity {} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/StatusCode.java b/akka-http-core/src/main/java/akka/http/javadsl/model/StatusCode.java deleted file mode 100644 index 3e5eacc8d3..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/StatusCode.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -/** - * Represents an Http status-code and message. See {@link StatusCodes} for the set of predefined - * status-codes. - * - * @see StatusCodes for convenience access to often used values. - */ -public abstract class StatusCode { - /** - * Returns the numeric code of this status code. - */ - public abstract int intValue(); - - /** - * Returns the reason message for this status code. - */ - public abstract String reason(); - - /** - * Returns the default message to be included as the content of an Http response - * with this status-code. - */ - public abstract String defaultMessage(); - - /** - * Returns if the status-code represents success. - */ - public abstract boolean isSuccess(); - - /** - * Returns if the status-code represents failure. - */ - public abstract boolean isFailure(); - - /** - * Returns if a response with this status-code is allowed to be accompanied with - * a non-empty entity. - */ - public abstract boolean allowsEntity(); - - /** - * Returns if the status-code is a redirection status code. - */ - public abstract boolean isRedirection(); -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/StatusCodes.java b/akka-http-core/src/main/java/akka/http/javadsl/model/StatusCodes.java deleted file mode 100644 index bd4ad24aed..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/StatusCodes.java +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -import akka.http.impl.util.Util; -import akka.http.scaladsl.model.StatusCodes$; - -import java.util.Optional; - -/** - * Contains the set of predefined status-codes along with static methods to access and create custom - * status-codes. - */ -public final class StatusCodes { - private StatusCodes() {} - - public static final StatusCode CONTINUE = akka.http.scaladsl.model.StatusCodes.Continue(); - public static final StatusCode SWITCHING_PROTOCOLS = akka.http.scaladsl.model.StatusCodes.SwitchingProtocols(); - public static final StatusCode PROCESSING = akka.http.scaladsl.model.StatusCodes.Processing(); - - public static final StatusCode OK = akka.http.scaladsl.model.StatusCodes.OK(); - public static final StatusCode CREATED = akka.http.scaladsl.model.StatusCodes.Created(); - public static final StatusCode ACCEPTED = akka.http.scaladsl.model.StatusCodes.Accepted(); - public static final StatusCode NON_AUTHORITATIVE_INFORMATION = akka.http.scaladsl.model.StatusCodes.NonAuthoritativeInformation(); - public static final StatusCode NO_CONTENT = akka.http.scaladsl.model.StatusCodes.NoContent(); - public static final StatusCode RESET_CONTENT = akka.http.scaladsl.model.StatusCodes.ResetContent(); - public static final StatusCode PARTIAL_CONTENT = akka.http.scaladsl.model.StatusCodes.PartialContent(); - public static final StatusCode MULTI_STATUS = akka.http.scaladsl.model.StatusCodes.MultiStatus(); - public static final StatusCode ALREADY_REPORTED = akka.http.scaladsl.model.StatusCodes.AlreadyReported(); - public static final StatusCode IMUSED = akka.http.scaladsl.model.StatusCodes.IMUsed(); - - public static final StatusCode MULTIPLE_CHOICES = akka.http.scaladsl.model.StatusCodes.MultipleChoices(); - public static final StatusCode MOVED_PERMANENTLY = akka.http.scaladsl.model.StatusCodes.MovedPermanently(); - public static final StatusCode FOUND = akka.http.scaladsl.model.StatusCodes.Found(); - public static final StatusCode SEE_OTHER = akka.http.scaladsl.model.StatusCodes.SeeOther(); - public static final StatusCode NOT_MODIFIED = akka.http.scaladsl.model.StatusCodes.NotModified(); - public static final StatusCode USE_PROXY = akka.http.scaladsl.model.StatusCodes.UseProxy(); - public static final StatusCode TEMPORARY_REDIRECT = akka.http.scaladsl.model.StatusCodes.TemporaryRedirect(); - public static final StatusCode PERMANENT_REDIRECT = akka.http.scaladsl.model.StatusCodes.PermanentRedirect(); - - public static final StatusCode BAD_REQUEST = akka.http.scaladsl.model.StatusCodes.BadRequest(); - public static final StatusCode UNAUTHORIZED = akka.http.scaladsl.model.StatusCodes.Unauthorized(); - public static final StatusCode PAYMENT_REQUIRED = akka.http.scaladsl.model.StatusCodes.PaymentRequired(); - public static final StatusCode FORBIDDEN = akka.http.scaladsl.model.StatusCodes.Forbidden(); - public static final StatusCode NOT_FOUND = akka.http.scaladsl.model.StatusCodes.NotFound(); - public static final StatusCode METHOD_NOT_ALLOWED = akka.http.scaladsl.model.StatusCodes.MethodNotAllowed(); - public static final StatusCode NOT_ACCEPTABLE = akka.http.scaladsl.model.StatusCodes.NotAcceptable(); - public static final StatusCode PROXY_AUTHENTICATION_REQUIRED = akka.http.scaladsl.model.StatusCodes.ProxyAuthenticationRequired(); - public static final StatusCode REQUEST_TIMEOUT = akka.http.scaladsl.model.StatusCodes.RequestTimeout(); - public static final StatusCode CONFLICT = akka.http.scaladsl.model.StatusCodes.Conflict(); - public static final StatusCode GONE = akka.http.scaladsl.model.StatusCodes.Gone(); - public static final StatusCode LENGTH_REQUIRED = akka.http.scaladsl.model.StatusCodes.LengthRequired(); - public static final StatusCode PRECONDITION_FAILED = akka.http.scaladsl.model.StatusCodes.PreconditionFailed(); - public static final StatusCode REQUEST_ENTITY_TOO_LARGE = akka.http.scaladsl.model.StatusCodes.RequestEntityTooLarge(); - public static final StatusCode REQUEST_URI_TOO_LONG = akka.http.scaladsl.model.StatusCodes.RequestUriTooLong(); - public static final StatusCode UNSUPPORTED_MEDIA_TYPE = akka.http.scaladsl.model.StatusCodes.UnsupportedMediaType(); - public static final StatusCode REQUESTED_RANGE_NOT_SATISFIABLE = akka.http.scaladsl.model.StatusCodes.RequestedRangeNotSatisfiable(); - public static final StatusCode EXPECTATION_FAILED = akka.http.scaladsl.model.StatusCodes.ExpectationFailed(); - public static final StatusCode ENHANCE_YOUR_CALM = akka.http.scaladsl.model.StatusCodes.EnhanceYourCalm(); - public static final StatusCode UNPROCESSABLE_ENTITY = akka.http.scaladsl.model.StatusCodes.UnprocessableEntity(); - public static final StatusCode LOCKED = akka.http.scaladsl.model.StatusCodes.Locked(); - public static final StatusCode FAILED_DEPENDENCY = akka.http.scaladsl.model.StatusCodes.FailedDependency(); - public static final StatusCode UNORDERED_COLLECTION = akka.http.scaladsl.model.StatusCodes.UnorderedCollection(); - public static final StatusCode UPGRADE_REQUIRED = akka.http.scaladsl.model.StatusCodes.UpgradeRequired(); - public static final StatusCode PRECONDITION_REQUIRED = akka.http.scaladsl.model.StatusCodes.PreconditionRequired(); - public static final StatusCode TOO_MANY_REQUESTS = akka.http.scaladsl.model.StatusCodes.TooManyRequests(); - public static final StatusCode REQUEST_HEADER_FIELDS_TOO_LARGE = akka.http.scaladsl.model.StatusCodes.RequestHeaderFieldsTooLarge(); - public static final StatusCode RETRY_WITH = akka.http.scaladsl.model.StatusCodes.RetryWith(); - public static final StatusCode BLOCKED_BY_PARENTAL_CONTROLS = akka.http.scaladsl.model.StatusCodes.BlockedByParentalControls(); - public static final StatusCode UNAVAILABLE_FOR_LEGAL_REASONS = akka.http.scaladsl.model.StatusCodes.UnavailableForLegalReasons(); - - public static final StatusCode INTERNAL_SERVER_ERROR = akka.http.scaladsl.model.StatusCodes.InternalServerError(); - public static final StatusCode NOT_IMPLEMENTED = akka.http.scaladsl.model.StatusCodes.NotImplemented(); - public static final StatusCode BAD_GATEWAY = akka.http.scaladsl.model.StatusCodes.BadGateway(); - public static final StatusCode SERVICE_UNAVAILABLE = akka.http.scaladsl.model.StatusCodes.ServiceUnavailable(); - public static final StatusCode GATEWAY_TIMEOUT = akka.http.scaladsl.model.StatusCodes.GatewayTimeout(); - public static final StatusCode HTTPVERSION_NOT_SUPPORTED = akka.http.scaladsl.model.StatusCodes.HTTPVersionNotSupported(); - public static final StatusCode VARIANT_ALSO_NEGOTIATES = akka.http.scaladsl.model.StatusCodes.VariantAlsoNegotiates(); - public static final StatusCode INSUFFICIENT_STORAGE = akka.http.scaladsl.model.StatusCodes.InsufficientStorage(); - public static final StatusCode LOOP_DETECTED = akka.http.scaladsl.model.StatusCodes.LoopDetected(); - public static final StatusCode BANDWIDTH_LIMIT_EXCEEDED = akka.http.scaladsl.model.StatusCodes.BandwidthLimitExceeded(); - public static final StatusCode NOT_EXTENDED = akka.http.scaladsl.model.StatusCodes.NotExtended(); - public static final StatusCode NETWORK_AUTHENTICATION_REQUIRED = akka.http.scaladsl.model.StatusCodes.NetworkAuthenticationRequired(); - public static final StatusCode NETWORK_READ_TIMEOUT = akka.http.scaladsl.model.StatusCodes.NetworkReadTimeout(); - public static final StatusCode NETWORK_CONNECT_TIMEOUT = akka.http.scaladsl.model.StatusCodes.NetworkConnectTimeout(); - - /** - * Create a custom status code. - */ - public static StatusCode custom(int intValue, String reason, String defaultMessage, boolean isSuccess, boolean allowsEntity) { - return akka.http.scaladsl.model.StatusCodes.custom(intValue, reason, defaultMessage, isSuccess, allowsEntity); - } - - /** - * Create a custom status code. - */ - public static StatusCode custom(int intValue, String reason, String defaultMessage) { - return akka.http.scaladsl.model.StatusCodes.custom(intValue, reason, defaultMessage); - } - - /** - * Looks up a status-code by numeric code. Throws an exception if no such status-code is found. - */ - public static StatusCode get(int intValue) { - return akka.http.scaladsl.model.StatusCode.int2StatusCode(intValue); - } - - /** - * Looks up a status-code by numeric code and returns Some(code). Returns None otherwise. - */ - public static Optional lookup(int intValue) { - return Util.lookupInRegistry(StatusCodes$.MODULE$, intValue); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/TransferEncoding.java b/akka-http-core/src/main/java/akka/http/javadsl/model/TransferEncoding.java deleted file mode 100644 index 2c344d04ab..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/TransferEncoding.java +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -import akka.http.impl.util.Util; -import akka.http.javadsl.model.headers.EntityTagRanges; - -import java.util.Map; - -/** - * @see TransferEncodings for convenience access to often used values. - */ -public abstract class TransferEncoding { - public abstract String name(); - - public abstract Map getParams(); - - public static TransferEncoding createExtension(String name) { - return new akka.http.scaladsl.model.TransferEncodings.Extension(name, Util.emptyMap); - } - public static TransferEncoding createExtension(String name, Map params) { - return new akka.http.scaladsl.model.TransferEncodings.Extension(name, Util.convertMapToScala(params)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/TransferEncodings.java b/akka-http-core/src/main/java/akka/http/javadsl/model/TransferEncodings.java deleted file mode 100644 index b41f6b061b..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/TransferEncodings.java +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -public final class TransferEncodings { - private TransferEncodings() {} - - public static final TransferEncoding CHUNKED = akka.http.scaladsl.model.TransferEncodings.chunked$.MODULE$; - public static final TransferEncoding COMPRESS = akka.http.scaladsl.model.TransferEncodings.compress$.MODULE$; - public static final TransferEncoding DEFLATE = akka.http.scaladsl.model.TransferEncodings.deflate$.MODULE$; - public static final TransferEncoding GZIP = akka.http.scaladsl.model.TransferEncodings.gzip$.MODULE$; -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/UniversalEntity.java b/akka-http-core/src/main/java/akka/http/javadsl/model/UniversalEntity.java deleted file mode 100644 index 1b84720dcc..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/UniversalEntity.java +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -/** Marker-interface for entity types that can be used in any context */ -public interface UniversalEntity extends RequestEntity, ResponseEntity, BodyPartEntity {} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/Uri.java b/akka-http-core/src/main/java/akka/http/javadsl/model/Uri.java deleted file mode 100644 index 436eb33cbb..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/Uri.java +++ /dev/null @@ -1,180 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -import java.nio.charset.Charset; - -import akka.http.impl.model.JavaUri; -import akka.http.scaladsl.model.UriJavaAccessor; -import akka.parboiled2.ParserInput$; - -import java.util.Optional; - -/** - * Represents an Uri. Use the `withX` methods to create modified copies of a given instance. - */ -public abstract class Uri { - /** - * Returns if this is an absolute Uri. - */ - public abstract boolean isAbsolute(); - - /** - * Returns if this is a relative Uri. - */ - public abstract boolean isRelative(); - - /** - * Returns if this is an empty Uri. - */ - public abstract boolean isEmpty(); - - /** - * Returns the scheme of this Uri. - */ - public abstract String scheme(); - - /** - * Returns the Host of this Uri. - */ - public abstract Host host(); - - /** - * Returns the port of this Uri. - */ - public abstract int port(); - - /** - * Returns the user-info of this Uri. - */ - public abstract String userInfo(); - - /** - * Returns a String representation of the path of this Uri. - */ - public abstract String path(); - - /** - * Returns the path segments of this Uri as an Iterable. - */ - public abstract Iterable pathSegments(); - - /** - * Returns a decoded String representation of the query of this Uri. - */ - public abstract Optional queryString(Charset charset); - - /** - * Returns an undecoded String representation of the query of this Uri. - */ - public abstract Optional rawQueryString(); - - /** - * Returns the parsed Query instance of this Uri. - */ - public abstract Query query(); - - /** - * Returns the parsed Query instance of this Uri using the given charset and parsing mode. - */ - public abstract Query query(Charset charset, akka.http.scaladsl.model.Uri.ParsingMode mode); - - /** - * Returns the fragment part of this Uri. - */ - public abstract Optional fragment(); - - /** - * Returns a copy of this instance with a new scheme. - */ - public abstract Uri scheme(String scheme); - - /** - * Returns a copy of this instance with a new Host. - */ - public abstract Uri host(Host host); - - /** - * Returns a copy of this instance with a new host. - */ - public abstract Uri host(String host); - - /** - * Returns a copy of this instance with a new port. - */ - public abstract Uri port(int port); - - /** - * Returns a copy of this instance with new user-info. - */ - public abstract Uri userInfo(String userInfo); - - /** - * Returns a copy of this instance with a new path. - */ - public abstract Uri path(String path); - - /** - * Returns a copy of this instance with a path segment added at the end. - */ - public abstract Uri addPathSegment(String segment); - - /** - * Returns a copy of this instance with a new query. - */ - public abstract Uri rawQueryString(String rawQuery); - - /** - * Returns a copy of this instance with a new query. - */ - public abstract Uri query(Query query); - - /** - * Returns a copy of this instance that is relative. - */ - public abstract Uri toRelative(); - - /** - * Returns a copy of this instance with a new fragment. - */ - public abstract Uri fragment(String fragment); - - /** - * Returns a copy of this instance with a new optional fragment. - */ - public abstract Uri fragment(Optional fragment); - - public static final akka.http.scaladsl.model.Uri.ParsingMode STRICT = UriJavaAccessor.pmStrict(); - public static final akka.http.scaladsl.model.Uri.ParsingMode RELAXED = UriJavaAccessor.pmRelaxed(); - - /** - * Creates a default Uri to be modified using the modification methods. - */ - public static final Uri EMPTY = new JavaUri(akka.http.scaladsl.model.Uri.Empty$.MODULE$); - - /** - * Returns a Uri created by parsing the given string representation. - */ - public static Uri create(String uri) { - return new JavaUri(akka.http.scaladsl.model.Uri.apply(uri)); - } - - /** - * Returns a Uri created by parsing the given string representation with the provided parsing mode. - */ - public static Uri create(String uri, akka.http.scaladsl.model.Uri.ParsingMode parsingMode) { - return new JavaUri(akka.http.scaladsl.model.Uri.apply(ParserInput$.MODULE$.apply(uri), parsingMode)); - } - - /** - * Returns a Uri created by parsing the given string representation with the provided charset and parsing mode. - */ - public static Uri create(String uri, Charset charset, akka.http.scaladsl.model.Uri.ParsingMode parsingMode) { - return new JavaUri(akka.http.scaladsl.model.Uri.apply(ParserInput$.MODULE$.apply(uri), charset, parsingMode)); - } - - - public static interface ParsingMode {} -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Accept.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Accept.java deleted file mode 100644 index acdcf4661b..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Accept.java +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -import akka.http.impl.util.Util; -import akka.http.javadsl.model.MediaRange; - -/** - * Model for the `Accept` header. - * Specification: http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-26#section-5.3.2 - */ -public abstract class Accept extends akka.http.scaladsl.model.HttpHeader { - public abstract Iterable getMediaRanges(); - - public abstract boolean acceptsAll(); - - public static Accept create(MediaRange... mediaRanges) { - return new akka.http.scaladsl.model.headers.Accept(Util.convertArray(mediaRanges)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AcceptCharset.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AcceptCharset.java deleted file mode 100644 index b261b9518f..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AcceptCharset.java +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -import akka.http.impl.util.Util; -import akka.http.javadsl.model.HttpCharsetRange; - -/** - * Model for the `Accept-Charset` header. - * Specification: http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-26#section-5.3.3 - */ -public abstract class AcceptCharset extends akka.http.scaladsl.model.HttpHeader { - public abstract Iterable getCharsetRanges(); - - public static AcceptCharset create(HttpCharsetRange... charsetRanges) { - return new akka.http.scaladsl.model.headers.Accept$minusCharset(Util.convertArray(charsetRanges)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AcceptEncoding.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AcceptEncoding.java deleted file mode 100644 index 128e9817c1..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AcceptEncoding.java +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -import akka.http.impl.util.Util; - -/** - * Model for the `Accept-Encoding` header. - * Specification: http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-26#section-5.3.4 - */ -public abstract class AcceptEncoding extends akka.http.scaladsl.model.HttpHeader { - public abstract Iterable getEncodings(); - - public static AcceptEncoding create(HttpEncoding encoding) { - return new akka.http.scaladsl.model.headers.Accept$minusEncoding(Util.convertArray(new HttpEncodingRange[]{encoding.toRange()})); - } - public static AcceptEncoding create(HttpEncodingRange... encodings) { - return new akka.http.scaladsl.model.headers.Accept$minusEncoding(Util.convertArray(encodings)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AcceptLanguage.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AcceptLanguage.java deleted file mode 100644 index 1ff9c5cbb2..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AcceptLanguage.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `Accept-Language` header. - * Specification: http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-26#section-5.3.5 - */ -public abstract class AcceptLanguage extends akka.http.scaladsl.model.HttpHeader { - public abstract Iterable getLanguages(); - - public static AcceptLanguage create(LanguageRange... languages) { - return new akka.http.scaladsl.model.headers.Accept$minusLanguage(akka.http.impl.util.Util.convertArray(languages)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AcceptRanges.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AcceptRanges.java deleted file mode 100644 index 139b7a6fc7..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AcceptRanges.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `Accept-Ranges` header. - * Specification: http://tools.ietf.org/html/draft-ietf-httpbis-p5-range-26#section-2.3 - */ -public abstract class AcceptRanges extends akka.http.scaladsl.model.HttpHeader { - public abstract Iterable getRangeUnits(); - - public static AcceptRanges create(RangeUnit... rangeUnits) { - return new akka.http.scaladsl.model.headers.Accept$minusRanges(akka.http.impl.util.Util.convertArray(rangeUnits)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AccessControlAllowCredentials.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AccessControlAllowCredentials.java deleted file mode 100644 index 98749b81c3..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AccessControlAllowCredentials.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `Access-Control-Allow-Credentials` header. - * Specification: http://www.w3.org/TR/cors/#access-control-allow-credentials-response-header - */ -public abstract class AccessControlAllowCredentials extends akka.http.scaladsl.model.HttpHeader { - public abstract boolean allow(); - - public static AccessControlAllowCredentials create(boolean allow) { - return new akka.http.scaladsl.model.headers.Access$minusControl$minusAllow$minusCredentials(allow); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AccessControlAllowHeaders.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AccessControlAllowHeaders.java deleted file mode 100644 index 15d951a5b2..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AccessControlAllowHeaders.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `Access-Control-Allow-Headers` header. - * Specification: http://www.w3.org/TR/cors/#access-control-allow-headers-response-header - */ -public abstract class AccessControlAllowHeaders extends akka.http.scaladsl.model.HttpHeader { - public abstract Iterable getHeaders(); - - public static AccessControlAllowHeaders create(String... headers) { - return new akka.http.scaladsl.model.headers.Access$minusControl$minusAllow$minusHeaders(akka.http.impl.util.Util.convertArray(headers)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AccessControlAllowMethods.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AccessControlAllowMethods.java deleted file mode 100644 index 5bf7ed35c7..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AccessControlAllowMethods.java +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -import akka.http.javadsl.model.HttpMethod; - -/** - * Model for the `Access-Control-Allow-Methods` header. - * Specification: http://www.w3.org/TR/cors/#access-control-allow-methods-response-header - */ -public abstract class AccessControlAllowMethods extends akka.http.scaladsl.model.HttpHeader { - public abstract Iterable getMethods(); - - public static AccessControlAllowMethods create(HttpMethod... methods) { - return new akka.http.scaladsl.model.headers.Access$minusControl$minusAllow$minusMethods(akka.http.impl.util.Util.convertArray(methods)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AccessControlAllowOrigin.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AccessControlAllowOrigin.java deleted file mode 100644 index 77227bbfec..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AccessControlAllowOrigin.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `Access-Control-Allow-Origin` header. - * Specification: http://www.w3.org/TR/cors/#access-control-allow-origin-response-header - */ -public abstract class AccessControlAllowOrigin extends akka.http.scaladsl.model.HttpHeader { - public abstract HttpOriginRange range(); - - public static AccessControlAllowOrigin create(HttpOriginRange range) { - return new akka.http.scaladsl.model.headers.Access$minusControl$minusAllow$minusOrigin(((akka.http.scaladsl.model.headers.HttpOriginRange) range)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AccessControlExposeHeaders.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AccessControlExposeHeaders.java deleted file mode 100644 index 53e19623d8..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AccessControlExposeHeaders.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `Access-Control-Expose-Headers` header. - * Specification: http://www.w3.org/TR/cors/#access-control-expose-headers-response-header - */ -public abstract class AccessControlExposeHeaders extends akka.http.scaladsl.model.HttpHeader { - public abstract Iterable getHeaders(); - - public static AccessControlExposeHeaders create(String... headers) { - return new akka.http.scaladsl.model.headers.Access$minusControl$minusExpose$minusHeaders(akka.http.impl.util.Util.convertArray(headers)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AccessControlMaxAge.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AccessControlMaxAge.java deleted file mode 100644 index ab4077c2af..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AccessControlMaxAge.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `Access-Control-Max-Age` header. - * Specification: http://www.w3.org/TR/cors/#access-control-max-age-response-header - */ -public abstract class AccessControlMaxAge extends akka.http.scaladsl.model.HttpHeader { - public abstract long deltaSeconds(); - - public static AccessControlMaxAge create(long deltaSeconds) { - return new akka.http.scaladsl.model.headers.Access$minusControl$minusMax$minusAge(deltaSeconds); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AccessControlRequestHeaders.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AccessControlRequestHeaders.java deleted file mode 100644 index 23a1ed5ae7..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AccessControlRequestHeaders.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `Access-Control-Request-Headers` header. - * Specification: http://www.w3.org/TR/cors/#access-control-request-headers-request-header - */ -public abstract class AccessControlRequestHeaders extends akka.http.scaladsl.model.HttpHeader { - public abstract Iterable getHeaders(); - - public static AccessControlRequestHeaders create(String... headers) { - return new akka.http.scaladsl.model.headers.Access$minusControl$minusRequest$minusHeaders(akka.http.impl.util.Util.convertArray(headers)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AccessControlRequestMethod.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AccessControlRequestMethod.java deleted file mode 100644 index 01adf9765e..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/AccessControlRequestMethod.java +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -import akka.http.javadsl.model.HttpMethod; - -/** - * Model for the `Access-Control-Request-Method` header. - * Specification: http://www.w3.org/TR/cors/#access-control-request-method-request-header - */ -public abstract class AccessControlRequestMethod extends akka.http.scaladsl.model.HttpHeader { - public abstract HttpMethod method(); - - public static AccessControlRequestMethod create(HttpMethod method) { - return new akka.http.scaladsl.model.headers.Access$minusControl$minusRequest$minusMethod(((akka.http.scaladsl.model.HttpMethod) method)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Age.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Age.java deleted file mode 100644 index 251d85e3bc..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Age.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `Age` header. - * Specification: http://tools.ietf.org/html/rfc7234#section-5.1 - */ -public abstract class Age extends akka.http.scaladsl.model.HttpHeader { - public abstract long deltaSeconds(); - - public static Age create(long deltaSeconds) { - return new akka.http.scaladsl.model.headers.Age(deltaSeconds); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Allow.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Allow.java deleted file mode 100644 index e4ff40efac..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Allow.java +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -import akka.http.javadsl.model.HttpMethod; - -/** - * Model for the `Allow` header. - * Specification: http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-26#section-7.4.1 - */ -public abstract class Allow extends akka.http.scaladsl.model.HttpHeader { - public abstract Iterable getMethods(); - - public static Allow create(HttpMethod... methods) { - return new akka.http.scaladsl.model.headers.Allow(akka.http.impl.util.Util.convertArray(methods)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Authorization.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Authorization.java deleted file mode 100644 index 053c993933..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Authorization.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `Authorization` header. - * Specification: http://tools.ietf.org/html/draft-ietf-httpbis-p7-auth-26#section-4.2 - */ -public abstract class Authorization extends akka.http.scaladsl.model.HttpHeader { - public abstract HttpCredentials credentials(); - - public static Authorization create(HttpCredentials credentials) { - return new akka.http.scaladsl.model.headers.Authorization(((akka.http.scaladsl.model.headers.HttpCredentials) credentials)); - } - public static Authorization basic(String username, String password) { - return create(HttpCredentials.createBasicHttpCredentials(username, password)); - } - public static Authorization oauth2(String token) { - return create(HttpCredentials.createOAuth2BearerToken(token)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/BasicHttpCredentials.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/BasicHttpCredentials.java deleted file mode 100644 index c014ba0475..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/BasicHttpCredentials.java +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -public abstract class BasicHttpCredentials extends akka.http.scaladsl.model.headers.HttpCredentials { - public abstract String username(); - public abstract String password(); -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ByteRange.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ByteRange.java deleted file mode 100644 index f849b62b73..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ByteRange.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -import akka.http.scaladsl.model.headers.ByteRange$; - -import java.util.OptionalLong; - -public abstract class ByteRange { - public abstract boolean isSlice(); - public abstract boolean isFromOffset(); - public abstract boolean isSuffix(); - - public abstract OptionalLong getSliceFirst(); - public abstract OptionalLong getSliceLast(); - public abstract OptionalLong getOffset(); - public abstract OptionalLong getSuffixLength(); - - public static ByteRange createSlice(long first, long last) { - return ByteRange$.MODULE$.apply(first, last); - } - public static ByteRange createFromOffset(long offset) { - return ByteRange$.MODULE$.fromOffset(offset); - } - public static ByteRange createSuffix(long length) { - return ByteRange$.MODULE$.suffix(length); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/CacheControl.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/CacheControl.java deleted file mode 100644 index 0d7ea1b2da..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/CacheControl.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `Cache-Control` header. - * Specification: http://tools.ietf.org/html/draft-ietf-httpbis-p6-cache-26#section-5.2 - */ -public abstract class CacheControl extends akka.http.scaladsl.model.HttpHeader { - public abstract Iterable getDirectives(); - - public static CacheControl create(CacheDirective... directives) { - return new akka.http.scaladsl.model.headers.Cache$minusControl(akka.http.impl.util.Util.convertArray(directives)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/CacheDirective.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/CacheDirective.java deleted file mode 100644 index afeb68852d..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/CacheDirective.java +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** @see CacheDirectives for convenience access to often used values. */ -public interface CacheDirective { - String value(); -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/CacheDirectives.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/CacheDirectives.java deleted file mode 100644 index 69ec1afe25..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/CacheDirectives.java +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -import scala.compat.java8.OptionConverters; - -import java.util.Optional; -import java.util.OptionalLong; - -public final class CacheDirectives { - private CacheDirectives() {} - - public static CacheDirective MAX_AGE(long deltaSeconds) { - return new akka.http.scaladsl.model.headers.CacheDirectives.max$minusage(deltaSeconds); - } - public static CacheDirective MAX_STALE() { - return new akka.http.scaladsl.model.headers.CacheDirectives.max$minusstale(OptionConverters.toScala(Optional.empty())); - } - public static CacheDirective MAX_STALE(long deltaSeconds) { - return new akka.http.scaladsl.model.headers.CacheDirectives.max$minusstale(OptionConverters.toScala(OptionalLong.of(deltaSeconds))); - } - public static CacheDirective MIN_FRESH(long deltaSeconds) { - return new akka.http.scaladsl.model.headers.CacheDirectives.min$minusfresh(deltaSeconds); - } - - public static final CacheDirective NO_CACHE = akka.http.scaladsl.model.headers.CacheDirectives.no$minuscache$.MODULE$; - public static final CacheDirective NO_STORE = akka.http.scaladsl.model.headers.CacheDirectives.no$minusstore$.MODULE$; - public static final CacheDirective NO_TRANSFORM = akka.http.scaladsl.model.headers.CacheDirectives.no$minustransform$.MODULE$; - public static final CacheDirective ONLY_IF_CACHED = akka.http.scaladsl.model.headers.CacheDirectives.only$minusif$minuscached$.MODULE$; - public static final CacheDirective MUST_REVALIDATE = akka.http.scaladsl.model.headers.CacheDirectives.must$minusrevalidate$.MODULE$; - - public static CacheDirective NO_CACHE(String... fieldNames) { - return akka.http.scaladsl.model.headers.CacheDirectives.no$minuscache$.MODULE$.apply(akka.japi.Util.immutableSeq(fieldNames)); - } - public static final CacheDirective PUBLIC = akka.http.scaladsl.model.headers.CacheDirectives.getPublic(); - public static CacheDirective PRIVATE(String... fieldNames) { - return akka.http.scaladsl.model.headers.CacheDirectives.createPrivate(fieldNames); - } - public static final CacheDirective PROXY_REVALIDATE = akka.http.scaladsl.model.headers.CacheDirectives.proxy$minusrevalidate$.MODULE$; - public static CacheDirective S_MAXAGE(long deltaSeconds) { - return new akka.http.scaladsl.model.headers.CacheDirectives.s$minusmaxage(deltaSeconds); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Connection.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Connection.java deleted file mode 100644 index 429c1856fc..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Connection.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `Connection` header. - * Specification: https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.10 - */ -public abstract class Connection extends akka.http.scaladsl.model.HttpHeader { - public abstract Iterable getTokens(); - - public static Connection create(String... directives) { - return new akka.http.scaladsl.model.headers.Connection(akka.http.impl.util.Util.convertArray(directives)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ContentDisposition.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ContentDisposition.java deleted file mode 100644 index 330c7bcf51..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ContentDisposition.java +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `Content-Disposition` header. - * Specification: http://tools.ietf.org/html/rfc6266 - */ -public abstract class ContentDisposition extends akka.http.scaladsl.model.HttpHeader { - public abstract ContentDispositionType dispositionType(); - public abstract java.util.Map getParams(); - - public static ContentDisposition create(ContentDispositionType dispositionType, java.util.Map params) { - return new akka.http.scaladsl.model.headers.Content$minusDisposition(((akka.http.scaladsl.model.headers.ContentDispositionType) dispositionType), akka.http.impl.util.Util.convertMapToScala(params)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ContentDispositionType.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ContentDispositionType.java deleted file mode 100644 index daf8c63841..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ContentDispositionType.java +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * @see ContentDispositionTypes for convenience access to often used values. - */ -public interface ContentDispositionType { - String name(); -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ContentDispositionTypes.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ContentDispositionTypes.java deleted file mode 100644 index 3b8289648a..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ContentDispositionTypes.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -public final class ContentDispositionTypes { - private ContentDispositionTypes() {} - - public static final ContentDispositionType INLINE = akka.http.scaladsl.model.headers.ContentDispositionTypes.inline$.MODULE$; - public static final ContentDispositionType ATTACHMENT = akka.http.scaladsl.model.headers.ContentDispositionTypes.attachment$.MODULE$; - public static final ContentDispositionType FORM_DATA = akka.http.scaladsl.model.headers.ContentDispositionTypes.form$minusdata$.MODULE$; - - public static ContentDispositionType Ext(String name) { - return new akka.http.scaladsl.model.headers.ContentDispositionTypes.Ext(name); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ContentEncoding.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ContentEncoding.java deleted file mode 100644 index 56cbf64aa8..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ContentEncoding.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `Content-Encoding` header. - * Specification: http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-26#section-3.1.2.2 - */ -public abstract class ContentEncoding extends akka.http.scaladsl.model.HttpHeader { - public abstract Iterable getEncodings(); - - public static ContentEncoding create(HttpEncoding... encodings) { - return new akka.http.scaladsl.model.headers.Content$minusEncoding(akka.http.impl.util.Util.convertArray(encodings)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ContentLength.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ContentLength.java deleted file mode 100644 index 959f007cfa..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ContentLength.java +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (C) 2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `Content-Length` header. - * Specification: https://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-26#section-3.3.2 - */ -public abstract class ContentLength extends akka.http.scaladsl.model.HttpHeader { - public abstract long length(); -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ContentRange.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ContentRange.java deleted file mode 100644 index 62f030d5e3..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ContentRange.java +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `Content-Range` header. - * Specification: http://tools.ietf.org/html/draft-ietf-httpbis-p5-range-26#section-4.2 - */ -public abstract class ContentRange extends akka.http.scaladsl.model.HttpHeader { - public abstract RangeUnit rangeUnit(); - public abstract akka.http.javadsl.model.ContentRange contentRange(); - - public static ContentRange create(RangeUnit rangeUnit, akka.http.javadsl.model.ContentRange contentRange) { - return new akka.http.scaladsl.model.headers.Content$minusRange(((akka.http.scaladsl.model.headers.RangeUnit) rangeUnit), ((akka.http.scaladsl.model.ContentRange) contentRange)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ContentType.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ContentType.java deleted file mode 100644 index 2248d91fc2..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ContentType.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `Content-Type` header. - * Specification: http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-26#section-3.1.1.5 - */ -public abstract class ContentType extends akka.http.scaladsl.model.HttpHeader { - public abstract akka.http.javadsl.model.ContentType contentType(); - - public static ContentType create(akka.http.javadsl.model.ContentType contentType) { - return new akka.http.scaladsl.model.headers.Content$minusType(((akka.http.scaladsl.model.ContentType) contentType)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Cookie.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Cookie.java deleted file mode 100644 index 4663e6842a..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Cookie.java +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `Cookie` header. - * Specification: https://tools.ietf.org/html/rfc6265#section-4.2 - */ -public abstract class Cookie extends akka.http.scaladsl.model.HttpHeader { - public abstract Iterable getCookies(); - - public static Cookie create(HttpCookiePair... cookies) { - return new akka.http.scaladsl.model.headers.Cookie(akka.http.impl.util.Util.convertArray(cookies)); - } - public static Cookie create(String name, String value) { - return create(HttpCookiePair.create(name, value)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/CustomHeader.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/CustomHeader.java deleted file mode 100644 index d0278289ce..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/CustomHeader.java +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * The model of an HTTP header. In its most basic form headers are simple name-value pairs. Header names - * are compared in a case-insensitive way. - */ -public abstract class CustomHeader extends akka.http.scaladsl.model.HttpHeader { - public abstract String name(); - public abstract String value(); -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Date.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Date.java deleted file mode 100644 index f567f42110..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Date.java +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -import akka.http.javadsl.model.DateTime; - -/** - * Model for the `Date` header. - * Specification: http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-26#section-7.1.1.2 - */ -public abstract class Date extends akka.http.scaladsl.model.HttpHeader { - public abstract DateTime date(); - - public static Date create(DateTime date) { - return new akka.http.scaladsl.model.headers.Date(((akka.http.scaladsl.model.DateTime) date)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ETag.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ETag.java deleted file mode 100644 index cfdd719e19..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ETag.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `ETag` header. - * Specification: http://tools.ietf.org/html/draft-ietf-httpbis-p4-conditional-26#section-2.3 - */ -public abstract class ETag extends akka.http.scaladsl.model.HttpHeader { - public abstract EntityTag etag(); - - public static ETag create(EntityTag etag) { - return new akka.http.scaladsl.model.headers.ETag(((akka.http.scaladsl.model.headers.EntityTag) etag)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/EntityTag.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/EntityTag.java deleted file mode 100644 index d014f3b521..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/EntityTag.java +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -public abstract class EntityTag { - public abstract String tag(); - public abstract boolean weak(); - - public static EntityTag create(String tag, boolean weak) { - return new akka.http.scaladsl.model.headers.EntityTag(tag, weak); - } - public static boolean matchesRange(EntityTag eTag, EntityTagRange range, boolean weak) { - return akka.http.scaladsl.model.headers.EntityTag.matchesRange(eTag, range, weak); - } - public static boolean matches(EntityTag eTag, EntityTag other, boolean weak) { - return akka.http.scaladsl.model.headers.EntityTag.matches(eTag, other, weak); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/EntityTagRange.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/EntityTagRange.java deleted file mode 100644 index 4019087804..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/EntityTagRange.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -import akka.http.impl.util.Util; - -/** - * @see EntityTagRanges for convenience access to often used values. - */ -public abstract class EntityTagRange { - public static EntityTagRange create(EntityTag... tags) { - return akka.http.scaladsl.model.headers.EntityTagRange.apply(Util.convertArray(tags)); - } - - /** - * @deprecated because of troublesome initialisation order (with regards to scaladsl class implementing this class). - * In some edge cases this field could end up containing a null value. - * Will be removed in Akka 3.x, use {@link EntityTagRanges#ALL} instead. - */ - @Deprecated - // FIXME: Remove in Akka 3.0 - public static final EntityTagRange ALL = EntityTagRanges.ALL; -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/EntityTagRanges.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/EntityTagRanges.java deleted file mode 100644 index 642b0f7cec..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/EntityTagRanges.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -public final class EntityTagRanges { - private EntityTagRanges() { } - - public static final EntityTagRange ALL = akka.http.scaladsl.model.headers.EntityTagRange.$times$.MODULE$; -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Expires.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Expires.java deleted file mode 100644 index f22fcd46bc..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Expires.java +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -import akka.http.javadsl.model.DateTime; - -/** - * Model for the `Expires` header. - * Specification: http://tools.ietf.org/html/rfc7234#section-5.3 - */ -public abstract class Expires extends akka.http.scaladsl.model.HttpHeader { - public abstract DateTime date(); - - public static Expires create(DateTime date) { - return new akka.http.scaladsl.model.headers.Expires(((akka.http.scaladsl.model.DateTime) date)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Host.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Host.java deleted file mode 100644 index 269a7f6a7c..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Host.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -import java.net.InetSocketAddress; - -public abstract class Host extends akka.http.scaladsl.model.HttpHeader { - - public static Host create(InetSocketAddress address) { - return akka.http.scaladsl.model.headers.Host.apply(address); - } - - public static Host create(String host) { - return akka.http.scaladsl.model.headers.Host.apply(host); - } - - public static Host create(String host, int port) { - return akka.http.scaladsl.model.headers.Host.apply(host, port); - } - - public abstract akka.http.javadsl.model.Host host(); - public abstract int port(); -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpChallenge.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpChallenge.java deleted file mode 100644 index 2d7b15d52b..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpChallenge.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -import akka.http.impl.util.Util; -import akka.japi.Option; - - -import java.util.Map; - -public abstract class HttpChallenge { - public abstract String scheme(); - public abstract String realm(); - - public abstract Map getParams(); - - /** - * @deprecated Use constructor with optional realm parameter instead. - */ - @Deprecated - public static HttpChallenge create(String scheme, String realm) { - return akka.http.scaladsl.model.headers.HttpChallenge.apply(scheme, scala.Option.apply(realm), Util.emptyMap); - } - - /** - * @deprecated Use constructor with optional realm parameter instead. - */ - @Deprecated - public static HttpChallenge create(String scheme, String realm, Map params) { - return akka.http.scaladsl.model.headers.HttpChallenge.apply(scheme, scala.Option.apply(realm), Util.convertMapToScala(params)); - } - - public static HttpChallenge create(String scheme, Option realm) { - return akka.http.scaladsl.model.headers.HttpChallenge.apply(scheme, realm.asScala(), Util.emptyMap); - } - - public static HttpChallenge create(String scheme, Option realm, Map params) { - return akka.http.scaladsl.model.headers.HttpChallenge.apply(scheme, realm.asScala(), Util.convertMapToScala(params)); - } - - public static HttpChallenge createBasic(String realm) { - return create("Basic", realm); - } - - public static HttpChallenge createOAuth2(String realm) { - return create("Bearer", realm); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpCookie.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpCookie.java deleted file mode 100644 index c617c9b206..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpCookie.java +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -import akka.http.javadsl.model.DateTime; -import akka.http.impl.util.Util; -import scala.compat.java8.OptionConverters; - -import java.util.Optional; -import java.util.OptionalLong; - -public abstract class HttpCookie { - public abstract String name(); - public abstract String value(); - public abstract HttpCookiePair pair(); - - public abstract Optional getExpires(); - public abstract OptionalLong getMaxAge(); - public abstract Optional getDomain(); - public abstract Optional getPath(); - public abstract boolean secure(); - public abstract boolean httpOnly(); - public abstract Optional getExtension(); - - public static HttpCookie create(String name, String value) { - return new akka.http.scaladsl.model.headers.HttpCookie( - name, value, - Util.scalaNone(), Util.scalaNone(), Util.scalaNone(), Util.scalaNone(), - false, false, - Util.scalaNone()); - } - public static HttpCookie create(String name, String value, Optional domain, Optional path) { - return new akka.http.scaladsl.model.headers.HttpCookie( - name, value, - Util.scalaNone(), Util.scalaNone(), - OptionConverters.toScala(domain), OptionConverters.toScala(path), - false, false, - Util.scalaNone()); - } - @SuppressWarnings("unchecked") - public static HttpCookie create( - String name, - String value, - Optional expires, - OptionalLong maxAge, - Optional domain, - Optional path, - boolean secure, - boolean httpOnly, - Optional extension) { - return new akka.http.scaladsl.model.headers.HttpCookie( - name, value, - Util.convertOptionalToScala(expires), - OptionConverters.toScala(maxAge), - OptionConverters.toScala(domain), - OptionConverters.toScala(path), - secure, - httpOnly, - OptionConverters.toScala(extension)); - } - - /** - * Returns a copy of this HttpCookie instance with the given expiration set. - */ - public abstract HttpCookie withExpires(DateTime dateTime); - - /** - * Returns a copy of this HttpCookie instance with the given max age set. - */ - public abstract HttpCookie withMaxAge(long maxAge); - - /** - * Returns a copy of this HttpCookie instance with the given domain set. - */ - public abstract HttpCookie withDomain(String domain); - - /** - * Returns a copy of this HttpCookie instance with the given path set. - */ - public abstract HttpCookie withPath(String path); - - /** - * Returns a copy of this HttpCookie instance with the given secure flag set. - */ - public abstract HttpCookie withSecure(boolean secure); - - /** - * Returns a copy of this HttpCookie instance with the given http-only flag set. - */ - public abstract HttpCookie withHttpOnly(boolean httpOnly); - - /** - * Returns a copy of this HttpCookie instance with the given extension set. - */ - public abstract HttpCookie withExtension(String extension); -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpCookiePair.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpCookiePair.java deleted file mode 100644 index a822878951..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpCookiePair.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Represents a cookie pair as used in the `Cookie` header as specified in - * http://tools.ietf.org/search/rfc6265#section-4.2.1 - */ -public abstract class HttpCookiePair { - public abstract String name(); - public abstract String value(); - - /** - * Converts this cookie pair into an HttpCookie to be used with the - * `Set-Cookie` header. - */ - public abstract HttpCookie toCookie(); - - public static HttpCookiePair create(String name, String value) { - return akka.http.scaladsl.model.headers.HttpCookiePair.apply(name, value); - } - - public static HttpCookiePair createRaw(String name, String value) { - return akka.http.scaladsl.model.headers.HttpCookiePair.raw(name, value); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpCredentials.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpCredentials.java deleted file mode 100644 index c39d19fee8..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpCredentials.java +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -import akka.http.impl.util.Util; - -import java.util.Map; - -public abstract class HttpCredentials { - public abstract String scheme(); - public abstract String token(); - - public abstract Map getParams(); - - public static HttpCredentials create(String scheme, String token) { - return new akka.http.scaladsl.model.headers.GenericHttpCredentials(scheme, token, Util.emptyMap); - } - public static HttpCredentials create(String scheme, String token, Map params) { - return new akka.http.scaladsl.model.headers.GenericHttpCredentials(scheme, token, Util.convertMapToScala(params)); - } - public static BasicHttpCredentials createBasicHttpCredentials(String username, String password) { - return new akka.http.scaladsl.model.headers.BasicHttpCredentials(username, password); - } - public static OAuth2BearerToken createOAuth2BearerToken(String token) { - return new akka.http.scaladsl.model.headers.OAuth2BearerToken(token); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpEncoding.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpEncoding.java deleted file mode 100644 index 0997df5ec1..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpEncoding.java +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -public abstract class HttpEncoding { - public abstract String value(); - - public HttpEncodingRange toRange() { - return HttpEncodingRange.create(this); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpEncodingRange.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpEncodingRange.java deleted file mode 100644 index 225b19bbec..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpEncodingRange.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -import akka.http.scaladsl.model.headers.HttpEncodingRange$; - -/** - * @see HttpEncodingRanges for convenience access to often used values. - */ -public abstract class HttpEncodingRange { - public abstract float qValue(); - public abstract boolean matches(HttpEncoding encoding); - - public abstract HttpEncodingRange withQValue(float qValue); - - public static HttpEncodingRange create(HttpEncoding encoding) { - return HttpEncodingRange$.MODULE$.apply((akka.http.scaladsl.model.headers.HttpEncoding) encoding); - } - - /** - * @deprecated because of troublesome initialisation order (with regards to scaladsl class implementing this class). - * In some edge cases this field could end up containing a null value. - * Will be removed in Akka 3.x, use {@link HttpEncodingRanges#ALL} instead. - */ - @Deprecated - // FIXME: Remove in Akka 3.0 - public static final HttpEncodingRange ALL = HttpEncodingRanges.ALL; -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpEncodingRanges.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpEncodingRanges.java deleted file mode 100644 index 300677cda4..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpEncodingRanges.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -public final class HttpEncodingRanges { - private HttpEncodingRanges() { } - - public static final HttpEncodingRange ALL = akka.http.scaladsl.model.headers.HttpEncodingRange.$times$.MODULE$; -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpEncodings.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpEncodings.java deleted file mode 100644 index b28ab4a8e9..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpEncodings.java +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -public final class HttpEncodings { - private HttpEncodings() {} - - public static final HttpEncoding CHUNKED = akka.http.scaladsl.model.headers.HttpEncodings.chunked(); - public static final HttpEncoding COMPRESS = akka.http.scaladsl.model.headers.HttpEncodings.compress(); - public static final HttpEncoding DEFLATE = akka.http.scaladsl.model.headers.HttpEncodings.deflate(); - public static final HttpEncoding GZIP = akka.http.scaladsl.model.headers.HttpEncodings.gzip(); - public static final HttpEncoding IDENTITY = akka.http.scaladsl.model.headers.HttpEncodings.identity(); - public static final HttpEncoding X_COMPRESS = akka.http.scaladsl.model.headers.HttpEncodings.x$minuscompress(); - public static final HttpEncoding X_ZIP = akka.http.scaladsl.model.headers.HttpEncodings.x$minuszip(); -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpOrigin.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpOrigin.java deleted file mode 100644 index 6b5b0a65a9..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpOrigin.java +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -import akka.http.scaladsl.model.headers.HttpOrigin$; - -public abstract class HttpOrigin { - public abstract String scheme(); - public abstract Host host(); - - public static HttpOrigin create(String scheme, Host host) { - return new akka.http.scaladsl.model.headers.HttpOrigin(scheme, (akka.http.scaladsl.model.headers.Host) host); - } - public static HttpOrigin parse(String originString) { - return HttpOrigin$.MODULE$.apply(originString); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpOriginRange.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpOriginRange.java deleted file mode 100644 index 70a949883f..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpOriginRange.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -import akka.http.scaladsl.model.headers.HttpOriginRange$; -import akka.http.impl.util.Util; - -/** - * @see HttpOriginRanges for convenience access to often used values. - */ -public abstract class HttpOriginRange { - public abstract boolean matches(HttpOrigin origin); - - public static HttpOriginRange create(HttpOrigin... origins) { - return HttpOriginRange$.MODULE$.apply(Util.convertArray(origins)); - } - - /** - * @deprecated because of troublesome initialisation order (with regards to scaladsl class implementing this class). - * In some edge cases this field could end up containing a null value. - * Will be removed in Akka 3.x, use {@link HttpEncodingRanges#ALL} instead. - */ - @Deprecated - // FIXME: Remove in Akka 3.0 - public static final HttpOriginRange ALL = HttpOriginRanges.ALL; -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpOriginRanges.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpOriginRanges.java deleted file mode 100644 index a397d5eaf4..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/HttpOriginRanges.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -public final class HttpOriginRanges { - private HttpOriginRanges() { } - - public static final HttpOriginRange ALL = akka.http.scaladsl.model.headers.HttpOriginRange.$times$.MODULE$; -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/IfMatch.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/IfMatch.java deleted file mode 100644 index 40264ec64a..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/IfMatch.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `If-Match` header. - * Specification: http://tools.ietf.org/html/draft-ietf-httpbis-p4-conditional-26#section-3.1 - */ -public abstract class IfMatch extends akka.http.scaladsl.model.HttpHeader { - public abstract EntityTagRange m(); - - public static IfMatch create(EntityTagRange m) { - return new akka.http.scaladsl.model.headers.If$minusMatch(((akka.http.scaladsl.model.headers.EntityTagRange) m)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/IfModifiedSince.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/IfModifiedSince.java deleted file mode 100644 index 74481a37b3..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/IfModifiedSince.java +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -import akka.http.javadsl.model.DateTime; - -/** - * Model for the `If-Modified-Since` header. - * Specification: http://tools.ietf.org/html/draft-ietf-httpbis-p4-conditional-26#section-3.3 - */ -public abstract class IfModifiedSince extends akka.http.scaladsl.model.HttpHeader { - public abstract DateTime date(); - - public static IfModifiedSince create(DateTime date) { - return new akka.http.scaladsl.model.headers.If$minusModified$minusSince(((akka.http.scaladsl.model.DateTime) date)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/IfNoneMatch.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/IfNoneMatch.java deleted file mode 100644 index b5e521c77e..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/IfNoneMatch.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `If-None-Match` header. - * Specification: http://tools.ietf.org/html/draft-ietf-httpbis-p4-conditional-26#section-3.2 - */ -public abstract class IfNoneMatch extends akka.http.scaladsl.model.HttpHeader { - public abstract EntityTagRange m(); - - public static IfNoneMatch create(EntityTagRange m) { - return new akka.http.scaladsl.model.headers.If$minusNone$minusMatch(((akka.http.scaladsl.model.headers.EntityTagRange) m)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/IfUnmodifiedSince.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/IfUnmodifiedSince.java deleted file mode 100644 index 76ab972823..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/IfUnmodifiedSince.java +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -import akka.http.javadsl.model.DateTime; - -/** - * Model for the `If-Unmodified-Since` header. - * Specification: http://tools.ietf.org/html/draft-ietf-httpbis-p4-conditional-26#section-3.4 - */ -public abstract class IfUnmodifiedSince extends akka.http.scaladsl.model.HttpHeader { - public abstract DateTime date(); - - public static IfUnmodifiedSince create(DateTime date) { - return new akka.http.scaladsl.model.headers.If$minusUnmodified$minusSince(((akka.http.scaladsl.model.DateTime) date)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Language.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Language.java deleted file mode 100644 index 1a8cd25ae1..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Language.java +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -import akka.http.impl.util.Util; -import akka.http.scaladsl.model.headers.Language$; - -public abstract class Language { - public static Language create(String primaryTag, String... subTags) { - return Language$.MODULE$.apply(primaryTag, Util.convertArray(subTags)); - } - - public abstract String primaryTag(); - public abstract Iterable getSubTags(); - public abstract LanguageRange withQValue(float qValue); -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/LanguageRange.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/LanguageRange.java deleted file mode 100644 index 6885e73dbb..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/LanguageRange.java +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -public interface LanguageRange { - public abstract String primaryTag(); - public abstract float qValue(); - public abstract boolean matches(Language language); - public abstract Iterable getSubTags(); - public abstract LanguageRange withQValue(float qValue); - - /** - * @deprecated because of troublesome initialisation order (with regards to scaladsl class implementing this class). - * In some edge cases this field could end up containing a null value. - * Will be removed in Akka 3.x, use {@link LanguageRanges#ALL} instead. - */ - @Deprecated - // FIXME: Remove in Akka 3.0 - public static final LanguageRange ALL = LanguageRanges.ALL; -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/LanguageRanges.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/LanguageRanges.java deleted file mode 100644 index 963852ac58..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/LanguageRanges.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -public class LanguageRanges { - private LanguageRanges() { } - - public static final LanguageRange ALL = akka.http.scaladsl.model.headers.LanguageRange.$times$.MODULE$; -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/LastModified.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/LastModified.java deleted file mode 100644 index 2346f05e46..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/LastModified.java +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -import akka.http.javadsl.model.DateTime; - -/** - * Model for the `Last-Modified` header. - * Specification: http://tools.ietf.org/html/draft-ietf-httpbis-p4-conditional-26#section-2.2 - */ -public abstract class LastModified extends akka.http.scaladsl.model.HttpHeader { - public abstract DateTime date(); - - public static LastModified create(DateTime date) { - return new akka.http.scaladsl.model.headers.Last$minusModified(((akka.http.scaladsl.model.DateTime) date)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Link.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Link.java deleted file mode 100644 index 8252796bba..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Link.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `Link` header. - * Specification: http://tools.ietf.org/html/rfc5988#section-5 - */ -public abstract class Link extends akka.http.scaladsl.model.HttpHeader { - public abstract Iterable getValues(); - - public static Link create(LinkValue... values) { - return new akka.http.scaladsl.model.headers.Link(akka.http.impl.util.Util.convertArray(values)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/LinkParam.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/LinkParam.java deleted file mode 100644 index 2c426d740a..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/LinkParam.java +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -public abstract class LinkParam { - public abstract String key(); - public abstract Object value(); -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/LinkParams.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/LinkParams.java deleted file mode 100644 index cfa4b2b4e3..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/LinkParams.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -import akka.http.javadsl.model.MediaType; -import akka.http.javadsl.model.Uri; -import akka.http.impl.util.Util; - -public final class LinkParams { - private LinkParams() {} - - public static final LinkParam next = akka.http.scaladsl.model.headers.LinkParams.next(); - public static final LinkParam prev = akka.http.scaladsl.model.headers.LinkParams.prev(); - public static final LinkParam first = akka.http.scaladsl.model.headers.LinkParams.first(); - public static final LinkParam last = akka.http.scaladsl.model.headers.LinkParams.last(); - - public static LinkParam rel(String value) { - return new akka.http.scaladsl.model.headers.LinkParams.rel(value); - } - public static LinkParam anchor(Uri uri) { - return new akka.http.scaladsl.model.headers.LinkParams.anchor(Util.convertUriToScala(uri)); - } - public static LinkParam rev(String value) { - return new akka.http.scaladsl.model.headers.LinkParams.rev(value); - } - public static LinkParam hreflang(Language language) { - return new akka.http.scaladsl.model.headers.LinkParams.hreflang((akka.http.scaladsl.model.headers.Language) language); - } - public static LinkParam media(String desc) { - return new akka.http.scaladsl.model.headers.LinkParams.media(desc); - } - public static LinkParam title(String title) { - return new akka.http.scaladsl.model.headers.LinkParams.title(title); - } - public static LinkParam title_All(String title) { - return new akka.http.scaladsl.model.headers.LinkParams.title$times(title); - } - public static LinkParam type(MediaType type) { - return new akka.http.scaladsl.model.headers.LinkParams.type((akka.http.scaladsl.model.MediaType) type); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/LinkValue.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/LinkValue.java deleted file mode 100644 index 90ed2291bf..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/LinkValue.java +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -import akka.http.javadsl.model.Uri; -import akka.http.impl.util.Util; - -public abstract class LinkValue { - public abstract Uri getUri(); - public abstract Iterable getParams(); - - public static LinkValue create(Uri uri, LinkParam... params) { - return new akka.http.scaladsl.model.headers.LinkValue( - Util.convertUriToScala(uri), - Util.convertArray(params)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Location.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Location.java deleted file mode 100644 index 65a146d4cb..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Location.java +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -import akka.http.javadsl.model.Uri; - -/** - * Model for the `Location` header. - * Specification: http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-26#section-7.1.2 - */ -public abstract class Location extends akka.http.scaladsl.model.HttpHeader { - public abstract Uri getUri(); - - public static Location create(Uri uri) { - return new akka.http.scaladsl.model.headers.Location(akka.http.impl.util.Util.convertUriToScala(uri)); - } - public static Location create(String uri) { - return create(Uri.create(uri)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/OAuth2BearerToken.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/OAuth2BearerToken.java deleted file mode 100644 index b3c6d250e3..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/OAuth2BearerToken.java +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -public abstract class OAuth2BearerToken extends akka.http.scaladsl.model.headers.HttpCredentials { - public abstract String token(); -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Origin.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Origin.java deleted file mode 100644 index 5b54dfb2ef..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Origin.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `Origin` header. - * Specification: http://tools.ietf.org/html/rfc6454#section-7 - */ -public abstract class Origin extends akka.http.scaladsl.model.HttpHeader { - public abstract Iterable getOrigins(); - - public static Origin create(HttpOrigin... origins) { - return new akka.http.scaladsl.model.headers.Origin(akka.http.impl.util.Util.convertArray(origins)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ProductVersion.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ProductVersion.java deleted file mode 100644 index 301565a0b6..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ProductVersion.java +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -public abstract class ProductVersion { - public abstract String product(); - public abstract String version(); - public abstract String comment(); - - public static ProductVersion create(String product, String version, String comment) { - return new akka.http.scaladsl.model.headers.ProductVersion(product, version, comment); - } - public static ProductVersion create(String product, String version) { - return create(product, version, ""); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ProxyAuthenticate.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ProxyAuthenticate.java deleted file mode 100644 index 6934de4650..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ProxyAuthenticate.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `Proxy-Authenticate` header. - * Specification: http://tools.ietf.org/html/draft-ietf-httpbis-p7-auth-26#section-4.3 - */ -public abstract class ProxyAuthenticate extends akka.http.scaladsl.model.HttpHeader { - public abstract Iterable getChallenges(); - - public static ProxyAuthenticate create(HttpChallenge... challenges) { - return new akka.http.scaladsl.model.headers.Proxy$minusAuthenticate(akka.http.impl.util.Util.convertArray(challenges)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ProxyAuthorization.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ProxyAuthorization.java deleted file mode 100644 index f3a9c0c356..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/ProxyAuthorization.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `Proxy-Authorization` header. - * Specification: http://tools.ietf.org/html/draft-ietf-httpbis-p7-auth-26#section-4.4 - */ -public abstract class ProxyAuthorization extends akka.http.scaladsl.model.HttpHeader { - public abstract HttpCredentials credentials(); - - public static ProxyAuthorization create(HttpCredentials credentials) { - return new akka.http.scaladsl.model.headers.Proxy$minusAuthorization(((akka.http.scaladsl.model.headers.HttpCredentials) credentials)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Range.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Range.java deleted file mode 100644 index c0410d5553..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Range.java +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `Range` header. - * Specification: http://tools.ietf.org/html/draft-ietf-httpbis-p5-range-26#section-3.1 - */ -public abstract class Range extends akka.http.scaladsl.model.HttpHeader { - public abstract RangeUnit rangeUnit(); - public abstract Iterable getRanges(); - - public static Range create(RangeUnit rangeUnit, ByteRange... ranges) { - return new akka.http.scaladsl.model.headers.Range(((akka.http.scaladsl.model.headers.RangeUnit) rangeUnit), akka.http.impl.util.Util.convertArray(ranges)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/RangeUnit.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/RangeUnit.java deleted file mode 100644 index 2cff6bde85..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/RangeUnit.java +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -public abstract class RangeUnit { - public abstract String name(); - - public static RangeUnit create(String name) { - return new akka.http.scaladsl.model.headers.RangeUnits.Other(name); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/RangeUnits.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/RangeUnits.java deleted file mode 100644 index 8f55775d9b..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/RangeUnits.java +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -public final class RangeUnits { - private RangeUnits() {} - - public static final RangeUnit BYTES = akka.http.scaladsl.model.headers.RangeUnits.Bytes$.MODULE$; -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/RawHeader.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/RawHeader.java deleted file mode 100644 index 57bf273667..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/RawHeader.java +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -public abstract class RawHeader extends akka.http.scaladsl.model.HttpHeader { - public abstract String name(); - public abstract String value(); - - public static RawHeader create(String name, String value) { - return new akka.http.scaladsl.model.headers.RawHeader(name, value); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/RawRequestURI.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/RawRequestURI.java deleted file mode 100644 index 35b1176bbd..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/RawRequestURI.java +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `Raw-Request-URI` header. - * Custom header we use for transporting the raw request URI either to the application (server-side) - * or to the request rendering stage (client-side). - */ -public abstract class RawRequestURI extends akka.http.scaladsl.model.HttpHeader { - public abstract String uri(); - - public static RawRequestURI create(String uri) { - return new akka.http.scaladsl.model.headers.Raw$minusRequest$minusURI(uri); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Referer.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Referer.java deleted file mode 100644 index 5adee7f0f7..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Referer.java +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -import akka.http.javadsl.model.Uri; - -/** - * Model for the `Referer` header. - * Specification: http://tools.ietf.org/html/rfc7231#section-5.5.2 - */ -public abstract class Referer extends akka.http.scaladsl.model.HttpHeader { - public abstract Uri getUri(); - - public static Referer create(Uri uri) { - return new akka.http.scaladsl.model.headers.Referer(akka.http.impl.util.Util.convertUriToScala(uri)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/RemoteAddress.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/RemoteAddress.java deleted file mode 100644 index 0749a26894..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/RemoteAddress.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `Remote-Address` header. - * Custom header we use for optionally transporting the peer's IP in an HTTP header. - */ -public abstract class RemoteAddress extends akka.http.scaladsl.model.HttpHeader { - public abstract akka.http.javadsl.model.RemoteAddress address(); - - public static RemoteAddress create(akka.http.javadsl.model.RemoteAddress address) { - return new akka.http.scaladsl.model.headers.Remote$minusAddress(((akka.http.scaladsl.model.RemoteAddress) address)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/SecWebSocketProtocol.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/SecWebSocketProtocol.java deleted file mode 100644 index 2683e5a45b..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/SecWebSocketProtocol.java +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (C) 2016-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -import akka.http.impl.util.Util; - -/** - * Model for the `Sec-WebSocket-Protocol` header. - */ -public abstract class SecWebSocketProtocol extends akka.http.scaladsl.model.HttpHeader { - public abstract Iterable getProtocols(); - - public static SecWebSocketProtocol create(String... protocols) { - return new akka.http.scaladsl.model.headers.Sec$minusWebSocket$minusProtocol(Util.convertArray(protocols)); - } - -} - diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Server.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Server.java deleted file mode 100644 index d0e5073fa4..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Server.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `Server` header. - * Specification: http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-26#section-7.4.2 - */ -public abstract class Server extends akka.http.scaladsl.model.HttpHeader { - public abstract Iterable getProducts(); - - public static Server create(ProductVersion... products) { - return new akka.http.scaladsl.model.headers.Server(akka.http.impl.util.Util.convertArray(products)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/SetCookie.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/SetCookie.java deleted file mode 100644 index b1a41a6017..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/SetCookie.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `Set-Cookie` header. - * Specification: https://tools.ietf.org/html/rfc6265 - */ -public abstract class SetCookie extends akka.http.scaladsl.model.HttpHeader { - public abstract HttpCookie cookie(); - - public static SetCookie create(HttpCookie cookie) { - return new akka.http.scaladsl.model.headers.Set$minusCookie(((akka.http.scaladsl.model.headers.HttpCookie) cookie)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/StrictTransportSecurity.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/StrictTransportSecurity.java deleted file mode 100644 index c0322d9c7d..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/StrictTransportSecurity.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `Strict-Transport-Security` header. - * Specification: https://tools.ietf.org/html/rfc6797 - */ -public abstract class StrictTransportSecurity extends akka.http.scaladsl.model.HttpHeader { - public abstract long maxAge(); - public abstract boolean includeSubDomains(); - - public static StrictTransportSecurity create(long maxAge) { - return new akka.http.scaladsl.model.headers.Strict$minusTransport$minusSecurity(maxAge, false); - } - public static StrictTransportSecurity create(long maxAge, boolean includeSubDomains) { - return new akka.http.scaladsl.model.headers.Strict$minusTransport$minusSecurity(maxAge, includeSubDomains); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/TimeoutAccess.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/TimeoutAccess.java deleted file mode 100644 index d198d77e2e..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/TimeoutAccess.java +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the synthetic `Timeout-Access` header. - */ -public abstract class TimeoutAccess extends akka.http.scaladsl.model.HttpHeader { - public abstract akka.http.javadsl.TimeoutAccess timeoutAccess(); - - public static TimeoutAccess create(akka.http.javadsl.TimeoutAccess timeoutAccess) { - return new akka.http.scaladsl.model.headers.Timeout$minusAccess((akka.http.scaladsl.TimeoutAccess) timeoutAccess); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/TlsSessionInfo.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/TlsSessionInfo.java deleted file mode 100644 index caf2a82e3d..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/TlsSessionInfo.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -import javax.net.ssl.SSLSession; - -/** - * Model for the synthetic `Tls-Session-Info` header which carries the SSLSession of the connection - * the message carrying this header was received with. - * - * This header will only be added if it enabled in the configuration by setting - * akka.http.[client|server].parsing.tls-session-info-header = on. - */ -public abstract class TlsSessionInfo extends CustomHeader { - /** - * @return the SSLSession this message was received over. - */ - public abstract SSLSession getSession(); - - public static TlsSessionInfo create(SSLSession session) { - return akka.http.scaladsl.model.headers.Tls$minusSession$minusInfo$.MODULE$.apply(session); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/TransferEncoding.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/TransferEncoding.java deleted file mode 100644 index 5974e18804..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/TransferEncoding.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `Transfer-Encoding` header. - * Specification: http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-26#section-3.3.1 - */ -public abstract class TransferEncoding extends akka.http.scaladsl.model.HttpHeader { - public abstract Iterable getEncodings(); - - public static TransferEncoding create(akka.http.javadsl.model.TransferEncoding... encodings) { - return new akka.http.scaladsl.model.headers.Transfer$minusEncoding(akka.http.impl.util.Util.convertArray(encodings)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/UserAgent.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/UserAgent.java deleted file mode 100644 index d534bac9ab..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/UserAgent.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `User-Agent` header. - * Specification: http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-26#section-5.5.3 - */ -public abstract class UserAgent extends akka.http.scaladsl.model.HttpHeader { - public abstract Iterable getProducts(); - - public static UserAgent create(ProductVersion... products) { - return new akka.http.scaladsl.model.headers.User$minusAgent(akka.http.impl.util.Util.convertArray(products)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/WWWAuthenticate.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/WWWAuthenticate.java deleted file mode 100644 index 2ba9edeaf6..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/WWWAuthenticate.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `WWW-Authenticate` header. - * Specification: http://tools.ietf.org/html/draft-ietf-httpbis-p7-auth-26#section-4.1 - */ -public abstract class WWWAuthenticate extends akka.http.scaladsl.model.HttpHeader { - public abstract Iterable getChallenges(); - - public static WWWAuthenticate create(HttpChallenge... challenges) { - return new akka.http.scaladsl.model.headers.WWW$minusAuthenticate(akka.http.impl.util.Util.convertArray(challenges)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/XForwardedFor.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/XForwardedFor.java deleted file mode 100644 index 3f862202e9..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/XForwardedFor.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `X-Forwarded-For` header. - * Specification: http://en.wikipedia.org/wiki/X-Forwarded-For - */ -public abstract class XForwardedFor extends akka.http.scaladsl.model.HttpHeader { - public abstract Iterable getAddresses(); - - public static XForwardedFor create(akka.http.javadsl.model.RemoteAddress... addresses) { - return new akka.http.scaladsl.model.headers.X$minusForwarded$minusFor(akka.http.impl.util.Util.convertArray(addresses)); - } -} diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/XRealIp.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/XRealIp.java deleted file mode 100644 index a68935b11d..0000000000 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/XRealIp.java +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.headers; - -/** - * Model for the `X-Real-Ip` header. - */ -public abstract class XRealIp extends akka.http.scaladsl.model.HttpHeader { - public abstract akka.http.javadsl.model.RemoteAddress address(); - - public static XRealIp create(akka.http.javadsl.model.RemoteAddress address) { - return new akka.http.scaladsl.model.headers.X$minusReal$minusIp(((akka.http.scaladsl.model.RemoteAddress) address)); - } -} diff --git a/akka-http-core/src/main/resources/reference.conf b/akka-http-core/src/main/resources/reference.conf deleted file mode 100644 index 13f004ed07..0000000000 --- a/akka-http-core/src/main/resources/reference.conf +++ /dev/null @@ -1,364 +0,0 @@ -######################################## -# akka-http-core Reference Config File # -######################################## - -# This is the reference config file that contains all the default settings. -# Make your edits/overrides in your application.conf. - -akka.http { - - server { - # The default value of the `Server` header to produce if no - # explicit `Server`-header was included in a response. - # If this value is the empty string and no header was included in - # the request, no `Server` header will be rendered at all. - server-header = akka-http/${akka.version} - - # The time after which an idle connection will be automatically closed. - # Set to `infinite` to completely disable idle connection timeouts. - idle-timeout = 60 s - - # Defines the default time period within which the application has to - # produce an HttpResponse for any given HttpRequest it received. - # The timeout begins to run when the *end* of the request has been - # received, so even potentially long uploads can have a short timeout. - # Set to `infinite` to completely disable request timeout checking. - # - # If this setting is not `infinite` the HTTP server layer attaches a - # `Timeout-Access` header to the request, which enables programmatic - # customization of the timeout period and timeout response for each - # request individually. - request-timeout = 20 s - - # The time period within which the TCP binding process must be completed. - bind-timeout = 1s - - # The maximum number of concurrently accepted connections when using the - # `Http().bindAndHandle` methods. - # - # This setting doesn't apply to the `Http().bind` method which will still - # deliver an unlimited backpressured stream of incoming connections. - max-connections = 1024 - - # The maximum number of requests that are accepted (and dispatched to - # the application) on one single connection before the first request - # has to be completed. - # Incoming requests that would cause the pipelining limit to be exceeded - # are not read from the connections socket so as to build up "back-pressure" - # to the client via TCP flow control. - # A setting of 1 disables HTTP pipelining, since only one request per - # connection can be "open" (i.e. being processed by the application) at any - # time. Set to higher values to enable HTTP pipelining. - # This value must be > 0 and <= 1024. - pipelining-limit = 16 - - # Enables/disables the addition of a `Remote-Address` header - # holding the clients (remote) IP address. - remote-address-header = off - - # Enables/disables the addition of a `Raw-Request-URI` header holding the - # original raw request URI as the client has sent it. - raw-request-uri-header = off - - # Enables/disables automatic handling of HEAD requests. - # If this setting is enabled the server dispatches HEAD requests as GET - # requests to the application and automatically strips off all message - # bodies from outgoing responses. - # Note that, even when this setting is off the server will never send - # out message bodies on responses to HEAD requests. - transparent-head-requests = on - - # Enables/disables the returning of more detailed error messages to - # the client in the error response. - # Should be disabled for browser-facing APIs due to the risk of XSS attacks - # and (probably) enabled for internal or non-browser APIs. - # Note that akka-http will always produce log messages containing the full - # error details. - verbose-error-messages = off - - # The initial size of the buffer to render the response headers in. - # Can be used for fine-tuning response rendering performance but probably - # doesn't have to be fiddled with in most applications. - response-header-size-hint = 512 - - # The requested maximum length of the queue of incoming connections. - # If the server is busy and the backlog is full the OS will start dropping - # SYN-packets and connection attempts may fail. Note, that the backlog - # size is usually only a maximum size hint for the OS and the OS can - # restrict the number further based on global limits. - backlog = 100 - - # If this setting is empty the server only accepts requests that carry a - # non-empty `Host` header. Otherwise it responds with `400 Bad Request`. - # Set to a non-empty value to be used in lieu of a missing or empty `Host` - # header to make the server accept such requests. - # Note that the server will never accept HTTP/1.1 request without a `Host` - # header, i.e. this setting only affects HTTP/1.1 requests with an empty - # `Host` header as well as HTTP/1.0 requests. - # Examples: `www.spray.io` or `example.com:8080` - default-host-header = "" - - # Socket options to set for the listening socket. If a setting is left - # undefined, it will use whatever the default on the system is. - socket-options { - so-receive-buffer-size = undefined - so-send-buffer-size = undefined - so-reuse-address = undefined - so-traffic-class = undefined - tcp-keep-alive = undefined - tcp-oob-inline = undefined - tcp-no-delay = undefined - } - - # Modify to tweak parsing settings on the server-side only. - parsing { - # no overrides by default, see `akka.http.parsing` for default values - } - } - - client { - # The default value of the `User-Agent` header to produce if no - # explicit `User-Agent`-header was included in a request. - # If this value is the empty string and no header was included in - # the request, no `User-Agent` header will be rendered at all. - user-agent-header = akka-http/${akka.version} - - # The time period within which the TCP connecting process must be completed. - connecting-timeout = 10s - - # The time after which an idle connection will be automatically closed. - # Set to `infinite` to completely disable idle timeouts. - idle-timeout = 60 s - - # The initial size of the buffer to render the request headers in. - # Can be used for fine-tuning request rendering performance but probably - # doesn't have to be fiddled with in most applications. - request-header-size-hint = 512 - - # Socket options to set for the listening socket. If a setting is left - # undefined, it will use whatever the default on the system is. - socket-options { - so-receive-buffer-size = undefined - so-send-buffer-size = undefined - so-reuse-address = undefined - so-traffic-class = undefined - tcp-keep-alive = undefined - tcp-oob-inline = undefined - tcp-no-delay = undefined - } - - # Modify to tweak parsing settings on the client-side only. - parsing { - # no overrides by default, see `akka.http.parsing` for default values - } - } - - host-connection-pool { - # The maximum number of parallel connections that a connection pool to a - # single host endpoint is allowed to establish. Must be greater than zero. - max-connections = 4 - - # The minimum number of parallel connections that a pool should keep alive ("hot"). - # If the number of connections is falling below the given threshold, new ones are being spawned. - # You can use this setting to build a hot pool of "always on" connections. - # Default is 0, meaning there might be no active connection at given moment. - # Keep in mind that `min-connections` should be smaller than `max-connections` or equal - min-connections = 0 - - # The maximum number of times failed requests are attempted again, - # (if the request can be safely retried) before giving up and returning an error. - # Set to zero to completely disable request retries. - max-retries = 5 - - # The maximum number of open requests accepted into the pool across all - # materializations of any of its client flows. - # Protects against (accidentally) overloading a single pool with too many client flow materializations. - # Note that with N concurrent materializations the max number of open request in the pool - # will never exceed N * max-connections * pipelining-limit. - # Must be a power of 2 and > 0! - max-open-requests = 32 - - # The maximum number of requests that are dispatched to the target host in - # batch-mode across a single connection (HTTP pipelining). - # A setting of 1 disables HTTP pipelining, since only one request per - # connection can be "in flight" at any time. - # Set to higher values to enable HTTP pipelining. - # This value must be > 0. - # (Note that, independently of this setting, pipelining will never be done - # on a connection that still has a non-idempotent request in flight. - # - # Before increasing this value, make sure you understand the effects of head-of-line blocking. - # Using a connection pool, a request may be issued on a connection where a previous - # long-running request hasn't finished yet. The response to the pipelined requests may then be stuck - # behind the response of the long-running previous requests on the server. This may introduce an - # unwanted "coupling" of run time between otherwise unrelated requests. - # - # See http://tools.ietf.org/html/rfc7230#section-6.3.2 for more info.) - pipelining-limit = 1 - - # The time after which an idle connection pool (without pending requests) - # will automatically terminate itself. Set to `infinite` to completely disable idle timeouts. - idle-timeout = 30 s - - # Modify to tweak client settings for host connection pools only. - # - # IMPORTANT: - # Please note that this section mirrors `akka.http.client` however is used only for pool-based APIs, - # such as `Http().superPool` or `Http().singleRequest`. - client = { - # The default value of the `User-Agent` header to produce if no - # explicit `User-Agent`-header was included in a request. - # If this value is the empty string and no header was included in - # the request, no `User-Agent` header will be rendered at all. - user-agent-header = akka-http/${akka.version} - - # The time period within which the TCP connecting process must be completed. - connecting-timeout = 10s - - # The time after which an idle connection will be automatically closed. - # Set to `infinite` to completely disable idle timeouts. - idle-timeout = 60 s - - # The initial size of the buffer to render the request headers in. - # Can be used for fine-tuning request rendering performance but probably - # doesn't have to be fiddled with in most applications. - request-header-size-hint = 512 - - # The proxy configurations to be used for requests with the specified - # scheme. - proxy { - # Proxy settings for unencrypted HTTP requests - # Set to 'none' to always connect directly, 'default' to use the system - # settings as described in http://docs.oracle.com/javase/6/docs/technotes/guides/net/proxies.html - # or specify the proxy host, port and non proxy hosts as demonstrated - # in the following example: - # http { - # host = myproxy.com - # port = 8080 - # non-proxy-hosts = ["*.direct-access.net"] - # } - http = default - - # Proxy settings for HTTPS requests (currently unsupported) - https = default - } - - # Socket options to set for the listening socket. If a setting is left - # undefined, it will use whatever the default on the system is. - socket-options { - so-receive-buffer-size = undefined - so-send-buffer-size = undefined - so-reuse-address = undefined - so-traffic-class = undefined - tcp-keep-alive = undefined - tcp-oob-inline = undefined - tcp-no-delay = undefined - } - - - # IMPORTANT: Please note that this section is replicated in `client` and `server`. - parsing { - # no overrides by default, see `akka.http.parsing` for default values - } - } - } - - # Modify to tweak default parsing settings. - # - # IMPORTANT: - # Please note that this sections settings can be overriden by the corresponding settings in: - # `akka.http.server.parsing`, `akka.http.client.parsing` or `akka.http.host-connection-pool.client.parsing`. - parsing { - # The limits for the various parts of the HTTP message parser. - max-uri-length = 2k - max-method-length = 16 - max-response-reason-length = 64 - max-header-name-length = 64 - max-header-value-length = 8k - max-header-count = 64 - max-chunk-ext-length = 256 - max-chunk-size = 1m - - # Default maximum content length which should not be exceeded by incoming request entities. - # Can be changed at runtime (to a higher or lower value) via the `HttpEntity::withSizeLimit` method. - # Note that it is not necessarily a problem to set this to a high value as all stream operations - # are always properly backpressured. - # Nevertheless you might want to apply some limit in order to prevent a single client from consuming - # an excessive amount of server resources. - # - # Set to `infinite` to completely disable entity length checks. (Even then you can still apply one - # programmatically via `withSizeLimit`.) - max-content-length = 8m - - # Sets the strictness mode for parsing request target URIs. - # The following values are defined: - # - # `strict`: RFC3986-compliant URIs are required, - # a 400 response is triggered on violations - # - # `relaxed`: all visible 7-Bit ASCII chars are allowed - # - uri-parsing-mode = strict - - # Sets the parsing mode for parsing cookies. - # The following value are defined: - # - # `rfc6265`: Only RFC6265-compliant cookies are parsed. Surrounding double-quotes are accepted and - # automatically removed. Non-compliant cookies are silently discarded. - # `raw`: Raw parsing allows any non-control character but ';' to appear in a cookie value. There's no further - # post-processing applied, so that the resulting value string may contain any number of whitespace, unicode, - # double quotes, or '=' characters at any position. - # The rules for parsing the cookie name are the same ones from RFC 6265. - # - cookie-parsing-mode = rfc6265 - - # Enables/disables the logging of warning messages in case an incoming - # message (request or response) contains an HTTP header which cannot be - # parsed into its high-level model class due to incompatible syntax. - # Note that, independently of this settings, akka-http will accept messages - # with such headers as long as the message as a whole would still be legal - # under the HTTP specification even without this header. - # If a header cannot be parsed into a high-level model instance it will be - # provided as a `RawHeader`. - # If logging is enabled it is performed with the configured - # `error-logging-verbosity`. - illegal-header-warnings = on - - # Configures the verbosity with which message (request or response) parsing - # errors are written to the application log. - # - # Supported settings: - # `off` : no log messages are produced - # `simple`: a condensed single-line message is logged - # `full` : the full error details (potentially spanning several lines) are logged - error-logging-verbosity = full - - # Configures the processing mode when encountering illegal characters in - # header value of response. - # - # Supported mode: - # `error` : default mode, throw an ParsingException and terminate the processing - # `warn` : ignore the illegal characters in response header value and log a warning message - # `ignore` : just ignore the illegal characters in response header value - illegal-response-header-value-processing-mode = error - - # limits for the number of different values per header type that the - # header cache will hold - header-cache { - default = 12 - Content-MD5 = 0 - Date = 0 - If-Match = 0 - If-Modified-Since = 0 - If-None-Match = 0 - If-Range = 0 - If-Unmodified-Since = 0 - User-Agent = 32 - } - - # Enables/disables inclusion of an Tls-Session-Info header in parsed - # messages over Tls transports (i.e., HttpRequest on server side and - # HttpResponse on client side). - tls-session-info-header = off - } -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/HttpConnectionTimeoutException.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/HttpConnectionTimeoutException.scala deleted file mode 100644 index b40825af07..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/HttpConnectionTimeoutException.scala +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright (C) 2015-2016 Lightbend Inc. - */ - -package akka.http.impl.engine - -import scala.util.control.NoStackTrace - -class HttpConnectionTimeoutException(msg: String) extends RuntimeException(msg) with NoStackTrace diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/client/OutgoingConnectionBlueprint.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/client/OutgoingConnectionBlueprint.scala deleted file mode 100644 index a49a6e191b..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/client/OutgoingConnectionBlueprint.scala +++ /dev/null @@ -1,351 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.client - -import akka.NotUsed -import akka.http.scaladsl.settings.{ ClientConnectionSettings, ParserSettings } -import akka.stream.impl.ConstantFun -import language.existentials -import scala.annotation.tailrec -import scala.concurrent.Promise -import scala.collection.mutable.ListBuffer -import akka.stream.TLSProtocol._ -import akka.util.ByteString -import akka.event.LoggingAdapter -import akka.stream._ -import akka.stream.scaladsl._ -import akka.http.scaladsl.Http -import akka.http.scaladsl.model.headers -import akka.http.scaladsl.model.{ IllegalResponseException, HttpRequest, HttpResponse, ResponseEntity } -import akka.http.impl.engine.rendering.{ RequestRenderingContext, HttpRequestRendererFactory } -import akka.http.impl.engine.parsing._ -import akka.http.impl.util._ -import akka.stream.stage.GraphStage -import akka.stream.stage.GraphStageLogic -import akka.stream.stage.{ InHandler, OutHandler } - -/** - * INTERNAL API - */ -private[http] object OutgoingConnectionBlueprint { - - type BypassData = HttpResponseParser.ResponseContext - - /* - Stream Setup - ============ - - requestIn +----------+ - +-----------------------------------------------+--->| Termi- | requestRendering - | | nation +---------------------> | - +-------------------------------------->| Merge | | - | Termination Backchannel | +----------+ | TCP- - | | | level - | | BypassData | client - | +------------+ | | flow - responseOut | responsePrep | Response |<---+ | - <------------+----------------| Parsing | | - | Merge |<------------------------------------------ V - +------------+ - */ - def apply( - hostHeader: headers.Host, - settings: ClientConnectionSettings, - log: LoggingAdapter): Http.ClientLayer = { - import settings._ - - val core = BidiFlow.fromGraph(GraphDSL.create() { implicit b ⇒ - import GraphDSL.Implicits._ - - val renderingContextCreation = b.add { - Flow[HttpRequest] map { request ⇒ - val sendEntityTrigger = - request.headers collectFirst { case headers.Expect.`100-continue` ⇒ Promise[NotUsed]().future } - RequestRenderingContext(request, hostHeader, sendEntityTrigger) - } - } - - val bypassFanout = b.add(Broadcast[RequestRenderingContext](2, eagerCancel = true)) - - val terminationMerge = b.add(TerminationMerge) - - val requestRendering: Flow[RequestRenderingContext, ByteString, NotUsed] = { - val requestRendererFactory = new HttpRequestRendererFactory(userAgentHeader, requestHeaderSizeHint, log) - Flow[RequestRenderingContext].flatMapConcat(requestRendererFactory.renderToSource).named("renderer") - } - - val bypass = Flow[RequestRenderingContext] map { ctx ⇒ - HttpResponseParser.ResponseContext(ctx.request.method, ctx.sendEntityTrigger.map(_.asInstanceOf[Promise[Unit]])) - } - - val responseParsingMerge = b.add { - // the initial header parser we initially use for every connection, - // will not be mutated, all "shared copy" parsers copy on first-write into the header cache - val rootParser = new HttpResponseParser(parserSettings, HttpHeaderParser(parserSettings, log) { info ⇒ - if (parserSettings.illegalHeaderWarnings) - logParsingError(info withSummaryPrepended "Illegal response header", log, parserSettings.errorLoggingVerbosity) - }) - new ResponseParsingMerge(rootParser) - } - - val responsePrep = Flow[List[ParserOutput.ResponseOutput]] - .mapConcat(ConstantFun.scalaIdentityFunction) - .via(new PrepareResponse(parserSettings)) - - val terminationFanout = b.add(Broadcast[HttpResponse](2)) - - val logger = b.add(MapError[ByteString] { case t ⇒ log.error(t, "Outgoing request stream error"); t }.named("errorLogger")) - val wrapTls = b.add(Flow[ByteString].map(SendBytes)) - - val collectSessionBytes = b.add(Flow[SslTlsInbound].collect { case s: SessionBytes ⇒ s }) - - renderingContextCreation.out ~> bypassFanout.in - bypassFanout.out(0) ~> terminationMerge.in0 - terminationMerge.out ~> requestRendering ~> logger ~> wrapTls - - bypassFanout.out(1) ~> bypass ~> responseParsingMerge.in1 - collectSessionBytes ~> responseParsingMerge.in0 - - responseParsingMerge.out ~> responsePrep ~> terminationFanout.in - terminationFanout.out(0) ~> terminationMerge.in1 - - BidiShape( - renderingContextCreation.in, - wrapTls.out, - collectSessionBytes.in, - terminationFanout.out(1)) - }) - - One2OneBidiFlow[HttpRequest, HttpResponse](-1) atop core - } - - // a simple merge stage that simply forwards its first input and ignores its second input - // (the terminationBackchannelInput), but applies a special completion handling - private object TerminationMerge extends GraphStage[FanInShape2[RequestRenderingContext, HttpResponse, RequestRenderingContext]] { - private val requests = Inlet[RequestRenderingContext]("requests") - private val responses = Inlet[HttpResponse]("responses") - private val out = Outlet[RequestRenderingContext]("out") - - override def initialAttributes = Attributes.name("TerminationMerge") - - val shape = new FanInShape2(requests, responses, out) - - override def createLogic(effectiveAttributes: Attributes) = new GraphStageLogic(shape) { - passAlong(requests, out, doFinish = false, doFail = true) - setHandler(out, eagerTerminateOutput) - - setHandler(responses, new InHandler { - override def onPush(): Unit = pull(responses) - }) - - override def preStart(): Unit = { - pull(requests) - pull(responses) - } - } - } - - import ParserOutput._ - - /** - * This is essentially a three state state machine, it is either 'idle' - waiting for a response to come in - * or has seen the start of a response and is waiting for either chunks followed by MessageEnd if chunked - * or just MessageEnd in the case of a strict response. - * - * For chunked responses a new substream into the response entity is opened and data is streamed there instead - * of downstream until end of chunks has been reached. - */ - private[client] final class PrepareResponse(parserSettings: ParserSettings) - extends GraphStage[FlowShape[ResponseOutput, HttpResponse]] { - - private val in = Inlet[ResponseOutput]("PrepareResponse.in") - private val out = Outlet[HttpResponse]("PrepareResponse.out") - - val shape = new FlowShape(in, out) - - override def createLogic(effectiveAttributes: Attributes) = new GraphStageLogic(shape) with InHandler with OutHandler { - private var entitySource: SubSourceOutlet[ResponseOutput] = _ - private def entitySubstreamStarted = entitySource ne null - private def idle = this - private var completionDeferred = false - private var completeOnMessageEnd = false - - def setIdleHandlers(): Unit = - if (completeOnMessageEnd || completionDeferred) completeStage() - else setHandlers(in, out, idle) - - def onPush(): Unit = grab(in) match { - case ResponseStart(statusCode, protocol, headers, entityCreator, closeRequested) ⇒ - val entity = createEntity(entityCreator) withSizeLimit parserSettings.maxContentLength - push(out, HttpResponse(statusCode, headers, entity, protocol)) - completeOnMessageEnd = closeRequested - - case MessageStartError(_, info) ⇒ - throw IllegalResponseException(info) - - case other ⇒ - throw new IllegalStateException(s"ResponseStart expected but $other received.") - } - - def onPull(): Unit = { - if (!entitySubstreamStarted) pull(in) - } - - override def onDownstreamFinish(): Unit = { - // if downstream cancels while streaming entity, - // make sure we also cancel the entity source, but - // after being done with streaming the entity - if (entitySubstreamStarted) { - completionDeferred = true - } else { - completeStage() - } - } - - setIdleHandlers() - - // with a strict message there still is a MessageEnd to wait for - lazy val waitForMessageEnd = new InHandler with OutHandler { - def onPush(): Unit = grab(in) match { - case MessageEnd ⇒ - if (isAvailable(out)) pull(in) - setIdleHandlers() - case other ⇒ throw new IllegalStateException(s"MessageEnd expected but $other received.") - } - - override def onPull(): Unit = { - // ignore pull as we will anyways pull when we get MessageEnd - } - } - - // with a streamed entity we push the chunks into the substream - // until we reach MessageEnd - private lazy val substreamHandler = new InHandler with OutHandler { - override def onPush(): Unit = grab(in) match { - case MessageEnd ⇒ - entitySource.complete() - entitySource = null - // there was a deferred pull from upstream - // while we were streaming the entity - if (isAvailable(out)) pull(in) - setIdleHandlers() - - case messagePart ⇒ - entitySource.push(messagePart) - } - - override def onPull(): Unit = pull(in) - - override def onUpstreamFinish(): Unit = { - entitySource.complete() - completeStage() - } - - override def onUpstreamFailure(reason: Throwable): Unit = { - entitySource.fail(reason) - failStage(reason) - } - } - - private def createEntity(creator: EntityCreator[ResponseOutput, ResponseEntity]): ResponseEntity = { - creator match { - case StrictEntityCreator(entity) ⇒ - // upstream demanded one element, which it just got - // but we want MessageEnd as well - pull(in) - setHandler(in, waitForMessageEnd) - setHandler(out, waitForMessageEnd) - entity - - case StreamedEntityCreator(creator) ⇒ - entitySource = new SubSourceOutlet[ResponseOutput]("EntitySource") - entitySource.setHandler(substreamHandler) - setHandler(in, substreamHandler) - creator(Source.fromGraph(entitySource.source)) - } - } - } - } - - /** - * A merge that follows this logic: - * 1. Wait on the methodBypass for the method of the request corresponding to the next response to be received - * 2. Read from the dataInput until exactly one response has been fully received - * 3. Go back to 1. - */ - private class ResponseParsingMerge(rootParser: HttpResponseParser) - extends GraphStage[FanInShape2[SessionBytes, BypassData, List[ResponseOutput]]] { - private val dataInput = Inlet[SessionBytes]("data") - private val bypassInput = Inlet[BypassData]("request") - private val out = Outlet[List[ResponseOutput]]("out") - - override def initialAttributes = Attributes.name("ResponseParsingMerge") - - val shape = new FanInShape2(dataInput, bypassInput, out) - - override def createLogic(effectiveAttributes: Attributes) = new GraphStageLogic(shape) { - // each connection uses a single (private) response parser instance for all its responses - // which builds a cache of all header instances seen on that connection - val parser = rootParser.createShallowCopy() - var waitingForMethod = true - - setHandler(bypassInput, new InHandler { - override def onPush(): Unit = { - val responseContext = grab(bypassInput) - parser.setContextForNextResponse(responseContext) - val output = parser.parseBytes(ByteString.empty) - drainParser(output) - } - override def onUpstreamFinish(): Unit = - if (waitingForMethod) completeStage() - }) - - setHandler(dataInput, new InHandler { - override def onPush(): Unit = { - val bytes = grab(dataInput) - val output = parser.parseSessionBytes(bytes) - drainParser(output) - } - override def onUpstreamFinish(): Unit = - if (waitingForMethod) completeStage() - else { - if (parser.onUpstreamFinish()) { - completeStage() - } else { - emit(out, parser.onPull() :: Nil, () ⇒ completeStage()) - } - } - }) - - setHandler(out, eagerTerminateOutput) - - val getNextMethod = () ⇒ { - waitingForMethod = true - if (isClosed(bypassInput)) completeStage() - else pull(bypassInput) - } - - val getNextData = () ⇒ { - waitingForMethod = false - if (isClosed(dataInput)) completeStage() - else pull(dataInput) - } - - @tailrec def drainParser(current: ResponseOutput, b: ListBuffer[ResponseOutput] = ListBuffer.empty): Unit = { - def e(output: List[ResponseOutput], andThen: () ⇒ Unit): Unit = - if (output.nonEmpty) emit(out, output, andThen) - else andThen() - current match { - case NeedNextRequestMethod ⇒ e(b.result(), getNextMethod) - case StreamEnd ⇒ e(b.result(), () ⇒ completeStage()) - case NeedMoreData ⇒ e(b.result(), getNextData) - case x ⇒ drainParser(parser.onPull(), b += x) - } - } - - override def preStart(): Unit = getNextMethod() - } - } -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/client/PoolConductor.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/client/PoolConductor.scala deleted file mode 100644 index 567edb151d..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/client/PoolConductor.scala +++ /dev/null @@ -1,254 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.client - -import language.existentials -import scala.annotation.tailrec -import scala.collection.immutable -import akka.event.LoggingAdapter -import akka.stream.scaladsl._ -import akka.stream._ -import akka.http.scaladsl.util.FastFuture -import akka.http.scaladsl.model.HttpMethod -import akka.stream.stage.GraphStage -import akka.stream.stage.GraphStageLogic -import akka.stream.stage.InHandler - -private object PoolConductor { - import PoolFlow.RequestContext - import PoolSlot.{ RawSlotEvent, SlotEvent } - - case class Ports( - requestIn: Inlet[RequestContext], - slotEventIn: Inlet[RawSlotEvent], - slotOuts: immutable.Seq[Outlet[SlotCommand]]) extends Shape { - - override val inlets = requestIn :: slotEventIn :: Nil - override def outlets = slotOuts - - override def deepCopy(): Shape = - Ports( - requestIn.carbonCopy(), - slotEventIn.carbonCopy(), - slotOuts.map(_.carbonCopy())) - - override def copyFromPorts(inlets: immutable.Seq[Inlet[_]], outlets: immutable.Seq[Outlet[_]]): Shape = - Ports( - inlets.head.asInstanceOf[Inlet[RequestContext]], - inlets.last.asInstanceOf[Inlet[RawSlotEvent]], - outlets.asInstanceOf[immutable.Seq[Outlet[SlotCommand]]]) - } - - final case class PoolSlotsSetting(minSlots: Int, maxSlots: Int) { - require(minSlots <= maxSlots, "min-connections must be <= max-connections") - } - - /* - Stream Setup - ============ - Slot- - Request- +-----------+ +-----------+ Switch- +-------------+ +-----------+ Command - Context | retry | | slot- | Command | doubler | | route +--------------> - +--------->| Merge +---->| Selector +-------------->| (MapConcat) +---->| (Flexi +--------------> - | | | | | | | Route) +--------------> - +----+------+ +-----+-----+ +-------------+ +-----------+ to slots - ^ ^ - | | SlotEvent - | +----+----+ - | | flatten | mapAsync - | +----+----+ - | | RawSlotEvent - | Request- | - | Context +---------+ - +-------------+ retry |<-------- RawSlotEvent (from slotEventMerge) - | Split | - +---------+ - - */ - def apply(slotSettings: PoolSlotsSetting, pipeliningLimit: Int, log: LoggingAdapter): Graph[Ports, Any] = - GraphDSL.create() { implicit b ⇒ - import GraphDSL.Implicits._ - - val retryMerge = b.add(MergePreferred[RequestContext](1, eagerComplete = true)) - val slotSelector = b.add(new SlotSelector(slotSettings, pipeliningLimit, log)) - val route = b.add(new Route(slotSettings.maxSlots)) - val retrySplit = b.add(Broadcast[RawSlotEvent](2)) - val flatten = Flow[RawSlotEvent].mapAsyncUnordered(slotSettings.maxSlots) { - case x: SlotEvent.Disconnected ⇒ FastFuture.successful(x) - case SlotEvent.RequestCompletedFuture(future) ⇒ future - case x: SlotEvent.ConnectedEagerly ⇒ FastFuture.successful(x) - case x ⇒ throw new IllegalStateException("Unexpected " + x) - } - - retryMerge.out ~> slotSelector.in0 - slotSelector.out ~> route.in - retrySplit.out(0).filter(!_.isInstanceOf[SlotEvent.RetryRequest]) ~> flatten ~> slotSelector.in1 - retrySplit.out(1).collect { case SlotEvent.RetryRequest(r) ⇒ r } ~> retryMerge.preferred - - Ports(retryMerge.in(0), retrySplit.in, route.outArray.toList) - } - - sealed trait SlotCommand - final case class DispatchCommand(rc: RequestContext) extends SlotCommand - final case object ConnectEagerlyCommand extends SlotCommand - - final case class SwitchSlotCommand(cmd: SlotCommand, slotIx: Int) - - // the SlotSelector keeps the state of all slots as instances of this ADT - private sealed trait SlotState - - // the connection of the respective slot is not connected - private case object Unconnected extends SlotState - - // the connection of the respective slot is connected with no requests currently in flight - private case object Idle extends SlotState - - // the connection of the respective slot has a number of requests in flight and all of them - // are idempotent which allows more requests to be pipelined onto the connection if required - private final case class Loaded(openIdempotentRequests: Int) extends SlotState { require(openIdempotentRequests > 0) } - - // the connection of the respective slot has a number of requests in flight and the - // last one of these is not idempotent which blocks the connection for more pipelined requests - private case class Busy(openRequests: Int) extends SlotState { require(openRequests > 0) } - private object Busy extends Busy(1) - - private class SlotSelector(slotSettings: PoolSlotsSetting, pipeliningLimit: Int, log: LoggingAdapter) - extends GraphStage[FanInShape2[RequestContext, SlotEvent, SwitchSlotCommand]] { - - private val ctxIn = Inlet[RequestContext]("requestContext") - private val slotIn = Inlet[SlotEvent]("slotEvents") - private val out = Outlet[SwitchSlotCommand]("slotCommand") - - override def initialAttributes = Attributes.name("SlotSelector") - - override val shape = new FanInShape2(ctxIn, slotIn, out) - - override def createLogic(effectiveAttributes: Attributes) = new GraphStageLogic(shape) { - val slotStates = Array.fill[SlotState](slotSettings.maxSlots)(Unconnected) - var nextSlot = 0 - - setHandler(ctxIn, new InHandler { - override def onPush(): Unit = { - val ctx = grab(ctxIn) - val slot = nextSlot - slotStates(slot) = slotStateAfterDispatch(slotStates(slot), ctx.request.method) - nextSlot = bestSlot() - emit(out, SwitchSlotCommand(DispatchCommand(ctx), slot), tryPullCtx) - } - }) - - setHandler(slotIn, new InHandler { - override def onPush(): Unit = { - grab(slotIn) match { - case SlotEvent.RequestCompleted(slotIx) ⇒ - slotStates(slotIx) = slotStateAfterRequestCompleted(slotStates(slotIx)) - case SlotEvent.Disconnected(slotIx, failed) ⇒ - slotStates(slotIx) = slotStateAfterDisconnect(slotStates(slotIx), failed) - reconnectIfNeeded() - case SlotEvent.ConnectedEagerly(slotIx) ⇒ - // do nothing ... - } - pull(slotIn) - val wasBlocked = nextSlot == -1 - nextSlot = bestSlot() - val nowUnblocked = nextSlot != -1 - if (wasBlocked && nowUnblocked) pull(ctxIn) // get next request context - } - }) - - setHandler(out, eagerTerminateOutput) - - val tryPullCtx = () ⇒ if (nextSlot != -1 && !hasBeenPulled(ctxIn)) pull(ctxIn) - - override def preStart(): Unit = { - pull(ctxIn) - pull(slotIn) - - // eagerly start at least slotSettings.minSlots connections - (0 until slotSettings.minSlots).foreach { connect } - } - - def connect(slotIx: Int): Unit = { - emit(out, SwitchSlotCommand(ConnectEagerlyCommand, slotIx)) - slotStates(slotIx) = Idle - } - - private def reconnectIfNeeded(): Unit = - if (slotStates.count(_ != Unconnected) < slotSettings.minSlots) { - connect(slotStates.indexWhere(_ == Unconnected)) - } - - def slotStateAfterDispatch(slotState: SlotState, method: HttpMethod): SlotState = - slotState match { - case Unconnected | Idle ⇒ if (method.isIdempotent) Loaded(1) else Busy(1) - case Loaded(n) ⇒ if (method.isIdempotent) Loaded(n + 1) else Busy(n + 1) - case Busy(_) ⇒ throw new IllegalStateException("Request scheduled onto busy connection?") - } - - def slotStateAfterRequestCompleted(slotState: SlotState): SlotState = - slotState match { - case Loaded(1) ⇒ Idle - case Loaded(n) ⇒ Loaded(n - 1) - case Busy(1) ⇒ Idle - case Busy(n) ⇒ Busy(n - 1) - case _ ⇒ throw new IllegalStateException(s"RequestCompleted on $slotState connection?") - } - - def slotStateAfterDisconnect(slotState: SlotState, failed: Int): SlotState = - slotState match { - case Idle if failed == 0 ⇒ Unconnected - case Loaded(n) if n > failed ⇒ Loaded(n - failed) - case Loaded(n) if n == failed ⇒ Unconnected - case Busy(n) if n > failed ⇒ Busy(n - failed) - case Busy(n) if n == failed ⇒ Unconnected - case _ ⇒ throw new IllegalStateException(s"Disconnect(_, $failed) on $slotState connection?") - } - - /** - * Implements the following Connection Slot selection strategy - * - Select the first idle connection in the pool, if there is one. - * - If none is idle select the first unconnected connection, if there is one. - * - If all are loaded select the connection with the least open requests (< pipeliningLimit) - * that only has requests with idempotent methods scheduled to it, if there is one. - * - Otherwise return -1 (which applies back-pressure to the request source) - * - * See http://tools.ietf.org/html/rfc7230#section-6.3.2 for more info on HTTP pipelining. - */ - @tailrec def bestSlot(ix: Int = 0, bestIx: Int = -1, bestState: SlotState = Busy): Int = - if (ix < slotStates.length) { - val pl = pipeliningLimit - slotStates(ix) → bestState match { - case (Idle, _) ⇒ ix - case (Unconnected, Loaded(_) | Busy) ⇒ bestSlot(ix + 1, ix, Unconnected) - case (x @ Loaded(a), Loaded(b)) if a < b ⇒ bestSlot(ix + 1, ix, x) - case (x @ Loaded(a), Busy) if a < pl ⇒ bestSlot(ix + 1, ix, x) - case _ ⇒ bestSlot(ix + 1, bestIx, bestState) - } - } else bestIx - } - } - - private class Route(slotCount: Int) extends GraphStage[UniformFanOutShape[SwitchSlotCommand, SlotCommand]] { - - override def initialAttributes = Attributes.name("PoolConductor.Route") - - override val shape = new UniformFanOutShape[SwitchSlotCommand, SlotCommand](slotCount) - - override def createLogic(effectiveAttributes: Attributes) = new GraphStageLogic(shape) { - shape.outArray foreach { setHandler(_, ignoreTerminateOutput) } - - val in = shape.in - setHandler(in, new InHandler { - override def onPush(): Unit = { - val switchCommand = grab(in) - emit(shape.outArray(switchCommand.slotIx), switchCommand.cmd, pullIn) - } - }) - val pullIn = () ⇒ pull(in) - - override def preStart(): Unit = pullIn() - } - } -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/client/PoolFlow.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/client/PoolFlow.scala deleted file mode 100644 index 6f404700f3..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/client/PoolFlow.scala +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.client - -import akka.NotUsed -import akka.http.impl.engine.client.PoolConductor.PoolSlotsSetting -import akka.http.scaladsl.settings.ConnectionPoolSettings - -import scala.concurrent.{ Promise, Future } -import scala.util.Try -import akka.event.LoggingAdapter -import akka.actor._ -import akka.stream.{ FlowShape, Materializer } -import akka.stream.scaladsl._ -import akka.http.scaladsl.model._ -import akka.http.scaladsl.Http - -private object PoolFlow { - - case class RequestContext(request: HttpRequest, responsePromise: Promise[HttpResponse], retriesLeft: Int) { - require(retriesLeft >= 0) - } - case class ResponseContext(rc: RequestContext, response: Try[HttpResponse]) - - /* - Pool Flow Stream Setup - ====================== - +-------------------+ - | | - +----> | Connection Slot 1 +----> - | | | | - | +---+---------------+ | - | | | - +-----------+ | +-------------------+ | +---------------+ - RequestContext | +----+ | | +----> | | ResponseContext - +----------------> | Conductor |---------> | Connection Slot 2 +---------> | responseMerge +------------------> - | +----+ | | +----> | | - +-----------+ | +---------+---------+ | +---------------+ - ^ | | | | - | | +-------------------+ | - | | | | | - RawSlotEvent | +----> | Connection Slot 3 +----> - | | | - | +---------------+---+ - | | | | - +-----------+ RawSlotEvent | | | - | slotEvent | <-------------+ | | - | Merge | <-------------------+ | - | | <-------------------------+ - +-----------+ - - Conductor: - - Maintains slot state overview by running a simple state machine per Connection Slot - - Decides which slot will receive the next request from upstream according to current slot state and dispatch configuration - - Forwards demand from selected slot to upstream - - Always maintains demand for SlotEvents from the Connection Slots - - Implemented as a sub-graph - - Connection Slot: - - Wraps a low-level outgoing connection flow and (re-)materializes and uses it whenever necessary - - Directly forwards demand from the underlying connection to the Conductor - - Dispatches SlotEvents to the Conductor (via the SlotEventMerge) - - Implemented as a sub-graph - - Response Merge: - - Simple merge of the Connection Slots' outputs - - */ - def apply( - connectionFlow: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]], - settings: ConnectionPoolSettings, log: LoggingAdapter)( - implicit - system: ActorSystem, fm: Materializer): Flow[RequestContext, ResponseContext, NotUsed] = - Flow.fromGraph(GraphDSL.create[FlowShape[RequestContext, ResponseContext]]() { implicit b ⇒ - import settings._ - import GraphDSL.Implicits._ - - val conductor = b.add( - PoolConductor(PoolSlotsSetting(maxSlots = maxConnections, minSlots = minConnections), pipeliningLimit, log) - ) - - val slots = Vector - .tabulate(maxConnections)(PoolSlot(_, connectionFlow)) - .map(b.add) - - val responseMerge = b.add(Merge[ResponseContext](maxConnections)) - val slotEventMerge = b.add(Merge[PoolSlot.RawSlotEvent](maxConnections)) - - slotEventMerge.out ~> conductor.slotEventIn - for ((slot, ix) ← slots.zipWithIndex) { - conductor.slotOuts(ix) ~> slot.in - slot.out0 ~> responseMerge.in(ix) - slot.out1 ~> slotEventMerge.in(ix) - } - FlowShape(conductor.requestIn, responseMerge.out) - }) -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/client/PoolGateway.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/client/PoolGateway.scala deleted file mode 100644 index 21e01a84b3..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/client/PoolGateway.scala +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.client - -import java.util.concurrent.atomic.AtomicLong - -import akka.Done -import akka.actor.ActorRef -import akka.http.impl.engine.client.PoolGateway.{ GatewayIdentifier, SharedGateway } -import akka.http.impl.engine.client.PoolMasterActor._ -import akka.http.impl.settings.HostConnectionPoolSetup -import akka.http.scaladsl.model.{ HttpRequest, HttpResponse } -import akka.stream.Materializer - -import scala.concurrent.{ Future, Promise } - -/** - * Manages access to a host connection pool through the [[PoolMasterActor]] - * - * A [[PoolGateway]] is represented by its [[HostConnectionPoolSetup]] and its [[GatewayIdentifier]]. If the later - * is [[SharedGateway]], it means that a shared pool must be used for this particular [[HostConnectionPoolSetup]]. - */ -private[http] final class PoolGateway(gatewayRef: ActorRef, val hcps: HostConnectionPoolSetup, val gatewayId: GatewayIdentifier)(implicit fm: Materializer) { - - /** - * Send a request through the corresponding pool. If the pool is not running, it will be started - * automatically. If it is shutting down, it will restart as soon as the shutdown operation is - * complete and serve this request. - * - * @param request the request - * @return the response - */ - def apply(request: HttpRequest): Future[HttpResponse] = { - val responsePromise = Promise[HttpResponse]() - gatewayRef ! SendRequest(this, request, responsePromise, fm) - responsePromise.future - } - - /** - * Start the corresponding pool to make it ready to serve requests. If the pool is already started, - * this does nothing. If it is being shutdown, it will restart as soon as the shutdown operation - * is complete. - * - * @return the gateway itself - */ - def startPool(): PoolGateway = { - gatewayRef ! StartPool(this, fm) - this - } - - /** - * Shutdown the corresponding pool and signal its termination. If the pool is not running or is - * being shutting down, this does nothing, - * - * @return a Future completed when the pool has been shutdown. - */ - def shutdown(): Future[Done] = { - val shutdownCompletedPromise = Promise[Done]() - gatewayRef ! Shutdown(this, shutdownCompletedPromise) - shutdownCompletedPromise.future - } - - override def toString = s"PoolGateway(hcps = $hcps)" - - // INTERNAL API (testing only) - private[client] def poolStatus(): Future[Option[PoolInterfaceStatus]] = { - val statusPromise = Promise[Option[PoolInterfaceStatus]]() - gatewayRef ! PoolStatus(this, statusPromise) - statusPromise.future - } - - override def equals(that: Any): Boolean = - that match { - case p: PoolGateway ⇒ p.hcps == hcps && p.gatewayId == gatewayId - case _ ⇒ false - } - - override def hashCode(): Int = hcps.hashCode() ^ gatewayId.hashCode() -} - -private[http] object PoolGateway { - - sealed trait GatewayIdentifier - case object SharedGateway extends GatewayIdentifier - final case class UniqueGateway(id: Long) extends GatewayIdentifier - - private[this] val uniqueGatewayId = new AtomicLong(0) - def newUniqueGatewayIdentifier = UniqueGateway(uniqueGatewayId.incrementAndGet()) - -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/client/PoolInterfaceActor.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/client/PoolInterfaceActor.scala deleted file mode 100644 index 1d0f7432ff..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/client/PoolInterfaceActor.scala +++ /dev/null @@ -1,160 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.client - -import akka.actor._ -import akka.http.impl.engine.client.PoolFlow._ -import akka.http.scaladsl.model._ -import akka.http.scaladsl.{ Http, HttpsConnectionContext } -import akka.stream.actor.ActorPublisherMessage._ -import akka.stream.actor.ActorSubscriberMessage._ -import akka.stream.actor.{ ActorPublisher, ActorSubscriber, ZeroRequestStrategy } -import akka.stream.impl.{ Buffer, SeqActorName } -import akka.stream.scaladsl.{ Flow, Keep, Sink, Source } -import akka.stream.{ BufferOverflowException, Materializer } - -import scala.annotation.tailrec -import scala.concurrent.Promise -import scala.concurrent.duration.FiniteDuration - -private object PoolInterfaceActor { - final case class PoolRequest(request: HttpRequest, responsePromise: Promise[HttpResponse]) extends NoSerializationVerificationNeeded - - case object Shutdown extends DeadLetterSuppression - - val name = SeqActorName("PoolInterfaceActor") - - def props(gateway: PoolGateway)(implicit fm: Materializer) = Props(new PoolInterfaceActor(gateway)).withDeploy(Deploy.local) -} - -/** - * An actor that wraps a completely encapsulated, running connection pool flow. - * - * Outside interface: - * The actor accepts `PoolRequest` messages and completes their `responsePromise` when the respective - * response has arrived. Incoming `PoolRequest` messages are not back-pressured but rather buffered in - * a fixed-size ringbuffer if required. Requests that would cause a buffer overflow are completed with - * a respective error. The user can prevent buffer overflows by configuring a `max-open-requests` value - * that is >= max-connections x pipelining-limit x number of respective client-flow materializations. - * - * Inside interface: - * To the inside (i.e. the running connection pool flow) the gateway actor acts as request source - * (ActorPublisher) and response sink (ActorSubscriber). - */ -private class PoolInterfaceActor(gateway: PoolGateway)(implicit fm: Materializer) - extends ActorSubscriber with ActorPublisher[RequestContext] with ActorLogging { - import PoolInterfaceActor._ - - private[this] val hcps = gateway.hcps - private[this] val inputBuffer = Buffer[PoolRequest](hcps.setup.settings.maxOpenRequests, fm) - private[this] var activeIdleTimeout: Option[Cancellable] = None - - log.debug("(Re-)starting host connection pool to {}:{}", hcps.host, hcps.port) - - initConnectionFlow() - - /** Start the pool flow with this actor acting as source as well as sink */ - private def initConnectionFlow() = { - import context.system - import hcps._ - import setup._ - - val connectionFlow = connectionContext match { - case httpsContext: HttpsConnectionContext ⇒ Http().outgoingConnectionHttps(host, port, httpsContext, None, settings.connectionSettings, setup.log) - case _ ⇒ Http().outgoingConnection(host, port, None, settings.connectionSettings, setup.log) - } - - val poolFlow = - PoolFlow(Flow[HttpRequest].viaMat(connectionFlow)(Keep.right), settings, setup.log) - .named("PoolFlow") - - Source.fromPublisher(ActorPublisher(self)).via(poolFlow).runWith(Sink.fromSubscriber(ActorSubscriber[ResponseContext](self))) - } - - activateIdleTimeoutIfNecessary() - - def requestStrategy = ZeroRequestStrategy - - def receive = { - - /////////////// COMING UP FROM POOL (SOURCE SIDE) ////////////// - - case Request(_) ⇒ dispatchRequests() // the pool is ready to take on more requests - - case Cancel ⇒ - // somehow the pool shut down, however, we don't do anything here because we'll also see an - // OnComplete or OnError which we use as the sole trigger for cleaning up - - /////////////// COMING DOWN FROM POOL (SINK SIDE) ////////////// - - case OnNext(ResponseContext(rc, responseTry)) ⇒ - rc.responsePromise.complete(responseTry) - activateIdleTimeoutIfNecessary() - - case OnComplete ⇒ // the pool shut down - log.debug("Host connection pool to {}:{} has completed orderly shutdown", hcps.host, hcps.port) - self ! PoisonPill // give potentially queued requests another chance to be forwarded back to the gateway - - case OnError(e) ⇒ // the pool shut down - log.debug("Host connection pool to {}:{} has shut down with error {}", hcps.host, hcps.port, e) - self ! PoisonPill // give potentially queued requests another chance to be forwarded back to the gateway - - /////////////// FROM CLIENT ////////////// - - case x: PoolRequest if isActive ⇒ - activeIdleTimeout foreach { timeout ⇒ - timeout.cancel() - activeIdleTimeout = None - } - if (totalDemand == 0) { - // if we can't dispatch right now we buffer and dispatch when demand from the pool arrives - if (inputBuffer.isFull) { - x.responsePromise.failure( - new BufferOverflowException(s"Exceeded configured max-open-requests value of [${inputBuffer.capacity}]")) - } else inputBuffer.enqueue(x) - } else dispatchRequest(x) // if we can dispatch right now, do it - request(1) // for every incoming request we demand one response from the pool - - case PoolRequest(request, responsePromise) ⇒ - // we have already started shutting down, i.e. this pool is not usable anymore - // so we forward the request back to the gateway - responsePromise.completeWith(gateway(request)) - - case Shutdown ⇒ // signal coming in from gateway - log.debug("Shutting down host connection pool to {}:{}", hcps.host, hcps.port) - onCompleteThenStop() - while (!inputBuffer.isEmpty) { - val PoolRequest(request, responsePromise) = inputBuffer.dequeue() - responsePromise.completeWith(gateway(request)) - } - } - - @tailrec private def dispatchRequests(): Unit = - if (totalDemand > 0 && !inputBuffer.isEmpty) { - dispatchRequest(inputBuffer.dequeue()) - dispatchRequests() - } - - def dispatchRequest(pr: PoolRequest): Unit = { - val scheme = Uri.httpScheme(hcps.setup.connectionContext.isSecure) - val hostHeader = headers.Host(hcps.host, Uri.normalizePort(hcps.port, scheme)) - val effectiveRequest = - pr.request - .withUri(pr.request.uri.toHttpRequestTargetOriginForm) - .withDefaultHeaders(hostHeader) - val retries = if (pr.request.method.isIdempotent) hcps.setup.settings.maxRetries else 0 - onNext(RequestContext(effectiveRequest, pr.responsePromise, retries)) - } - - def activateIdleTimeoutIfNecessary(): Unit = - if (shouldStopOnIdle()) { - import context.dispatcher - val timeout = hcps.setup.settings.idleTimeout.asInstanceOf[FiniteDuration] - activeIdleTimeout = Some(context.system.scheduler.scheduleOnce(timeout)(gateway.shutdown())) - } - - private def shouldStopOnIdle(): Boolean = - remainingRequested == 0 && hcps.setup.settings.idleTimeout.isFinite && hcps.setup.settings.minConnections == 0 -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/client/PoolMasterActor.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/client/PoolMasterActor.scala deleted file mode 100644 index 48a5ce52be..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/client/PoolMasterActor.scala +++ /dev/null @@ -1,157 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.client - -import akka.Done -import akka.actor.{ Actor, ActorLogging, ActorRef, DeadLetterSuppression, Deploy, NoSerializationVerificationNeeded, Props, Terminated } -import akka.http.impl.engine.client.PoolInterfaceActor.PoolRequest -import akka.http.impl.settings.HostConnectionPoolSetup -import akka.http.scaladsl.HttpExt -import akka.http.scaladsl.model.{ HttpRequest, HttpResponse } -import akka.stream.Materializer - -import scala.concurrent.{ Future, Promise } - -/** - * INTERNAL API - * - * Manages access to a host connection pool or rather: a sequence of pool incarnations. - * - * A host connection pool for a given [[HostConnectionPoolSetup]] is a running stream, whose outside interface is - * provided by its [[PoolInterfaceActor]] actor. The actor accepts [[PoolInterfaceActor.PoolRequest]] messages - * and completes their `responsePromise` whenever the respective response has been received (or an error occurred). - * - * The [[PoolMasterActor]] provides a layer of indirection between a [[PoolGateway]], which represents a pool, - * and the [[PoolInterfaceActor]] instances which are created on-demand and stopped after an idle-timeout. - * - * Several [[PoolGateway]] objects may be mapped to the same pool if they have the same [[HostConnectionPoolSetup]] - * and are marked as being shared. This is the case for example for gateways obtained through - * [[HttpExt.cachedHostConnectionPool]]. Some other gateways are not shared, such as those obtained through - * [[HttpExt.newHostConnectionPool]], and will have their dedicated restartable pool. - * - */ -private[http] final class PoolMasterActor extends Actor with ActorLogging { - - import PoolMasterActor._ - - private[this] var poolStatus = Map[PoolGateway, PoolInterfaceStatus]() - private[this] var poolInterfaces = Map[ActorRef, PoolGateway]() - - /** - * Start a new pool interface actor, register it in our maps, and watch its death. No actor should - * currently exist for this pool. - * - * @param gateway the pool gateway this pool corresponds to - * @param fm the materializer to use for this pool - * @return the newly created actor ref - */ - private[this] def startPoolInterfaceActor(gateway: PoolGateway)(implicit fm: Materializer): ActorRef = { - if (poolStatus.contains(gateway)) { - throw new IllegalStateException(s"pool interface actor for $gateway already exists") - } - val ref = context.actorOf(PoolInterfaceActor.props(gateway), PoolInterfaceActor.name.next()) - poolStatus += gateway → PoolInterfaceRunning(ref) - poolInterfaces += ref → gateway - context.watch(ref) - } - - def receive = { - - // Start or restart a pool without sending it a request. This is used to ensure that - // freshly created pools will be ready to serve requests immediately. - case s @ StartPool(gateway, materializer) ⇒ - poolStatus.get(gateway) match { - case Some(PoolInterfaceRunning(_)) ⇒ - case Some(PoolInterfaceShuttingDown(shutdownCompletedPromise)) ⇒ - // Pool is being shutdown. When this is done, start the pool again. - shutdownCompletedPromise.future.onComplete(_ ⇒ self ! s)(context.dispatcher) - case None ⇒ - startPoolInterfaceActor(gateway)(materializer) - } - - // Send a request to a pool. If needed, the pool will be started or restarted. - case s @ SendRequest(gateway, request, responsePromise, materializer) ⇒ - poolStatus.get(gateway) match { - case Some(PoolInterfaceRunning(ref)) ⇒ - ref ! PoolRequest(request, responsePromise) - case Some(PoolInterfaceShuttingDown(shutdownCompletedPromise)) ⇒ - // The request will be resent when the pool shutdown is complete (the first - // request will recreate the pool). - shutdownCompletedPromise.future.foreach(_ ⇒ self ! s)(context.dispatcher) - case None ⇒ - startPoolInterfaceActor(gateway)(materializer) ! PoolRequest(request, responsePromise) - } - - // Shutdown a pool and signal its termination. - case Shutdown(gateway, shutdownCompletedPromise) ⇒ - poolStatus.get(gateway).foreach { - case PoolInterfaceRunning(ref) ⇒ - // Ask the pool to shutdown itself. Queued connections will be resent here - // to this actor by the pool actor, they will be retried once the shutdown - // has completed. - ref ! PoolInterfaceActor.Shutdown - poolStatus += gateway → PoolInterfaceShuttingDown(shutdownCompletedPromise) - case PoolInterfaceShuttingDown(formerPromise) ⇒ - // Pool is already shutting down, mirror the existing promise. - shutdownCompletedPromise.tryCompleteWith(formerPromise.future) - case _ ⇒ - // Pool does not exist, shutdown is not needed. - shutdownCompletedPromise.trySuccess(Done) - } - - // Shutdown all known pools and signal their termination. - case ShutdownAll(shutdownCompletedPromise) ⇒ - import context.dispatcher - def track(remaining: Iterator[Future[Done]]): Unit = - if (remaining.hasNext) remaining.next().onComplete(_ ⇒ track(remaining)) - else shutdownCompletedPromise.trySuccess(Done) - track(poolStatus.keys.map(_.shutdown()).toIterator) - - // When a pool actor terminate, signal its termination and remove it from our maps. - case Terminated(ref) ⇒ - poolInterfaces.get(ref).foreach { gateway ⇒ - poolStatus.get(gateway) match { - case Some(PoolInterfaceRunning(_)) ⇒ - log.error("connection pool for {} has shut down unexpectedly", gateway) - case Some(PoolInterfaceShuttingDown(shutdownCompletedPromise)) ⇒ - shutdownCompletedPromise.trySuccess(Done) - case None ⇒ - // This will never happen as poolInterfaces and poolStatus are modified - // together. If there is no status then there is no gateway to start with. - } - poolStatus -= gateway - poolInterfaces -= ref - } - - // Testing only. - case PoolStatus(gateway, statusPromise) ⇒ - statusPromise.success(poolStatus.get(gateway)) - - // Testing only. - case PoolSize(sizePromise) ⇒ - sizePromise.success(poolStatus.size) - } - -} - -private[http] object PoolMasterActor { - - val props = Props[PoolMasterActor].withDeploy(Deploy.local) - - sealed trait PoolInterfaceStatus - final case class PoolInterfaceRunning(ref: ActorRef) extends PoolInterfaceStatus - final case class PoolInterfaceShuttingDown(shutdownCompletedPromise: Promise[Done]) extends PoolInterfaceStatus - - final case class StartPool(gateway: PoolGateway, materializer: Materializer) extends NoSerializationVerificationNeeded - final case class SendRequest(gateway: PoolGateway, request: HttpRequest, responsePromise: Promise[HttpResponse], materializer: Materializer) - extends NoSerializationVerificationNeeded - final case class Shutdown(gateway: PoolGateway, shutdownCompletedPromise: Promise[Done]) extends NoSerializationVerificationNeeded with DeadLetterSuppression - final case class ShutdownAll(shutdownCompletedPromise: Promise[Done]) extends NoSerializationVerificationNeeded with DeadLetterSuppression - - // INTERNAL API (for testing only) - final case class PoolStatus(gateway: PoolGateway, statusPromise: Promise[Option[PoolInterfaceStatus]]) extends NoSerializationVerificationNeeded - final case class PoolSize(sizePromise: Promise[Int]) extends NoSerializationVerificationNeeded - -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/client/PoolSlot.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/client/PoolSlot.scala deleted file mode 100644 index cb1ebb51e8..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/client/PoolSlot.scala +++ /dev/null @@ -1,170 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.client - -import akka.event.LoggingAdapter -import akka.http.impl.engine.client.PoolConductor.{ ConnectEagerlyCommand, DispatchCommand, SlotCommand } -import akka.http.impl.engine.client.PoolSlot.SlotEvent.ConnectedEagerly -import akka.http.scaladsl.model.{ HttpEntity, HttpRequest, HttpResponse } -import akka.stream._ -import akka.stream.scaladsl._ -import akka.stream.stage.GraphStageLogic.EagerTerminateOutput -import akka.stream.stage.{ GraphStage, GraphStageLogic, InHandler, OutHandler } - -import scala.concurrent.Future -import scala.language.existentials -import scala.util.{ Failure, Success } -import scala.collection.JavaConverters._ - -private object PoolSlot { - import PoolFlow.{ RequestContext, ResponseContext } - - sealed trait RawSlotEvent - sealed trait SlotEvent extends RawSlotEvent - object SlotEvent { - final case class RequestCompletedFuture(future: Future[RequestCompleted]) extends RawSlotEvent - final case class RetryRequest(rc: RequestContext) extends RawSlotEvent - final case class RequestCompleted(slotIx: Int) extends SlotEvent - final case class Disconnected(slotIx: Int, failedRequests: Int) extends SlotEvent - /** - * Slot with id "slotIx" has responded to request from PoolConductor and connected immediately - * Ordinary connections from slots don't produce this event - */ - final case class ConnectedEagerly(slotIx: Int) extends SlotEvent - } - - def apply(slotIx: Int, connectionFlow: Flow[HttpRequest, HttpResponse, Any])(implicit m: Materializer): Graph[FanOutShape2[SlotCommand, ResponseContext, RawSlotEvent], Any] = - new SlotProcessor(slotIx, connectionFlow, ActorMaterializerHelper.downcast(m).logger) - - /** - * To the outside it provides a stable flow stage, consuming `SlotCommand` instances on its - * input side and producing `ResponseContext` and `RawSlotEvent` instances on its outputs. - * The given `connectionFlow` is materialized into a running flow whenever required. - * Completion and errors from the connection are not surfaced to the outside (unless we are - * shutting down completely). - */ - private class SlotProcessor(slotIx: Int, connectionFlow: Flow[HttpRequest, HttpResponse, Any], log: LoggingAdapter)(implicit fm: Materializer) - extends GraphStage[FanOutShape2[SlotCommand, ResponseContext, RawSlotEvent]] { - - val in: Inlet[SlotCommand] = Inlet("SlotProcessor.in") - val responsesOut: Outlet[ResponseContext] = Outlet("SlotProcessor.responsesOut") - val eventsOut: Outlet[RawSlotEvent] = Outlet("SlotProcessor.eventsOut") - - override def shape: FanOutShape2[SlotCommand, ResponseContext, RawSlotEvent] = new FanOutShape2(in, responsesOut, eventsOut) - - override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = new GraphStageLogic(shape) with InHandler { - private var firstRequest: RequestContext = _ - private val inflightRequests = new java.util.ArrayDeque[RequestContext]() - - private var connectionFlowSource: SubSourceOutlet[HttpRequest] = _ - private var connectionFlowSink: SubSinkInlet[HttpResponse] = _ - - private var isConnected = false - - def disconnect(ex: Option[Throwable] = None) = { - connectionFlowSource.complete() - if (isConnected) { - isConnected = false - - // if there was an error sending the request may have been sent so decrement retriesLeft - // otherwise the downstream hasn't sent so sent them back without modifying retriesLeft - val (retries, failures) = ex.map { fail ⇒ - val (inflightRetry, inflightFail) = inflightRequests.iterator().asScala.partition(_.retriesLeft > 0) - val retries = inflightRetry.map(rc ⇒ SlotEvent.RetryRequest(rc.copy(retriesLeft = rc.retriesLeft - 1))).toList - val failures = inflightFail.map(rc ⇒ ResponseContext(rc, Failure(fail))).toList - (retries, failures) - }.getOrElse((inflightRequests.iterator().asScala.map(rc ⇒ SlotEvent.RetryRequest(rc)).toList, Nil)) - - inflightRequests.clear() - - emitMultiple(responsesOut, failures) - emitMultiple(eventsOut, SlotEvent.Disconnected(slotIx, retries.size + failures.size) :: retries, () ⇒ if (failures.isEmpty && !hasBeenPulled(in)) pull(in)) - } - } - - // SourceOutlet is connected to the connectionFlow's inlet, when the connectionFlow - // completes (e.g. connection closed) complete the subflow and emit the Disconnected event - private val connectionOutFlowHandler = new OutHandler { - // inner stream pulls, we either give first request or pull upstream - override def onPull(): Unit = { - if (firstRequest != null) { - inflightRequests.add(firstRequest) - connectionFlowSource.push(firstRequest.request) - firstRequest = null - } else pull(in) - } - - override def onDownstreamFinish(): Unit = connectionFlowSource.complete() - } - - // SinkInlet is connected to the connectionFlow's outlet, an upstream - // complete indicates the remote has shutdown cleanly, a failure is - // abnormal termination/connection refused. Successful requests - // will show up in `onPush` - private val connectionInFlowHandler = new InHandler { - // inner stream pushes we push downstream - override def onPush(): Unit = { - val response = connectionFlowSink.grab() - val requestContext = inflightRequests.pop - - val (entity, whenCompleted) = HttpEntity.captureTermination(response.entity) - import fm.executionContext - push(responsesOut, ResponseContext(requestContext, Success(response withEntity entity))) - push(eventsOut, SlotEvent.RequestCompletedFuture(whenCompleted.map(_ ⇒ SlotEvent.RequestCompleted(slotIx)))) - } - - override def onUpstreamFinish(): Unit = disconnect() - - override def onUpstreamFailure(ex: Throwable): Unit = disconnect(Some(ex)) - } - - // upstream pushes we create the inner stream if necessary or push if we're already connected - override def onPush(): Unit = { - def establishConnectionFlow() = { - connectionFlowSource = new SubSourceOutlet[HttpRequest]("RequestSource") - connectionFlowSource.setHandler(connectionOutFlowHandler) - - connectionFlowSink = new SubSinkInlet[HttpResponse]("ResponseSink") - connectionFlowSink.setHandler(connectionInFlowHandler) - - isConnected = true - - Source.fromGraph(connectionFlowSource.source) - .via(connectionFlow).runWith(Sink.fromGraph(connectionFlowSink.sink))(subFusingMaterializer) - - connectionFlowSink.pull() - } - - grab(in) match { - case ConnectEagerlyCommand ⇒ - if (!isConnected) establishConnectionFlow() - - emit(eventsOut, ConnectedEagerly(slotIx)) - - case DispatchCommand(rc: RequestContext) ⇒ - if (isConnected) { - inflightRequests.add(rc) - connectionFlowSource.push(rc.request) - } else { - firstRequest = rc - establishConnectionFlow() - } - } - } - - setHandler(in, this) - - setHandler(responsesOut, new OutHandler { - override def onPull(): Unit = { - // downstream pulls, if connected we pull inner - if (isConnected) connectionFlowSink.pull() - else if (!hasBeenPulled(in)) pull(in) - } - }) - - setHandler(eventsOut, EagerTerminateOutput) - } - } -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/BodyPartParser.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/BodyPartParser.scala deleted file mode 100644 index 56eeb9ea98..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/BodyPartParser.scala +++ /dev/null @@ -1,301 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.parsing - -import akka.NotUsed - -import scala.annotation.tailrec -import akka.event.LoggingAdapter -import akka.parboiled2.CharPredicate -import akka.stream.scaladsl.Source -import akka.stream.stage._ -import akka.util.ByteString -import akka.http.scaladsl.model._ -import akka.http.impl.util._ -import akka.stream.{ Attributes, FlowShape, Inlet, Outlet } -import headers._ - -import scala.collection.mutable.ListBuffer -import akka.stream.impl.fusing.SubSource - -/** - * INTERNAL API - * - * see: http://tools.ietf.org/html/rfc2046#section-5.1.1 - */ -private[http] final class BodyPartParser( - defaultContentType: ContentType, - boundary: String, - log: LoggingAdapter, - settings: BodyPartParser.Settings) - extends GraphStage[FlowShape[ByteString, BodyPartParser.Output]] { - import BodyPartParser._ - import settings._ - - require(boundary.nonEmpty, "'boundary' parameter of multipart Content-Type must be non-empty") - require(boundary.charAt(boundary.length - 1) != ' ', "'boundary' parameter of multipart Content-Type must not end with a space char") - require( - boundaryChar matchesAll boundary, - s"'boundary' parameter of multipart Content-Type contains illegal character '${boundaryChar.firstMismatch(boundary).get}'") - - sealed trait StateResult // phantom type for ensuring soundness of our parsing method setup - - private[this] val needle: Array[Byte] = { - val array = new Array[Byte](boundary.length + 4) - array(0) = '\r'.toByte - array(1) = '\n'.toByte - array(2) = '-'.toByte - array(3) = '-'.toByte - boundary.getAsciiBytes(array, 4) - array - } - - // we use the Boyer-Moore string search algorithm for finding the boundaries in the multipart entity, - // TODO: evaluate whether an upgrade to the more efficient FJS is worth the implementation cost - // see: http://www.cgjennings.ca/fjs/ and http://ijes.info/4/1/42544103.pdf - private[this] val boyerMoore = new BoyerMoore(needle) - - // TODO: prevent re-priming header parser from scratch - private[this] val headerParser = HttpHeaderParser(settings, log) { errorInfo ⇒ - if (illegalHeaderWarnings) log.warning(errorInfo.withSummaryPrepended("Illegal multipart header").formatPretty) - } - - val in = Inlet[ByteString]("BodyPartParser.in") - val out = Outlet[BodyPartParser.Output]("BodyPartParser.out") - - override val shape = FlowShape(in, out) - - override def createLogic(attributes: Attributes): GraphStageLogic = - new GraphStageLogic(shape) with InHandler with OutHandler { - private var output = collection.immutable.Queue.empty[Output] // FIXME this probably is too wasteful - private var state: ByteString ⇒ StateResult = tryParseInitialBoundary - private var shouldTerminate = false - - override def onPush(): Unit = { - if (!shouldTerminate) { - val elem = grab(in) - try state(elem) - catch { - case e: ParsingException ⇒ fail(e.info) - case NotEnoughDataException ⇒ - // we are missing a try/catch{continue} wrapper somewhere - throw new IllegalStateException("unexpected NotEnoughDataException", NotEnoughDataException) - } - if (output.nonEmpty) push(out, dequeue()) - else if (!shouldTerminate) pull(in) - else completeStage() - } else completeStage() - } - - override def onPull(): Unit = { - if (output.nonEmpty) push(out, dequeue()) - else if (isClosed(in)) { - if (!shouldTerminate) push(out, ParseError(ErrorInfo("Unexpected end of multipart entity"))) - completeStage() - } else pull(in) - } - - override def onUpstreamFinish(): Unit = { - if (isAvailable(out)) onPull() - } - - setHandlers(in, out, this) - - def warnOnIllegalHeader(errorInfo: ErrorInfo): Unit = - if (illegalHeaderWarnings) log.warning(errorInfo.withSummaryPrepended("Illegal multipart header").formatPretty) - - def tryParseInitialBoundary(input: ByteString): StateResult = - // we don't use boyerMoore here because we are testing for the boundary *without* a - // preceding CRLF and at a known location (the very beginning of the entity) - try { - if (boundary(input, 0)) { - val ix = boundaryLength - if (crlf(input, ix)) parseHeaderLines(input, ix + 2) - else if (doubleDash(input, ix)) setShouldTerminate() - else parsePreamble(input, 0) - } else parsePreamble(input, 0) - } catch { - case NotEnoughDataException ⇒ continue(input, 0)((newInput, _) ⇒ tryParseInitialBoundary(newInput)) - } - - def parsePreamble(input: ByteString, offset: Int): StateResult = - try { - @tailrec def rec(index: Int): StateResult = { - val needleEnd = boyerMoore.nextIndex(input, index) + needle.length - if (crlf(input, needleEnd)) parseHeaderLines(input, needleEnd + 2) - else if (doubleDash(input, needleEnd)) setShouldTerminate() - else rec(needleEnd) - } - rec(offset) - } catch { - case NotEnoughDataException ⇒ continue(input.takeRight(needle.length + 2), 0)(parsePreamble) - } - - @tailrec def parseHeaderLines(input: ByteString, lineStart: Int, headers: ListBuffer[HttpHeader] = ListBuffer[HttpHeader](), - headerCount: Int = 0, cth: Option[`Content-Type`] = None): StateResult = { - def contentType = - cth match { - case Some(x) ⇒ x.contentType - case None ⇒ defaultContentType - } - - var lineEnd = 0 - val resultHeader = - try { - if (!boundary(input, lineStart)) { - lineEnd = headerParser.parseHeaderLine(input, lineStart)() - headerParser.resultHeader - } else BoundaryHeader - } catch { - case NotEnoughDataException ⇒ null - } - resultHeader match { - case null ⇒ continue(input, lineStart)(parseHeaderLinesAux(headers, headerCount, cth)) - - case BoundaryHeader ⇒ - emit(BodyPartStart(headers.toList, _ ⇒ HttpEntity.empty(contentType))) - val ix = lineStart + boundaryLength - if (crlf(input, ix)) parseHeaderLines(input, ix + 2) - else if (doubleDash(input, ix)) setShouldTerminate() - else fail("Illegal multipart boundary in message content") - - case EmptyHeader ⇒ parseEntity(headers.toList, contentType)(input, lineEnd) - - case h: `Content-Type` ⇒ - if (cth.isEmpty) parseHeaderLines(input, lineEnd, headers, headerCount + 1, Some(h)) - else if (cth.get == h) parseHeaderLines(input, lineEnd, headers, headerCount, cth) - else fail("multipart part must not contain more than one Content-Type header") - - case h if headerCount < maxHeaderCount ⇒ - parseHeaderLines(input, lineEnd, headers += h, headerCount + 1, cth) - - case _ ⇒ fail(s"multipart part contains more than the configured limit of $maxHeaderCount headers") - } - } - - // work-around for compiler complaining about non-tail-recursion if we inline this method - def parseHeaderLinesAux(headers: ListBuffer[HttpHeader], headerCount: Int, - cth: Option[`Content-Type`])(input: ByteString, lineStart: Int): StateResult = - parseHeaderLines(input, lineStart, headers, headerCount, cth) - - def parseEntity(headers: List[HttpHeader], contentType: ContentType, - emitPartChunk: (List[HttpHeader], ContentType, ByteString) ⇒ Unit = { - (headers, ct, bytes) ⇒ - emit(BodyPartStart(headers, entityParts ⇒ HttpEntity.IndefiniteLength( - ct, - entityParts.collect { case EntityPart(data) ⇒ data }))) - emit(bytes) - }, - emitFinalPartChunk: (List[HttpHeader], ContentType, ByteString) ⇒ Unit = { - (headers, ct, bytes) ⇒ - emit(BodyPartStart(headers, { rest ⇒ - SubSource.kill(rest) - HttpEntity.Strict(ct, bytes) - })) - })(input: ByteString, offset: Int): StateResult = - try { - @tailrec def rec(index: Int): StateResult = { - val currentPartEnd = boyerMoore.nextIndex(input, index) - def emitFinalChunk() = emitFinalPartChunk(headers, contentType, input.slice(offset, currentPartEnd)) - val needleEnd = currentPartEnd + needle.length - if (crlf(input, needleEnd)) { - emitFinalChunk() - parseHeaderLines(input, needleEnd + 2) - } else if (doubleDash(input, needleEnd)) { - emitFinalChunk() - setShouldTerminate() - } else rec(needleEnd) - } - rec(offset) - } catch { - case NotEnoughDataException ⇒ - // we cannot emit all input bytes since the end of the input might be the start of the next boundary - val emitEnd = input.length - needle.length - 2 - if (emitEnd > offset) { - emitPartChunk(headers, contentType, input.slice(offset, emitEnd)) - val simpleEmit: (List[HttpHeader], ContentType, ByteString) ⇒ Unit = (_, _, bytes) ⇒ emit(bytes) - continue(input drop emitEnd, 0)(parseEntity(null, null, simpleEmit, simpleEmit)) - } else continue(input, offset)(parseEntity(headers, contentType, emitPartChunk, emitFinalPartChunk)) - } - - def emit(bytes: ByteString): Unit = if (bytes.nonEmpty) emit(EntityPart(bytes)) - - def emit(element: Output): Unit = output = output.enqueue(element) - - def dequeue(): Output = { - val head = output.head - output = output.tail - head - } - - def continue(input: ByteString, offset: Int)(next: (ByteString, Int) ⇒ StateResult): StateResult = { - state = - math.signum(offset - input.length) match { - case -1 ⇒ more ⇒ next(input ++ more, offset) - case 0 ⇒ next(_, 0) - case 1 ⇒ throw new IllegalStateException - } - done() - } - - def continue(next: (ByteString, Int) ⇒ StateResult): StateResult = { - state = next(_, 0) - done() - } - - def fail(summary: String): StateResult = fail(ErrorInfo(summary)) - - def fail(info: ErrorInfo): StateResult = { - emit(ParseError(info)) - setShouldTerminate() - } - - def setShouldTerminate(): StateResult = { - shouldTerminate = true - done() - } - - def done(): StateResult = null // StateResult is a phantom type - - // the length of the needle without the preceding CRLF - def boundaryLength = needle.length - 2 - - @tailrec def boundary(input: ByteString, offset: Int, ix: Int = 2): Boolean = - (ix == needle.length) || (byteAt(input, offset + ix - 2) == needle(ix)) && boundary(input, offset, ix + 1) - - def crlf(input: ByteString, offset: Int): Boolean = - byteChar(input, offset) == '\r' && byteChar(input, offset + 1) == '\n' - - def doubleDash(input: ByteString, offset: Int): Boolean = - byteChar(input, offset) == '-' && byteChar(input, offset + 1) == '-' - } -} - -private[http] object BodyPartParser { - // http://tools.ietf.org/html/rfc2046#section-5.1.1 - val boundaryChar = CharPredicate.Digit ++ CharPredicate.Alpha ++ "'()+_,-./:=? " - - private object BoundaryHeader extends HttpHeader { - def renderInRequests = false - def renderInResponses = false - def name = "" - def lowercaseName = "" - def value = "" - def render[R <: Rendering](r: R): r.type = r - override def toString = "BoundaryHeader" - } - - sealed trait Output - sealed trait PartStart extends Output - final case class BodyPartStart(headers: List[HttpHeader], createEntity: Source[Output, NotUsed] ⇒ BodyPartEntity) extends PartStart - final case class EntityPart(data: ByteString) extends Output - final case class ParseError(info: ErrorInfo) extends PartStart - - abstract class Settings extends HttpHeaderParser.Settings { - def maxHeaderCount: Int - def illegalHeaderWarnings: Boolean - def defaultHeaderValueCacheLimit: Int - } -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/BoyerMoore.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/BoyerMoore.scala deleted file mode 100644 index b8ba9557b6..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/BoyerMoore.scala +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.parsing - -import scala.annotation.tailrec -import akka.util.ByteString - -/** - * Straight-forward Boyer-Moore string search implementation. - */ -private class BoyerMoore(needle: Array[Byte]) { - require(needle.length > 0, "needle must be non-empty") - - private[this] val nl1 = needle.length - 1 - - private[this] val charTable: Array[Int] = { - val table = Array.fill(256)(needle.length) - @tailrec def rec(i: Int): Unit = - if (i < nl1) { - table(needle(i) & 0xff) = nl1 - i - rec(i + 1) - } - rec(0) - table - } - - private[this] val offsetTable: Array[Int] = { - val table = new Array[Int](needle.length) - - @tailrec def isPrefix(i: Int, j: Int): Boolean = - i == needle.length || needle(i) == needle(j) && isPrefix(i + 1, j + 1) - @tailrec def loop1(i: Int, lastPrefixPosition: Int): Unit = - if (i >= 0) { - val nextLastPrefixPosition = if (isPrefix(i + 1, 0)) i + 1 else lastPrefixPosition - table(nl1 - i) = nextLastPrefixPosition - i + nl1 - loop1(i - 1, nextLastPrefixPosition) - } - loop1(nl1, needle.length) - - @tailrec def suffixLength(i: Int, j: Int, result: Int): Int = - if (i >= 0 && needle(i) == needle(j)) suffixLength(i - 1, j - 1, result + 1) else result - @tailrec def loop2(i: Int): Unit = - if (i < nl1) { - val sl = suffixLength(i, nl1, 0) - table(sl) = nl1 - i + sl - loop2(i + 1) - } - loop2(0) - table - } - - /** - * Returns the index of the next occurrence of `needle` in `haystack` that is >= `offset`. - * If none is found a `NotEnoughDataException` is thrown. - */ - def nextIndex(haystack: ByteString, offset: Int): Int = { - @tailrec def rec(i: Int, j: Int): Int = { - val byte = byteAt(haystack, i) - if (needle(j) == byte) { - if (j == 0) i // found - else rec(i - 1, j - 1) - } else rec(i + math.max(offsetTable(nl1 - j), charTable(byte & 0xff)), nl1) - } - rec(offset + nl1, nl1) - } -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/HttpHeaderParser.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/HttpHeaderParser.scala deleted file mode 100644 index 867e39680b..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/HttpHeaderParser.scala +++ /dev/null @@ -1,594 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.parsing - -import java.nio.{ CharBuffer, ByteBuffer } -import java.util.Arrays.copyOf -import java.lang.{ StringBuilder ⇒ JStringBuilder } -import akka.event.LoggingAdapter -import akka.http.scaladsl.settings.ParserSettings.IllegalResponseHeaderValueProcessingMode -import akka.http.scaladsl.settings.ParserSettings - -import scala.annotation.tailrec -import akka.parboiled2.CharUtils -import akka.util.ByteString -import akka.http.impl.util._ -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers.{ EmptyHeader, RawHeader } -import akka.http.impl.model.parser.HeaderParser -import akka.http.impl.model.parser.CharacterClasses._ - -/** - * INTERNAL API - * - * Provides for time- and space-efficient parsing of an HTTP header line in an HTTP message. - * It keeps a cache of all headers encountered in a previous request, so as to avoid reparsing and recreation of header - * model instances. - * For the life-time of one HTTP connection an instance of this class is owned by the connection, i.e. not shared - * with other connections. After the connection is closed it may be used by subsequent connections. - * - * The core of this parser/cache is a mutable space-efficient ternary trie (prefix tree) structure, whose data are - * split across three arrays. The tree supports node addition and update, but no deletion (i.e. we never remove - * entries). - * - * The `nodes` array keeps the main data of the trie nodes. One node is formed of a char (2 bytes) of which the - * LSB (least significant byte) is an ASCII octet and the MSB either 0 or an index into the `branchData` array. - * There are three types of nodes: - * - * 1. Simple nodes: non-branching, non-leaf nodes (the most frequent kind of node) have an MSB of zero and exactly - * one logical sub-node which is the next node in the `nodes` array (i.e. the one at index n + 1). - * 2. Branching nodes: have 2 or three children and a non-zero MSB. (MSB value - 1)*3 is the row index into the - * `branchData` array, which contains the branching data. - * 3. Leaf nodes: have no sub-nodes and no character value. They merely "stop" the node chain and point to a value. - * The LSB of leaf nodes is zero and the (MSB value - 1) is an index into the values array. - * - * This design has the following consequences: - * - Since only leaf nodes can have values the trie cannot store keys that are prefixes of other stored keys. - * - If the trie stores n values it has less than n branching nodes (adding the first value does not create a - * branching node, the addition of every subsequent value creates at most one additional branching node). - * - If the trie has n branching nodes it stores at least n * 2 and at most n * 3 values. - * - * The `branchData` array keeps the branching data for branching nodes in the trie. - * It's a flattened two-dimensional array with a row consisting of the following 3 signed 16-bit integers: - * row-index + 0: if non-zero: index into the `nodes` array for the "lesser" child of the node - * row-index + 1: if non-zero: index into the `nodes` array for the "equal" child of the node - * row-index + 2: if non-zero: index into the `nodes` array for the "greater" child of the node - * The array has a fixed size of 254 rows (since we can address at most 256 - 1 values with the node MSB and - * we always have fewer branching nodes than values). - * - * The `values` array contains the payload data addressed by trie leaf nodes. - * Since we address them via the nodes MSB and zero is reserved the trie - * cannot hold more then 255 items, so this array has a fixed size of 255. - */ -private[engine] final class HttpHeaderParser private ( - val settings: HttpHeaderParser.Settings, - val log: LoggingAdapter, - onIllegalHeader: ErrorInfo ⇒ Unit, - private[this] var nodes: Array[Char] = new Array(512), // initial size, can grow as needed - private[this] var nodeCount: Int = 0, - private[this] var branchData: Array[Short] = new Array(254 * 3), - private[this] var branchDataCount: Int = 0, - private[this] var values: Array[AnyRef] = new Array(255), // fixed size of 255 - private[this] var valueCount: Int = 0, - private[this] var trieIsPrivate: Boolean = false) { // signals the trie data can be mutated w/o having to copy first - - // TODO: evaluate whether switching to a value-class-based approach allows us to improve code readability without sacrificing performance - - import HttpHeaderParser._ - import settings._ - - /** - * Contains the parsed header instance after a call to `parseHeaderLine`. - */ - var resultHeader: HttpHeader = EmptyHeader - - def isEmpty = nodeCount == 0 - - /** - * Returns a copy of this parser that shares the trie data with this instance. - */ - def createShallowCopy(): HttpHeaderParser = - new HttpHeaderParser(settings, log, onIllegalHeader, nodes, nodeCount, branchData, branchDataCount, values, valueCount) - - /** - * Parses a header line and returns the line start index of the subsequent line. - * The parsed header instance is written to the `resultHeader` member. - * If the trie still has space for this type of header (and as a whole) the parsed header is cached. - * Throws a `NotEnoughDataException` if the given input doesn't contain enough data to fully parse the given header - * line. Note that there must be at least one byte available after a CRLF sequence, in order to distinguish a true - * line ending from a line fold. - * If the header is invalid a respective `ParsingException` is thrown. - */ - @tailrec - def parseHeaderLine(input: ByteString, lineStart: Int = 0)(cursor: Int = lineStart, nodeIx: Int = 0): Int = { - def startValueBranch(rootValueIx: Int, valueParser: HeaderValueParser) = { - val (header, endIx) = valueParser(this, input, cursor, onIllegalHeader) - if (valueParser.cachingEnabled) - try { - val valueIx = newValueIndex // compute early in order to trigger OutOfTrieSpaceExceptions before any change - unshareIfRequired() - val nodeIx = nodeCount - insertRemainingCharsAsNewNodes(input, header)(cursor, endIx, valueIx) - values(rootValueIx) = ValueBranch(rootValueIx, valueParser, branchRootNodeIx = nodeIx, valueCount = 1) - } catch { - case OutOfTrieSpaceException ⇒ // if we cannot insert a value then we simply don't - } - resultHeader = header - endIx - } - val node = nodes(nodeIx) - node & 0xFF match { - case 0 ⇒ // leaf node (or intermediate ValueBranch pointer) - val valueIx = (node >>> 8) - 1 - values(valueIx) match { - case branch: ValueBranch ⇒ parseHeaderValue(input, cursor, branch)() - case valueParser: HeaderValueParser ⇒ startValueBranch(valueIx, valueParser) // no header yet of this type - case EmptyHeader ⇒ resultHeader = EmptyHeader; cursor - } - case nodeChar ⇒ - val char = CharUtils.toLowerCase(byteChar(input, cursor)) - if (char == node) // fast match, advance and descend - parseHeaderLine(input, lineStart)(cursor + 1, nodeIx + 1) - else node >>> 8 match { - case 0 ⇒ // header doesn't exist yet and has no model (since we have not yet seen a colon) - parseRawHeader(input, lineStart, cursor, nodeIx) - case msb ⇒ // branching node - val signum = math.signum(char - nodeChar) - branchData(rowIx(msb) + 1 + signum) match { - case 0 ⇒ // header doesn't exist yet and has no model (otherwise we'd arrive at a value) - parseRawHeader(input, lineStart, cursor, nodeIx) - case subNodeIx ⇒ // descend into branch and advance on char matches (otherwise descend but don't advance) - parseHeaderLine(input, lineStart)(cursor + 1 - math.abs(signum), subNodeIx) - } - } - } - } - - private def parseRawHeader(input: ByteString, lineStart: Int, cursor: Int, nodeIx: Int): Int = { - val colonIx = scanHeaderNameAndReturnIndexOfColon(input, lineStart, lineStart + 1 + maxHeaderNameLength)(cursor) - val headerName = asciiString(input, lineStart, colonIx) - try { - val valueParser = new RawHeaderValueParser(headerName, maxHeaderValueLength, - headerValueCacheLimit(headerName), log, illegalResponseHeaderValueProcessingMode) - insert(input, valueParser)(cursor, colonIx + 1, nodeIx, colonIx) - parseHeaderLine(input, lineStart)(cursor, nodeIx) - } catch { - case OutOfTrieSpaceException ⇒ // if we cannot insert we drop back to simply creating new header instances - val (headerValue, endIx) = scanHeaderValue(this, input, colonIx + 1, colonIx + maxHeaderValueLength + 3, - log, settings.illegalResponseHeaderValueProcessingMode)() - resultHeader = RawHeader(headerName, headerValue.trim) - endIx - } - } - - @tailrec - private def parseHeaderValue(input: ByteString, valueStart: Int, branch: ValueBranch)(cursor: Int = valueStart, nodeIx: Int = branch.branchRootNodeIx): Int = { - def parseAndInsertHeader() = { - val (header, endIx) = branch.parser(this, input, valueStart, onIllegalHeader) - if (branch.spaceLeft) - try { - insert(input, header)(cursor, endIx, nodeIx, colonIx = 0) - values(branch.valueIx) = branch.withValueCountIncreased - } catch { case OutOfTrieSpaceException ⇒ /* if we cannot insert then we simply don't */ } - resultHeader = header - endIx - } - val char = byteChar(input, cursor) - val node = nodes(nodeIx) - if (char == node) // fast match, descend - parseHeaderValue(input, valueStart, branch)(cursor + 1, nodeIx + 1) - else node >>> 8 match { - case 0 ⇒ parseAndInsertHeader() - case msb ⇒ node & 0xFF match { - case 0 ⇒ // leaf node - resultHeader = values(msb - 1).asInstanceOf[HttpHeader] - cursor - case nodeChar ⇒ // branching node - val signum = math.signum(char - nodeChar) - branchData(rowIx(msb) + 1 + signum) match { - case 0 ⇒ parseAndInsertHeader() // header doesn't exist yet - case subNodeIx ⇒ // descend into branch and advance on char matches (otherwise descend but don't advance) - parseHeaderValue(input, valueStart, branch)(cursor + 1 - math.abs(signum), subNodeIx) - } - } - } - } - - /** - * Inserts a value into the cache trie. - * CAUTION: this method must only be called if: - * - the trie is not empty (use `insertRemainingCharsAsNewNodes` for inserting the very first value) - * - the input does not contain illegal characters - * - the input is not a prefix of an already stored value, i.e. the input must be properly terminated (CRLF or colon) - */ - @tailrec - private def insert(input: ByteString, value: AnyRef)(cursor: Int = 0, endIx: Int = input.length, nodeIx: Int = 0, colonIx: Int = 0): Unit = { - val char = - if (cursor < colonIx) CharUtils.toLowerCase(input(cursor).toChar) - else if (cursor < endIx) input(cursor).toChar - else '\u0000' - val node = nodes(nodeIx) - if (char == node) insert(input, value)(cursor + 1, endIx, nodeIx + 1, colonIx) // fast match, descend into only subnode - else { - val nodeChar = node & 0xFF - val signum = math.signum(char - nodeChar) - node >>> 8 match { - case 0 ⇒ // input doesn't exist yet in the trie, insert - val valueIx = newValueIndex // compute early in order to trigger OutOfTrieSpaceExceptions before any change - val rowIx = newBranchDataRowIndex - unshareIfRequired() - val newNodeIx = nodeCount.toShort - insertRemainingCharsAsNewNodes(input, value)(cursor, endIx, valueIx, colonIx) - nodes(nodeIx) = nodeBits(rowIx, nodeChar) - branchData(rowIx + 1) = (nodeIx + 1).toShort - branchData(rowIx + 1 + signum) = newNodeIx - case msb ⇒ - if (nodeChar == 0) { // leaf node - require(cursor == endIx, "Cannot insert key of which a prefix already has a value") - values(msb - 1) = value // override existing entry - } else { - val branchIndex = rowIx(msb) + 1 + signum - branchData(branchIndex) match { // branching node - case 0 ⇒ // branch doesn't exist yet, create - val valueIx = newValueIndex // compute early in order to trigger OutOfTrieSpaceExceptions before any change - unshareIfRequired() - val newNodeIx = nodeCount.toShort - insertRemainingCharsAsNewNodes(input, value)(cursor, endIx, valueIx, colonIx) - branchData(branchIndex) = newNodeIx // make the previously implicit "equals" sub node explicit - case subNodeIx ⇒ // descend, but advance only on match - insert(input, value)(cursor + 1 - math.abs(signum), endIx, subNodeIx, colonIx) - } - } - } - } - } - - /** - * Inserts a value into the cache trie as new nodes. - * CAUTION: this method must only be called if the trie data have already been "unshared"! - */ - @tailrec - private def insertRemainingCharsAsNewNodes(input: ByteString, value: AnyRef)(cursor: Int = 0, endIx: Int = input.length, valueIx: Int = newValueIndex, colonIx: Int = 0): Unit = { - val newNodeIx = newNodeIndex - if (cursor < endIx) { - val c = input(cursor).toChar - val char = if (cursor < colonIx) CharUtils.toLowerCase(c) else c - nodes(newNodeIx) = char - insertRemainingCharsAsNewNodes(input, value)(cursor + 1, endIx, valueIx, colonIx) - } else { - values(valueIx) = value - nodes(newNodeIx) = ((valueIx + 1) << 8).toChar - } - } - - private def unshareIfRequired(): Unit = - if (!trieIsPrivate) { - nodes = copyOf(nodes, nodes.length) - branchData = copyOf(branchData, branchData.length) - values = copyOf(values, values.length) - trieIsPrivate = true - } - - private def newNodeIndex: Int = { - val index = nodeCount - if (index < Short.MaxValue) { - if (index == nodes.length) nodes = copyOf(nodes, math.min(index * 3 / 2, Short.MaxValue)) - nodeCount = index + 1 - index - } else throw OutOfTrieSpaceException - } - - private def newBranchDataRowIndex: Int = { - val index = branchDataCount - branchDataCount = index + 3 - index - } - - private def newValueIndex: Int = { - val index = valueCount - if (index < values.length) { - valueCount = index + 1 - index - } else throw OutOfTrieSpaceException - } - - private def rowIx(msb: Int) = (msb - 1) * 3 - private def nodeBits(rowIx: Int, char: Int) = (((rowIx / 3 + 1) << 8) | char).toChar - - /** - * Renders the trie structure into an ASCII representation. - */ - def formatTrie: String = { - def recurse(nodeIx: Int = 0): (Seq[List[String]], Int) = { - def recurseAndPrefixLines(subNodeIx: Int, p1: String, p2: String, p3: String) = { - val (lines, mainIx) = recurse(subNodeIx) - val prefixedLines = lines.zipWithIndex map { - case (line, ix) ⇒ (if (ix < mainIx) p1 else if (ix > mainIx) p3 else p2) :: line - } - prefixedLines → mainIx - } - def branchLines(dataIx: Int, p1: String, p2: String, p3: String) = branchData(dataIx) match { - case 0 ⇒ Seq.empty - case subNodeIx ⇒ recurseAndPrefixLines(subNodeIx, p1, p2, p3)._1 - } - val node = nodes(nodeIx) - val char = escape((node & 0xFF).toChar) - node >>> 8 match { - case 0 ⇒ recurseAndPrefixLines(nodeIx + 1, " ", char + "-", " ") - case msb ⇒ node & 0xFF match { - case 0 ⇒ values(msb - 1) match { - case ValueBranch(_, valueParser, branchRootNodeIx, _) ⇒ - val pad = " " * (valueParser.headerName.length + 3) - recurseAndPrefixLines(branchRootNodeIx, pad, "(" + valueParser.headerName + ")-", pad) - case vp: HeaderValueParser ⇒ Seq(" (" :: vp.headerName :: ")" :: Nil) → 0 - case value: RawHeader ⇒ Seq(" *" :: value.toString :: Nil) → 0 - case value ⇒ Seq(" " :: value.toString :: Nil) → 0 - } - case nodeChar ⇒ - val rix = rowIx(msb) - val preLines = branchLines(rix, " ", "┌─", "| ") - val postLines = branchLines(rix + 2, "| ", "└─", " ") - val p1 = if (preLines.nonEmpty) "| " else " " - val p3 = if (postLines.nonEmpty) "| " else " " - val (matchLines, mainLineIx) = recurseAndPrefixLines(branchData(rix + 1), p1, char + '-', p3) - (preLines ++ matchLines ++ postLines, mainLineIx + preLines.size) - } - } - } - val sb = new JStringBuilder() - val (lines, mainLineIx) = recurse() - lines.zipWithIndex foreach { - case (line, ix) ⇒ - sb.append(if (ix == mainLineIx) '-' else ' ') - line foreach (s ⇒ sb.append(s)) - sb.append('\n') - } - sb.toString - } - - /** - * Returns the number of header values stored per header type. - */ - def contentHistogram: Map[String, Int] = { - def build(nodeIx: Int = 0): Map[String, Int] = { - val node = nodes(nodeIx) - node >>> 8 match { - case 0 ⇒ build(nodeIx + 1) - case msb if (node & 0xFF) == 0 ⇒ values(msb - 1) match { - case ValueBranch(_, parser, _, count) ⇒ Map(parser.headerName → count) - case _ ⇒ Map.empty - } - case msb ⇒ - def branch(ix: Int): Map[String, Int] = if (ix > 0) build(ix) else Map.empty - val rix = rowIx(msb) - branch(branchData(rix + 0)) ++ branch(branchData(rix + 1)) ++ branch(branchData(rix + 2)) - } - } - build() - } - - /** - * Returns a string representation of the raw trie data. - */ - def formatRawTrie: String = { - def char(c: Char) = (c >> 8).toString + (if ((c & 0xFF) > 0) "/" + (c & 0xFF).toChar else "/Ω") - s"nodes: ${nodes take nodeCount map char mkString ", "}\n" + - s"branchData: ${branchData take branchDataCount grouped 3 map { case Array(a, b, c) ⇒ s"$a/$b/$c" } mkString ", "}\n" + - s"values: ${values take valueCount mkString ", "}" - } - - /** - * Returns a string representation of the trie structure size. - */ - def formatSizes: String = s"$nodeCount nodes, ${branchDataCount / 3} branchData rows, $valueCount values" - - // helpers for UTF-8 decoding, - // since they are only accessed when an UTF8 byte sequence is actually hit and UTF-8 sequences in header values are - // rare these fields can be lazy, the overhead of the lazy access should be overcompensated for by the saved - // allocations in the majority of cases - private lazy val byteBuffer = ByteBuffer.allocate(4) - private lazy val charBuffer = CharBuffer.allocate(2) - private lazy val decoder = UTF8.newDecoder() - - // returns the decoded character as a simple 16-bit Char value or a 32-bit surrogate pair - // or -1 if the byteBuffer bytes are not a complete and legal UTF-8 byte sequence - private def decodeByteBuffer(): Int = { - byteBuffer.flip() - val coderResult = decoder.decode(byteBuffer, charBuffer, false) - charBuffer.flip() - val result = - if (coderResult.isUnderflow & charBuffer.hasRemaining) { - val c = charBuffer.get() - if (charBuffer.hasRemaining) (charBuffer.get() << 16) | c else c - } else -1 - byteBuffer.clear() - charBuffer.clear() - result - } -} - -/** - * INTERNAL API - */ -private[http] object HttpHeaderParser { - import SpecializedHeaderValueParsers._ - - abstract class Settings extends HeaderParser.Settings { - def maxHeaderNameLength: Int - def maxHeaderValueLength: Int - def headerValueCacheLimit(headerName: String): Int - def customMediaTypes: MediaTypes.FindCustom - def illegalResponseHeaderValueProcessingMode: IllegalResponseHeaderValueProcessingMode - } - - private def predefinedHeaders = Seq( - "Accept: *", - "Accept: */*", - "Connection: Keep-Alive", - "Connection: close", - "Connection: keep-alive", - "Content-Length: 0", - "Cache-Control: max-age=0", - "Cache-Control: no-cache", - "Expect: 100-continue") - - def apply(settings: HttpHeaderParser.Settings, log: LoggingAdapter)(onIllegalHeader: ErrorInfo ⇒ Unit = info ⇒ throw IllegalHeaderException(info)) = - prime(unprimed(settings, log, onIllegalHeader)) - - def unprimed(settings: HttpHeaderParser.Settings, log: LoggingAdapter, warnOnIllegalHeader: ErrorInfo ⇒ Unit) = - new HttpHeaderParser(settings, log, warnOnIllegalHeader) - - def prime(parser: HttpHeaderParser): HttpHeaderParser = { - val valueParsers: Seq[HeaderValueParser] = - HeaderParser.ruleNames.map { name ⇒ - new ModeledHeaderValueParser(name, parser.settings.maxHeaderValueLength, parser.settings.headerValueCacheLimit(name), parser.log, parser.settings) - }(collection.breakOut) - def insertInGoodOrder(items: Seq[Any])(startIx: Int = 0, endIx: Int = items.size): Unit = - if (endIx - startIx > 0) { - val pivot = (startIx + endIx) / 2 - items(pivot) match { - case valueParser: HeaderValueParser ⇒ - val insertName = valueParser.headerName.toRootLowerCase + ':' - if (parser.isEmpty) parser.insertRemainingCharsAsNewNodes(ByteString(insertName), valueParser)() - else parser.insert(ByteString(insertName), valueParser)() - case header: String ⇒ - parser.parseHeaderLine(ByteString(header + "\r\nx"))() - } - insertInGoodOrder(items)(startIx, pivot) - insertInGoodOrder(items)(pivot + 1, endIx) - } - insertInGoodOrder(valueParsers.sortBy(_.headerName))() - insertInGoodOrder(specializedHeaderValueParsers)() - insertInGoodOrder(predefinedHeaders.sorted)() - parser.insert(ByteString("\r\n"), EmptyHeader)() - parser - } - - // helper forwarders for testing - def insert(parser: HttpHeaderParser, input: ByteString, value: AnyRef): Unit = - parser.insert(input, value)() - def insertRemainingCharsAsNewNodes(parser: HttpHeaderParser, input: ByteString, value: AnyRef): Unit = - parser.insertRemainingCharsAsNewNodes(input, value)() - - private[parsing] abstract class HeaderValueParser(val headerName: String, val maxValueCount: Int) { - def apply(hhp: HttpHeaderParser, input: ByteString, valueStart: Int, onIllegalHeader: ErrorInfo ⇒ Unit): (HttpHeader, Int) - override def toString: String = s"HeaderValueParser[$headerName]" - def cachingEnabled = maxValueCount > 0 - } - - private[parsing] class ModeledHeaderValueParser(headerName: String, maxHeaderValueLength: Int, maxValueCount: Int, log: LoggingAdapter, settings: HeaderParser.Settings) - extends HeaderValueParser(headerName, maxValueCount) { - def apply(hhp: HttpHeaderParser, input: ByteString, valueStart: Int, onIllegalHeader: ErrorInfo ⇒ Unit): (HttpHeader, Int) = { - // TODO: optimize by running the header value parser directly on the input ByteString (rather than an extracted String); seems done? - val (headerValue, endIx) = scanHeaderValue(hhp, input, valueStart, valueStart + maxHeaderValueLength + 2, log, settings.illegalResponseHeaderValueProcessingMode)() - val trimmedHeaderValue = headerValue.trim - val header = HeaderParser.parseFull(headerName, trimmedHeaderValue, settings) match { - case Right(h) ⇒ h - case Left(error) ⇒ - onIllegalHeader(error.withSummaryPrepended(s"Illegal '$headerName' header")) - RawHeader(headerName, trimmedHeaderValue) - } - header → endIx - } - } - - private[parsing] class RawHeaderValueParser(headerName: String, maxHeaderValueLength: Int, maxValueCount: Int, - log: LoggingAdapter, mode: IllegalResponseHeaderValueProcessingMode) extends HeaderValueParser(headerName, maxValueCount) { - def apply(hhp: HttpHeaderParser, input: ByteString, valueStart: Int, onIllegalHeader: ErrorInfo ⇒ Unit): (HttpHeader, Int) = { - val (headerValue, endIx) = scanHeaderValue(hhp, input, valueStart, valueStart + maxHeaderValueLength + 2, log, mode)() - RawHeader(headerName, headerValue.trim) → endIx - } - } - - @tailrec private def scanHeaderNameAndReturnIndexOfColon(input: ByteString, start: Int, limit: Int)(ix: Int): Int = - if (ix < limit) - byteChar(input, ix) match { - case ':' ⇒ ix - case c if tchar(c) ⇒ scanHeaderNameAndReturnIndexOfColon(input, start, limit)(ix + 1) - case c ⇒ fail(s"Illegal character '${escape(c)}' in header name") - } - else fail(s"HTTP header name exceeds the configured limit of ${limit - start - 1} characters") - - @tailrec private def scanHeaderValue(hhp: HttpHeaderParser, input: ByteString, start: Int, limit: Int, log: LoggingAdapter, - mode: IllegalResponseHeaderValueProcessingMode)(sb: JStringBuilder = null, ix: Int = start): (String, Int) = { - - def appended(c: Char) = (if (sb != null) sb else new JStringBuilder(asciiString(input, start, ix))).append(c) - def appended2(c: Int) = if ((c >> 16) != 0) appended(c.toChar).append((c >> 16).toChar) else appended(c.toChar) - if (ix < limit) - byteChar(input, ix) match { - case '\t' ⇒ scanHeaderValue(hhp, input, start, limit, log, mode)(appended(' '), ix + 1) - case '\r' if byteChar(input, ix + 1) == '\n' ⇒ - if (WSP(byteChar(input, ix + 2))) scanHeaderValue(hhp, input, start, limit, log, mode)(appended(' '), ix + 3) - else (if (sb != null) sb.toString else asciiString(input, start, ix), ix + 2) - case c ⇒ - var nix = ix + 1 - val nsb = - if (' ' <= c && c <= '\u007F') if (sb != null) sb.append(c) else null // legal 7-Bit ASCII - else if ((c & 0xE0) == 0xC0) { // 2-byte UTF-8 sequence? - hhp.byteBuffer.put(c.toByte) - hhp.byteBuffer.put(byteAt(input, ix + 1)) - nix = ix + 2 - hhp.decodeByteBuffer() match { // if we cannot decode as UTF8 we don't decode but simply copy - case -1 ⇒ if (sb != null) sb.append(c).append(byteChar(input, ix + 1)) else null - case cc ⇒ appended2(cc) - } - } else if ((c & 0xF0) == 0xE0) { // 3-byte UTF-8 sequence? - hhp.byteBuffer.put(c.toByte) - hhp.byteBuffer.put(byteAt(input, ix + 1)) - hhp.byteBuffer.put(byteAt(input, ix + 2)) - nix = ix + 3 - hhp.decodeByteBuffer() match { // if we cannot decode as UTF8 we don't decode but simply copy - case -1 ⇒ if (sb != null) sb.append(c).append(byteChar(input, ix + 1)).append(byteChar(input, ix + 2)) else null - case cc ⇒ appended2(cc) - } - } else if ((c & 0xF8) == 0xF0) { // 4-byte UTF-8 sequence? - hhp.byteBuffer.put(c.toByte) - hhp.byteBuffer.put(byteAt(input, ix + 1)) - hhp.byteBuffer.put(byteAt(input, ix + 2)) - hhp.byteBuffer.put(byteAt(input, ix + 3)) - nix = ix + 4 - hhp.decodeByteBuffer() match { // if we cannot decode as UTF8 we don't decode but simply copy - case -1 ⇒ if (sb != null) sb.append(c).append(byteChar(input, ix + 1)).append(byteChar(input, ix + 2)).append(byteChar(input, ix + 3)) else null - case cc ⇒ appended2(cc) - } - } else { - mode match { - case ParserSettings.IllegalResponseHeaderValueProcessingMode.Error ⇒ - fail(s"Illegal character '${escape(c)}' in header value") - case ParserSettings.IllegalResponseHeaderValueProcessingMode.Warn ⇒ - // ignore the illegal character and log a warning message - log.warning(s"Illegal character '${escape(c)}' in header value") - sb - case ParserSettings.IllegalResponseHeaderValueProcessingMode.Ignore ⇒ - // just ignore the illegal character - sb - } - - } - scanHeaderValue(hhp, input, start, limit, log, mode)(nsb, nix) - } - else fail(s"HTTP header value exceeds the configured limit of ${limit - start - 2} characters") - } - - def fail(summary: String) = throw new ParsingException(StatusCodes.BadRequest, ErrorInfo(summary)) - - private object OutOfTrieSpaceException extends SingletonException - - /** - * Instances of this class are added as "intermediate" values into the trie at the point where the header name has - * been parsed (so we know the header type). - * These instances behave like regular values (i.e. they live in the `values` array) but encapsulate meta information - * about the trie header-type-specific branch below them. - * - * @param valueIx The index into the `values` array that contains this instance (kind of a "self" pointer) - * @param parser The parser instance used to create the actual header model class for headers of this type - * @param branchRootNodeIx the nodeIx for the root node of the trie branch holding all cached header values of this type - * @param valueCount the number of values already stored in this header-type-specific branch - */ - private final case class ValueBranch(valueIx: Int, parser: HeaderValueParser, branchRootNodeIx: Int, valueCount: Int) { - def withValueCountIncreased = copy(valueCount = valueCount + 1) - def spaceLeft = valueCount < parser.maxValueCount - } -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/HttpMessageParser.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/HttpMessageParser.scala deleted file mode 100644 index bb6cd112ef..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/HttpMessageParser.scala +++ /dev/null @@ -1,357 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.parsing - -import javax.net.ssl.SSLSession - -import akka.stream.TLSProtocol._ - -import scala.annotation.tailrec -import scala.collection.mutable.ListBuffer -import akka.parboiled2.CharUtils -import akka.util.ByteString -import akka.stream.stage._ -import akka.http.impl.model.parser.CharacterClasses -import akka.http.scaladsl.settings.ParserSettings -import akka.http.scaladsl.model._ -import headers._ -import HttpProtocols._ -import ParserOutput._ -import akka.stream.{ Attributes, FlowShape, Inlet, Outlet } - -/** - * INTERNAL API - * - * Common logic for http request and response message parsing - */ -private[http] trait HttpMessageParser[Output >: MessageOutput <: ParserOutput] { - - import HttpMessageParser._ - - protected final val result = new ListBuffer[Output] - private[this] var state: ByteString ⇒ StateResult = startNewMessage(_, 0) - private[this] var protocol: HttpProtocol = `HTTP/1.1` - protected var completionHandling: CompletionHandling = CompletionOk - protected var terminated = false - - private[this] var lastSession: SSLSession = null // used to prevent having to recreate header on each message - private[this] var tlsSessionInfoHeader: `Tls-Session-Info` = null - - protected def settings: ParserSettings - protected def headerParser: HttpHeaderParser - /** invoked if the specified protocol is unknown */ - protected def onBadProtocol(): Nothing - protected def parseMessage(input: ByteString, offset: Int): HttpMessageParser.StateResult - protected def parseEntity(headers: List[HttpHeader], protocol: HttpProtocol, input: ByteString, bodyStart: Int, - clh: Option[`Content-Length`], cth: Option[`Content-Type`], teh: Option[`Transfer-Encoding`], - expect100continue: Boolean, hostHeaderPresent: Boolean, closeAfterResponseCompletion: Boolean): HttpMessageParser.StateResult - - protected final def initialHeaderBuffer: ListBuffer[HttpHeader] = - if (settings.includeTlsSessionInfoHeader && tlsSessionInfoHeader != null) ListBuffer(tlsSessionInfoHeader) - else ListBuffer() - - final def parseSessionBytes(input: SessionBytes): Output = { - if (input.session ne lastSession) { - lastSession = input.session - tlsSessionInfoHeader = `Tls-Session-Info`(input.session) - } - parseBytes(input.bytes) - } - final def parseBytes(input: ByteString): Output = { - @tailrec def run(next: ByteString ⇒ StateResult): StateResult = - (try next(input) - catch { - case e: ParsingException ⇒ failMessageStart(e.status, e.info) - case NotEnoughDataException ⇒ - // we are missing a try/catch{continue} wrapper somewhere - throw new IllegalStateException("unexpected NotEnoughDataException", NotEnoughDataException) - case IllegalHeaderException(error) ⇒ - failMessageStart(StatusCodes.BadRequest, error) - }) match { - case Trampoline(x) ⇒ run(x) - case x ⇒ x - } - - if (result.nonEmpty) throw new IllegalStateException("Unexpected `onPush`") - run(state) - doPull() - } - - protected final def doPull(): Output = - if (result.nonEmpty) { - val head = result.head - result.remove(0) // faster than `ListBuffer::drop` - head - } else if (terminated) StreamEnd else NeedMoreData - - protected final def shouldComplete(): Boolean = { - completionHandling() match { - case Some(x) ⇒ emit(x) - case None ⇒ // nothing to do - } - terminated = true - result.isEmpty - } - - protected final def startNewMessage(input: ByteString, offset: Int): StateResult = { - if (offset < input.length) setCompletionHandling(CompletionIsMessageStartError) - try parseMessage(input, offset) - catch { case NotEnoughDataException ⇒ continue(input, offset)(startNewMessage) } - } - - protected final def parseProtocol(input: ByteString, cursor: Int): Int = { - def c(ix: Int) = byteChar(input, cursor + ix) - if (c(0) == 'H' && c(1) == 'T' && c(2) == 'T' && c(3) == 'P' && c(4) == '/' && c(5) == '1' && c(6) == '.') { - protocol = c(7) match { - case '0' ⇒ `HTTP/1.0` - case '1' ⇒ `HTTP/1.1` - case _ ⇒ onBadProtocol - } - cursor + 8 - } else onBadProtocol - } - - @tailrec protected final def parseHeaderLines(input: ByteString, lineStart: Int, headers: ListBuffer[HttpHeader] = initialHeaderBuffer, - headerCount: Int = 0, ch: Option[Connection] = None, - clh: Option[`Content-Length`] = None, cth: Option[`Content-Type`] = None, - teh: Option[`Transfer-Encoding`] = None, e100c: Boolean = false, - hh: Boolean = false): StateResult = - if (headerCount < settings.maxHeaderCount) { - var lineEnd = 0 - val resultHeader = - try { - lineEnd = headerParser.parseHeaderLine(input, lineStart)() - headerParser.resultHeader - } catch { - case NotEnoughDataException ⇒ null - } - resultHeader match { - case null ⇒ continue(input, lineStart)(parseHeaderLinesAux(headers, headerCount, ch, clh, cth, teh, e100c, hh)) - - case EmptyHeader ⇒ - val close = HttpMessage.connectionCloseExpected(protocol, ch) - setCompletionHandling(CompletionIsEntityStreamError) - parseEntity(headers.toList, protocol, input, lineEnd, clh, cth, teh, e100c, hh, close) - - case h: `Content-Length` ⇒ clh match { - case None ⇒ parseHeaderLines(input, lineEnd, headers, headerCount + 1, ch, Some(h), cth, teh, e100c, hh) - case Some(`h`) ⇒ parseHeaderLines(input, lineEnd, headers, headerCount, ch, clh, cth, teh, e100c, hh) - case _ ⇒ failMessageStart("HTTP message must not contain more than one Content-Length header") - } - case h: `Content-Type` ⇒ cth match { - case None ⇒ parseHeaderLines(input, lineEnd, headers, headerCount + 1, ch, clh, Some(h), teh, e100c, hh) - case Some(`h`) ⇒ parseHeaderLines(input, lineEnd, headers, headerCount, ch, clh, cth, teh, e100c, hh) - case _ ⇒ failMessageStart("HTTP message must not contain more than one Content-Type header") - } - case h: `Transfer-Encoding` ⇒ teh match { - case None ⇒ parseHeaderLines(input, lineEnd, headers, headerCount + 1, ch, clh, cth, Some(h), e100c, hh) - case Some(x) ⇒ parseHeaderLines(input, lineEnd, headers, headerCount, ch, clh, cth, Some(x append h.encodings), e100c, hh) - } - case h: Connection ⇒ ch match { - case None ⇒ parseHeaderLines(input, lineEnd, headers += h, headerCount + 1, Some(h), clh, cth, teh, e100c, hh) - case Some(x) ⇒ parseHeaderLines(input, lineEnd, headers, headerCount, Some(x append h.tokens), clh, cth, teh, e100c, hh) - } - case h: Host ⇒ - if (!hh) parseHeaderLines(input, lineEnd, headers += h, headerCount + 1, ch, clh, cth, teh, e100c, hh = true) - else failMessageStart("HTTP message must not contain more than one Host header") - - case h: Expect ⇒ parseHeaderLines(input, lineEnd, headers += h, headerCount + 1, ch, clh, cth, teh, e100c = true, hh) - - case h ⇒ parseHeaderLines(input, lineEnd, headers += h, headerCount + 1, ch, clh, cth, teh, e100c, hh) - } - } else failMessageStart(s"HTTP message contains more than the configured limit of ${settings.maxHeaderCount} headers") - - // work-around for compiler complaining about non-tail-recursion if we inline this method - private def parseHeaderLinesAux(headers: ListBuffer[HttpHeader], headerCount: Int, ch: Option[Connection], - clh: Option[`Content-Length`], cth: Option[`Content-Type`], teh: Option[`Transfer-Encoding`], - e100c: Boolean, hh: Boolean)(input: ByteString, lineStart: Int): StateResult = - parseHeaderLines(input, lineStart, headers, headerCount, ch, clh, cth, teh, e100c, hh) - - protected final def parseFixedLengthBody( - remainingBodyBytes: Long, - isLastMessage: Boolean)(input: ByteString, bodyStart: Int): StateResult = { - val remainingInputBytes = input.length - bodyStart - if (remainingInputBytes > 0) { - if (remainingInputBytes < remainingBodyBytes) { - emit(EntityPart(input.drop(bodyStart).compact)) - continue(parseFixedLengthBody(remainingBodyBytes - remainingInputBytes, isLastMessage)) - } else { - val offset = bodyStart + remainingBodyBytes.toInt - emit(EntityPart(input.slice(bodyStart, offset).compact)) - emit(MessageEnd) - setCompletionHandling(CompletionOk) - if (isLastMessage) terminate() - else startNewMessage(input, offset) - } - } else continue(input, bodyStart)(parseFixedLengthBody(remainingBodyBytes, isLastMessage)) - } - - protected final def parseChunk(input: ByteString, offset: Int, isLastMessage: Boolean, totalBytesRead: Long): StateResult = { - @tailrec def parseTrailer(extension: String, lineStart: Int, headers: List[HttpHeader] = Nil, - headerCount: Int = 0): StateResult = { - var errorInfo: ErrorInfo = null - val lineEnd = - try headerParser.parseHeaderLine(input, lineStart)() - catch { case e: ParsingException ⇒ errorInfo = e.info; 0 } - if (errorInfo eq null) { - headerParser.resultHeader match { - case EmptyHeader ⇒ - val lastChunk = - if (extension.isEmpty && headers.isEmpty) HttpEntity.LastChunk else HttpEntity.LastChunk(extension, headers) - emit(EntityChunk(lastChunk)) - emit(MessageEnd) - setCompletionHandling(CompletionOk) - if (isLastMessage) terminate() - else startNewMessage(input, lineEnd) - case header if headerCount < settings.maxHeaderCount ⇒ - parseTrailer(extension, lineEnd, header :: headers, headerCount + 1) - case _ ⇒ failEntityStream(s"Chunk trailer contains more than the configured limit of ${settings.maxHeaderCount} headers") - } - } else failEntityStream(errorInfo) - } - - def parseChunkBody(chunkSize: Int, extension: String, cursor: Int): StateResult = - if (chunkSize > 0) { - val chunkBodyEnd = cursor + chunkSize - def result(terminatorLen: Int) = { - emit(EntityChunk(HttpEntity.Chunk(input.slice(cursor, chunkBodyEnd).compact, extension))) - Trampoline(_ ⇒ parseChunk(input, chunkBodyEnd + terminatorLen, isLastMessage, totalBytesRead + chunkSize)) - } - byteChar(input, chunkBodyEnd) match { - case '\r' if byteChar(input, chunkBodyEnd + 1) == '\n' ⇒ result(2) - case '\n' ⇒ result(1) - case x ⇒ failEntityStream("Illegal chunk termination") - } - } else parseTrailer(extension, cursor) - - @tailrec def parseChunkExtensions(chunkSize: Int, cursor: Int)(startIx: Int = cursor): StateResult = - if (cursor - startIx <= settings.maxChunkExtLength) { - def extension = asciiString(input, startIx, cursor) - byteChar(input, cursor) match { - case '\r' if byteChar(input, cursor + 1) == '\n' ⇒ parseChunkBody(chunkSize, extension, cursor + 2) - case '\n' ⇒ parseChunkBody(chunkSize, extension, cursor + 1) - case _ ⇒ parseChunkExtensions(chunkSize, cursor + 1)(startIx) - } - } else failEntityStream(s"HTTP chunk extension length exceeds configured limit of ${settings.maxChunkExtLength} characters") - - @tailrec def parseSize(cursor: Int, size: Long): StateResult = - if (size <= settings.maxChunkSize) { - byteChar(input, cursor) match { - case c if CharacterClasses.HEXDIG(c) ⇒ parseSize(cursor + 1, size * 16 + CharUtils.hexValue(c)) - case ';' if cursor > offset ⇒ parseChunkExtensions(size.toInt, cursor + 1)() - case '\r' if cursor > offset && byteChar(input, cursor + 1) == '\n' ⇒ parseChunkBody(size.toInt, "", cursor + 2) - case c ⇒ failEntityStream(s"Illegal character '${escape(c)}' in chunk start") - } - } else failEntityStream(s"HTTP chunk size exceeds the configured limit of ${settings.maxChunkSize} bytes") - - try parseSize(offset, 0) - catch { - case NotEnoughDataException ⇒ continue(input, offset)(parseChunk(_, _, isLastMessage, totalBytesRead)) - } - } - - protected def emit(output: Output): Unit = result += output - - protected final def continue(input: ByteString, offset: Int)(next: (ByteString, Int) ⇒ StateResult): StateResult = { - state = - math.signum(offset - input.length) match { - case -1 ⇒ - val remaining = input.drop(offset) - more ⇒ next(remaining ++ more, 0) - case 0 ⇒ next(_, 0) - case 1 ⇒ throw new IllegalStateException - } - done() - } - - protected final def continue(next: (ByteString, Int) ⇒ StateResult): StateResult = { - state = next(_, 0) - done() - } - - protected final def failMessageStart(summary: String): StateResult = failMessageStart(summary, "") - protected final def failMessageStart(summary: String, detail: String): StateResult = failMessageStart(StatusCodes.BadRequest, summary, detail) - protected final def failMessageStart(status: StatusCode): StateResult = failMessageStart(status, status.defaultMessage) - protected final def failMessageStart(status: StatusCode, summary: String, detail: String = ""): StateResult = failMessageStart(status, ErrorInfo(summary, detail)) - protected final def failMessageStart(status: StatusCode, info: ErrorInfo): StateResult = { - emit(MessageStartError(status, info)) - setCompletionHandling(CompletionOk) - terminate() - } - - protected final def failEntityStream(summary: String): StateResult = failEntityStream(summary, "") - protected final def failEntityStream(summary: String, detail: String): StateResult = failEntityStream(ErrorInfo(summary, detail)) - protected final def failEntityStream(info: ErrorInfo): StateResult = { - emit(EntityStreamError(info)) - setCompletionHandling(CompletionOk) - terminate() - } - - protected final def terminate(): StateResult = { - terminated = true - done() - } - - /** - * Use [[continue]] or [[terminate]] to suspend or terminate processing. - * Do not call this directly. - */ - private def done(): StateResult = null // StateResult is a phantom type - - protected final def contentType(cth: Option[`Content-Type`]) = cth match { - case Some(x) ⇒ x.contentType - case None ⇒ ContentTypes.`application/octet-stream` - } - - protected final def emptyEntity(cth: Option[`Content-Type`]) = - StrictEntityCreator(if (cth.isDefined) HttpEntity.empty(cth.get.contentType) else HttpEntity.Empty) - - protected final def strictEntity(cth: Option[`Content-Type`], input: ByteString, bodyStart: Int, - contentLength: Int) = - StrictEntityCreator(HttpEntity.Strict(contentType(cth), input.slice(bodyStart, bodyStart + contentLength))) - - protected final def defaultEntity[A <: ParserOutput](cth: Option[`Content-Type`], contentLength: Long) = - StreamedEntityCreator[A, UniversalEntity] { entityParts ⇒ - val data = entityParts.collect { - case EntityPart(bytes) ⇒ bytes - case EntityStreamError(info) ⇒ throw EntityStreamException(info) - } - HttpEntity.Default(contentType(cth), contentLength, HttpEntity.limitableByteSource(data)) - } - - protected final def chunkedEntity[A <: ParserOutput](cth: Option[`Content-Type`]) = - StreamedEntityCreator[A, RequestEntity] { entityChunks ⇒ - val chunks = entityChunks.collect { - case EntityChunk(chunk) ⇒ chunk - case EntityStreamError(info) ⇒ throw EntityStreamException(info) - } - HttpEntity.Chunked(contentType(cth), HttpEntity.limitableChunkSource(chunks)) - } - - protected final def addTransferEncodingWithChunkedPeeled(headers: List[HttpHeader], teh: `Transfer-Encoding`): List[HttpHeader] = - teh.withChunkedPeeled match { - case Some(x) ⇒ x :: headers - case None ⇒ headers - } - - protected final def setCompletionHandling(completionHandling: CompletionHandling): Unit = - this.completionHandling = completionHandling - -} - -/** - * INTERNAL API - */ -private[http] object HttpMessageParser { - sealed trait StateResult // phantom type for ensuring soundness of our parsing method setup - final case class Trampoline(f: ByteString ⇒ StateResult) extends StateResult - - type CompletionHandling = () ⇒ Option[ErrorOutput] - val CompletionOk: CompletionHandling = () ⇒ None - val CompletionIsMessageStartError: CompletionHandling = - () ⇒ Some(ParserOutput.MessageStartError(StatusCodes.BadRequest, ErrorInfo("Illegal HTTP message start"))) - val CompletionIsEntityStreamError: CompletionHandling = - () ⇒ Some(ParserOutput.EntityStreamError(ErrorInfo("Entity stream truncation"))) -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/HttpRequestParser.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/HttpRequestParser.scala deleted file mode 100644 index c41fe5db1e..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/HttpRequestParser.scala +++ /dev/null @@ -1,211 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.parsing - -import java.lang.{ StringBuilder ⇒ JStringBuilder } - -import scala.annotation.{ switch, tailrec } -import akka.http.scaladsl.settings.ParserSettings -import akka.util.{ ByteString, OptionVal } -import akka.http.impl.engine.ws.Handshake -import akka.http.impl.model.parser.CharacterClasses -import akka.http.scaladsl.model._ -import headers._ -import StatusCodes._ -import ParserOutput._ -import akka.http.impl.util.ByteStringParserInput -import akka.stream.{ Attributes, FlowShape, Inlet, Outlet } -import akka.stream.TLSProtocol.SessionBytes -import akka.stream.stage.{ GraphStage, GraphStageLogic, InHandler, OutHandler } - -/** - * INTERNAL API - */ -private[http] final class HttpRequestParser( - settings: ParserSettings, - rawRequestUriHeader: Boolean, - headerParser: HttpHeaderParser) - extends GraphStage[FlowShape[SessionBytes, RequestOutput]] { self ⇒ - - import HttpMessageParser._ - import settings._ - - val in = Inlet[SessionBytes]("HttpRequestParser.in") - val out = Outlet[RequestOutput]("HttpRequestParser.out") - - val shape = FlowShape.of(in, out) - - override protected def initialAttributes: Attributes = Attributes.name("HttpRequestParser") - - override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = new GraphStageLogic(shape) with HttpMessageParser[RequestOutput] with InHandler with OutHandler { - - import HttpMessageParser._ - - override val settings = self.settings - override val headerParser = self.headerParser.createShallowCopy() - - private[this] var method: HttpMethod = _ - private[this] var uri: Uri = _ - private[this] var uriBytes: ByteString = _ - - override def onPush(): Unit = handleParserOutput(parseSessionBytes(grab(in))) - override def onPull(): Unit = handleParserOutput(doPull()) - - override def onUpstreamFinish(): Unit = - if (super.shouldComplete()) completeStage() - else if (isAvailable(out)) handleParserOutput(doPull()) - - setHandlers(in, out, this) - - private def handleParserOutput(output: RequestOutput): Unit = { - output match { - case StreamEnd ⇒ completeStage() - case NeedMoreData ⇒ pull(in) - case x ⇒ push(out, x) - } - } - - override def parseMessage(input: ByteString, offset: Int): StateResult = { - var cursor = parseMethod(input, offset) - cursor = parseRequestTarget(input, cursor) - cursor = parseProtocol(input, cursor) - if (byteChar(input, cursor) == '\r' && byteChar(input, cursor + 1) == '\n') - parseHeaderLines(input, cursor + 2) - else onBadProtocol - } - - def parseMethod(input: ByteString, cursor: Int): Int = { - @tailrec def parseCustomMethod(ix: Int = 0, sb: JStringBuilder = new JStringBuilder(16)): Int = - if (ix < maxMethodLength) { - byteChar(input, cursor + ix) match { - case ' ' ⇒ - customMethods(sb.toString) match { - case Some(m) ⇒ - method = m - cursor + ix + 1 - case None ⇒ throw new ParsingException(NotImplemented, ErrorInfo("Unsupported HTTP method", sb.toString)) - } - case c ⇒ parseCustomMethod(ix + 1, sb.append(c)) - } - } else throw new ParsingException( - BadRequest, - ErrorInfo("Unsupported HTTP method", s"HTTP method too long (started with '${sb.toString}'). " + - "Increase `akka.http.server.parsing.max-method-length` to support HTTP methods with more characters.")) - - @tailrec def parseMethod(meth: HttpMethod, ix: Int = 1): Int = - if (ix == meth.value.length) - if (byteChar(input, cursor + ix) == ' ') { - method = meth - cursor + ix + 1 - } else parseCustomMethod() - else if (byteChar(input, cursor + ix) == meth.value.charAt(ix)) parseMethod(meth, ix + 1) - else parseCustomMethod() - - import HttpMethods._ - (byteChar(input, cursor): @switch) match { - case 'G' ⇒ parseMethod(GET) - case 'P' ⇒ byteChar(input, cursor + 1) match { - case 'O' ⇒ parseMethod(POST, 2) - case 'U' ⇒ parseMethod(PUT, 2) - case 'A' ⇒ parseMethod(PATCH, 2) - case _ ⇒ parseCustomMethod() - } - case 'D' ⇒ parseMethod(DELETE) - case 'H' ⇒ parseMethod(HEAD) - case 'O' ⇒ parseMethod(OPTIONS) - case 'T' ⇒ parseMethod(TRACE) - case 'C' ⇒ parseMethod(CONNECT) - case _ ⇒ parseCustomMethod() - } - } - - def parseRequestTarget(input: ByteString, cursor: Int): Int = { - val uriStart = cursor - val uriEndLimit = cursor + maxUriLength - - @tailrec def findUriEnd(ix: Int = cursor): Int = - if (ix == input.length) throw NotEnoughDataException - else if (CharacterClasses.WSPCRLF(input(ix).toChar)) ix - else if (ix < uriEndLimit) findUriEnd(ix + 1) - else throw new ParsingException( - RequestUriTooLong, - s"URI length exceeds the configured limit of $maxUriLength characters") - - val uriEnd = findUriEnd() - try { - uriBytes = input.slice(uriStart, uriEnd) - uri = Uri.parseHttpRequestTarget(new ByteStringParserInput(uriBytes), mode = uriParsingMode) - } catch { - case IllegalUriException(info) ⇒ throw new ParsingException(BadRequest, info) - } - uriEnd + 1 - } - - override def onBadProtocol() = throw new ParsingException(HTTPVersionNotSupported) - - // http://tools.ietf.org/html/rfc7230#section-3.3 - override def parseEntity(headers: List[HttpHeader], protocol: HttpProtocol, input: ByteString, bodyStart: Int, - clh: Option[`Content-Length`], cth: Option[`Content-Type`], teh: Option[`Transfer-Encoding`], - expect100continue: Boolean, hostHeaderPresent: Boolean, closeAfterResponseCompletion: Boolean): StateResult = - if (hostHeaderPresent || protocol == HttpProtocols.`HTTP/1.0`) { - def emitRequestStart( - createEntity: EntityCreator[RequestOutput, RequestEntity], - headers: List[HttpHeader] = headers) = { - val allHeaders0 = - if (rawRequestUriHeader) `Raw-Request-URI`(uriBytes.decodeString(HttpCharsets.`US-ASCII`.nioCharset)) :: headers - else headers - - val allHeaders = - if (method == HttpMethods.GET) { - Handshake.Server.websocketUpgrade(headers, hostHeaderPresent) match { - case OptionVal.Some(upgrade) ⇒ upgrade :: allHeaders0 - case OptionVal.None ⇒ allHeaders0 - } - } else allHeaders0 - - emit(RequestStart(method, uri, protocol, allHeaders, createEntity, expect100continue, closeAfterResponseCompletion)) - } - - teh match { - case None ⇒ - val contentLength = clh match { - case Some(`Content-Length`(len)) ⇒ len - case None ⇒ 0 - } - if (contentLength == 0) { - emitRequestStart(emptyEntity(cth)) - setCompletionHandling(HttpMessageParser.CompletionOk) - startNewMessage(input, bodyStart) - } else if (!method.isEntityAccepted) { - failMessageStart(UnprocessableEntity, s"${method.name} requests must not have an entity") - } else if (contentLength <= input.size - bodyStart) { - val cl = contentLength.toInt - emitRequestStart(strictEntity(cth, input, bodyStart, cl)) - setCompletionHandling(HttpMessageParser.CompletionOk) - startNewMessage(input, bodyStart + cl) - } else { - emitRequestStart(defaultEntity(cth, contentLength)) - parseFixedLengthBody(contentLength, closeAfterResponseCompletion)(input, bodyStart) - } - - case Some(_) if !method.isEntityAccepted ⇒ - failMessageStart(UnprocessableEntity, s"${method.name} requests must not have an entity") - - case Some(te) ⇒ - val completedHeaders = addTransferEncodingWithChunkedPeeled(headers, te) - if (te.isChunked) { - if (clh.isEmpty) { - emitRequestStart(chunkedEntity(cth), completedHeaders) - parseChunk(input, bodyStart, closeAfterResponseCompletion, totalBytesRead = 0L) - } else failMessageStart("A chunked request must not contain a Content-Length header.") - } else parseEntity(completedHeaders, protocol, input, bodyStart, clh, cth, teh = None, - expect100continue, hostHeaderPresent, closeAfterResponseCompletion) - } - } else failMessageStart("Request is missing required `Host` header") - - } - - override def toString: String = "HttpRequestParser" -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/HttpResponseParser.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/HttpResponseParser.scala deleted file mode 100644 index ca8683f2b2..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/HttpResponseParser.scala +++ /dev/null @@ -1,232 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.parsing - -import scala.annotation.tailrec -import scala.concurrent.Promise -import scala.util.control.NoStackTrace -import akka.http.scaladsl.settings.ParserSettings -import akka.http.impl.model.parser.CharacterClasses -import akka.util.ByteString -import akka.http.scaladsl.model._ -import headers._ -import ParserOutput._ -import akka.stream.{ Attributes, FlowShape, Inlet, Outlet } -import akka.stream.TLSProtocol.SessionBytes -import akka.stream.stage.{ GraphStage, GraphStageLogic, InHandler, OutHandler } - -/** - * INTERNAL API - */ -private[http] class HttpResponseParser(protected val settings: ParserSettings, protected val headerParser: HttpHeaderParser) - extends HttpMessageParser[ResponseOutput] { self ⇒ - import HttpResponseParser._ - import HttpMessageParser._ - import settings._ - - private[this] var contextForCurrentResponse: Option[ResponseContext] = None - private[this] var statusCode: StatusCode = StatusCodes.OK - - // Note that this GraphStage mutates the HttpMessageParser instance, use with caution. - final val stage = new GraphStage[FlowShape[SessionBytes, ResponseOutput]] { - val in: Inlet[SessionBytes] = Inlet("HttpResponseParser.in") - val out: Outlet[ResponseOutput] = Outlet("HttpResponseParser.out") - override val shape: FlowShape[SessionBytes, ResponseOutput] = FlowShape(in, out) - - override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = - new GraphStageLogic(shape) with InHandler with OutHandler { - override def onPush(): Unit = handleParserOutput(self.parseSessionBytes(grab(in))) - override def onPull(): Unit = handleParserOutput(self.onPull()) - - override def onUpstreamFinish(): Unit = - if (self.onUpstreamFinish()) completeStage() - else if (isAvailable(out)) handleParserOutput(self.onPull()) - - private def handleParserOutput(output: ResponseOutput): Unit = { - output match { - case StreamEnd ⇒ completeStage() - case NeedMoreData ⇒ pull(in) - case x ⇒ push(out, x) - } - } - - setHandlers(in, out, this) - } - } - - final def createShallowCopy(): HttpResponseParser = new HttpResponseParser(settings, headerParser.createShallowCopy()) - - final def setContextForNextResponse(responseContext: ResponseContext): Unit = - if (contextForCurrentResponse.isEmpty) contextForCurrentResponse = Some(responseContext) - - final def onPull(): ResponseOutput = - if (result.nonEmpty) { - val head = result.head - result.remove(0) // faster than `ListBuffer::drop` - head - } else if (terminated) StreamEnd else NeedMoreData - - final def onUpstreamFinish(): Boolean = { - completionHandling() match { - case Some(x) ⇒ emit(x) - case None ⇒ // nothing to do - } - terminated = true - result.isEmpty - } - - override final def emit(output: ResponseOutput): Unit = { - if (output == MessageEnd) contextForCurrentResponse = None - super.emit(output) - } - - override protected def parseMessage(input: ByteString, offset: Int): StateResult = - if (contextForCurrentResponse.isDefined) { - var cursor = parseProtocol(input, offset) - if (byteChar(input, cursor) == ' ') { - cursor = parseStatus(input, cursor + 1) - parseHeaderLines(input, cursor) - } else onBadProtocol() - } else { - emit(NeedNextRequestMethod) - continue(input, offset)(startNewMessage) - } - - override final def onBadProtocol() = throw new ParsingException("The server-side HTTP version is not supported") - - private def parseStatus(input: ByteString, cursor: Int): Int = { - def badStatusCode = throw new ParsingException("Illegal response status code") - def parseStatusCode() = { - def intValue(offset: Int): Int = { - val c = byteChar(input, cursor + offset) - if (CharacterClasses.DIGIT(c)) c - '0' else badStatusCode - } - val code = intValue(0) * 100 + intValue(1) * 10 + intValue(2) - statusCode = code match { - case 200 ⇒ StatusCodes.OK - case _ ⇒ StatusCodes.getForKey(code) match { - case Some(x) ⇒ x - case None ⇒ customStatusCodes(code) getOrElse badStatusCode - } - } - } - if (byteChar(input, cursor + 3) == ' ') { - parseStatusCode() - val startIdx = cursor + 4 - @tailrec def skipReason(idx: Int): Int = - if (idx - startIdx <= maxResponseReasonLength) - if (byteChar(input, idx) == '\r' && byteChar(input, idx + 1) == '\n') idx + 2 - else skipReason(idx + 1) - else throw new ParsingException("Response reason phrase exceeds the configured limit of " + - maxResponseReasonLength + " characters") - skipReason(startIdx) - } else if (byteChar(input, cursor + 3) == '\r' && byteChar(input, cursor + 4) == '\n') { - throw new ParsingException("Status code misses trailing space") - } else badStatusCode - } - - def handleInformationalResponses: Boolean = true - - // http://tools.ietf.org/html/rfc7230#section-3.3 - protected final def parseEntity(headers: List[HttpHeader], protocol: HttpProtocol, input: ByteString, bodyStart: Int, - clh: Option[`Content-Length`], cth: Option[`Content-Type`], teh: Option[`Transfer-Encoding`], - expect100continue: Boolean, hostHeaderPresent: Boolean, closeAfterResponseCompletion: Boolean): StateResult = { - - def emitResponseStart( - createEntity: EntityCreator[ResponseOutput, ResponseEntity], - headers: List[HttpHeader] = headers) = { - val close = - contextForCurrentResponse.get.oneHundredContinueTrigger match { - case None ⇒ closeAfterResponseCompletion - case Some(trigger) if statusCode.isSuccess ⇒ - trigger.trySuccess(()) - closeAfterResponseCompletion - case Some(trigger) ⇒ - trigger.tryFailure(OneHundredContinueError) - true - } - emit(ResponseStart(statusCode, protocol, headers, createEntity, close)) - } - - def finishEmptyResponse() = - statusCode match { - case _: StatusCodes.Informational if handleInformationalResponses ⇒ - if (statusCode == StatusCodes.Continue) - contextForCurrentResponse.get.oneHundredContinueTrigger.foreach(_.trySuccess(())) - - // http://tools.ietf.org/html/rfc7231#section-6.2 says: - // "A client MUST be able to parse one or more 1xx responses received prior to a final response, - // even if the client does not expect one." - // so we simply drop this interim response and start parsing the next one - startNewMessage(input, bodyStart) - case _ ⇒ - emitResponseStart(emptyEntity(cth)) - setCompletionHandling(HttpMessageParser.CompletionOk) - emit(MessageEnd) - startNewMessage(input, bodyStart) - } - - if (statusCode.allowsEntity && (contextForCurrentResponse.get.requestMethod != HttpMethods.HEAD)) { - teh match { - case None ⇒ clh match { - case Some(`Content-Length`(contentLength)) ⇒ - if (contentLength == 0) finishEmptyResponse() - else if (contentLength <= input.size - bodyStart) { - val cl = contentLength.toInt - emitResponseStart(strictEntity(cth, input, bodyStart, cl)) - setCompletionHandling(HttpMessageParser.CompletionOk) - emit(MessageEnd) - startNewMessage(input, bodyStart + cl) - } else { - emitResponseStart(defaultEntity(cth, contentLength)) - parseFixedLengthBody(contentLength, closeAfterResponseCompletion)(input, bodyStart) - } - case None ⇒ - emitResponseStart { - StreamedEntityCreator { entityParts ⇒ - val data = entityParts.collect { case EntityPart(bytes) ⇒ bytes } - HttpEntity.CloseDelimited(contentType(cth), HttpEntity.limitableByteSource(data)) - } - } - setCompletionHandling(HttpMessageParser.CompletionOk) - parseToCloseBody(input, bodyStart, totalBytesRead = 0) - } - - case Some(te) ⇒ - val completedHeaders = addTransferEncodingWithChunkedPeeled(headers, te) - if (te.isChunked) { - if (clh.isEmpty) { - emitResponseStart(chunkedEntity(cth), completedHeaders) - parseChunk(input, bodyStart, closeAfterResponseCompletion, totalBytesRead = 0L) - } else failMessageStart("A chunked response must not contain a Content-Length header.") - } else parseEntity(completedHeaders, protocol, input, bodyStart, clh, cth, teh = None, - expect100continue, hostHeaderPresent, closeAfterResponseCompletion) - } - } else finishEmptyResponse() - } - - private def parseToCloseBody(input: ByteString, bodyStart: Int, totalBytesRead: Long): StateResult = { - val newTotalBytes = totalBytesRead + math.max(0, input.length - bodyStart) - if (input.length > bodyStart) - emit(EntityPart(input.drop(bodyStart).compact)) - continue(parseToCloseBody(_, _, newTotalBytes)) - } -} - -private[http] object HttpResponseParser { - /** - * @param requestMethod the request's HTTP method - * @param oneHundredContinueTrigger if the request contains an `Expect: 100-continue` header this option contains - * a promise whose completion either triggers the sending of the (suspended) - * request entity or the closing of the connection (for error completion) - */ - private[http] final case class ResponseContext( - requestMethod: HttpMethod, - oneHundredContinueTrigger: Option[Promise[Unit]]) - - private[http] object OneHundredContinueError - extends RuntimeException("Received error response for request with `Expect: 100-continue` header") - with NoStackTrace -} \ No newline at end of file diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/ParserOutput.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/ParserOutput.scala deleted file mode 100644 index 278c71203f..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/ParserOutput.scala +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.parsing - -import akka.NotUsed -import akka.http.scaladsl.model._ -import akka.stream.scaladsl.Source -import akka.util.ByteString -import akka.stream.impl.fusing.SubSource - -/** - * INTERNAL API - */ -private[http] sealed trait ParserOutput - -/** - * INTERNAL API - */ -private[http] object ParserOutput { - sealed trait RequestOutput extends ParserOutput - sealed trait ResponseOutput extends ParserOutput - sealed trait MessageStart extends ParserOutput - sealed trait MessageOutput extends RequestOutput with ResponseOutput - sealed trait ErrorOutput extends MessageOutput - - final case class RequestStart( - method: HttpMethod, - uri: Uri, - protocol: HttpProtocol, - headers: List[HttpHeader], - createEntity: EntityCreator[RequestOutput, RequestEntity], - expect100Continue: Boolean, - closeRequested: Boolean) extends MessageStart with RequestOutput - - final case class ResponseStart( - statusCode: StatusCode, - protocol: HttpProtocol, - headers: List[HttpHeader], - createEntity: EntityCreator[ResponseOutput, ResponseEntity], - closeRequested: Boolean) extends MessageStart with ResponseOutput - - case object MessageEnd extends MessageOutput - - final case class EntityPart(data: ByteString) extends MessageOutput - - final case class EntityChunk(chunk: HttpEntity.ChunkStreamPart) extends MessageOutput - - final case class MessageStartError(status: StatusCode, info: ErrorInfo) extends MessageStart with ErrorOutput - - final case class EntityStreamError(info: ErrorInfo) extends ErrorOutput - - //////////// meta messages /////////// - - case object StreamEnd extends MessageOutput - - case object NeedMoreData extends MessageOutput - - case object NeedNextRequestMethod extends ResponseOutput - - final case class RemainingBytes(bytes: ByteString) extends ResponseOutput - - ////////////////////////////////////// - - sealed abstract class EntityCreator[-A <: ParserOutput, +B >: HttpEntity.Strict <: HttpEntity] extends (Source[A, NotUsed] ⇒ B) - - final case class StrictEntityCreator(entity: HttpEntity.Strict) extends EntityCreator[ParserOutput, HttpEntity.Strict] { - def apply(parts: Source[ParserOutput, NotUsed]) = { - // We might need to drain stray empty tail streams which will be read by no one. - SubSource.kill(parts) - entity - } - } - final case class StreamedEntityCreator[-A <: ParserOutput, +B >: HttpEntity.Strict <: HttpEntity](creator: Source[A, NotUsed] ⇒ B) - extends EntityCreator[A, B] { - def apply(parts: Source[A, NotUsed]) = creator(parts) - } -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/SpecializedHeaderValueParsers.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/SpecializedHeaderValueParsers.scala deleted file mode 100644 index a17757bdd3..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/SpecializedHeaderValueParsers.scala +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.parsing - -import scala.annotation.tailrec -import akka.util.ByteString -import akka.http.impl.model.parser.CharacterClasses._ -import akka.http.scaladsl.model.{ HttpHeader, ErrorInfo } -import akka.http.scaladsl.model.headers.`Content-Length` - -/** - * INTERNAL API - */ -private object SpecializedHeaderValueParsers { - import HttpHeaderParser._ - - def specializedHeaderValueParsers = Seq(ContentLengthParser) - - object ContentLengthParser extends HeaderValueParser("Content-Length", maxValueCount = 1) { - def apply(hhp: HttpHeaderParser, input: ByteString, valueStart: Int, onIllegalHeader: ErrorInfo ⇒ Unit): (HttpHeader, Int) = { - @tailrec def recurse(ix: Int = valueStart, result: Long = 0): (HttpHeader, Int) = { - val c = byteChar(input, ix) - if (result < 0) fail("`Content-Length` header value must not exceed 63-bit integer range") - else if (DIGIT(c)) recurse(ix + 1, result * 10 + c - '0') - else if (WSP(c)) recurse(ix + 1, result) - else if (c == '\r' && byteChar(input, ix + 1) == '\n') (`Content-Length`(result), ix + 2) - else fail("Illegal `Content-Length` header value") - } - recurse() - } - } -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/package.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/package.scala deleted file mode 100644 index 6fe8fb6d90..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/package.scala +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine - -import java.lang.{ StringBuilder ⇒ JStringBuilder } -import akka.http.scaladsl.settings.ParserSettings - -import scala.annotation.tailrec -import akka.event.LoggingAdapter -import akka.util.ByteString -import akka.http.scaladsl.model.{ ErrorInfo, StatusCode, StatusCodes } -import akka.http.impl.util.SingletonException - -/** - * INTERNAL API - */ -package object parsing { - - private[http] def escape(c: Char): String = c match { - case '\t' ⇒ "\\t" - case '\r' ⇒ "\\r" - case '\n' ⇒ "\\n" - case x if Character.isISOControl(x) ⇒ "\\u%04x" format c.toInt - case x ⇒ x.toString - } - - private[http] def byteChar(input: ByteString, ix: Int): Char = byteAt(input, ix).toChar - - private[http] def byteAt(input: ByteString, ix: Int): Byte = - if (ix < input.length) input(ix) else throw NotEnoughDataException - - private[http] def asciiString(input: ByteString, start: Int, end: Int): String = { - @tailrec def build(ix: Int = start, sb: JStringBuilder = new JStringBuilder(end - start)): String = - if (ix == end) sb.toString else build(ix + 1, sb.append(input(ix).toChar)) - if (start == end) "" else build() - } - - private[http] def logParsingError(info: ErrorInfo, log: LoggingAdapter, - setting: ParserSettings.ErrorLoggingVerbosity): Unit = - setting match { - case ParserSettings.ErrorLoggingVerbosity.Off ⇒ // nothing to do - case ParserSettings.ErrorLoggingVerbosity.Simple ⇒ log.warning(info.summary) - case ParserSettings.ErrorLoggingVerbosity.Full ⇒ log.warning(info.formatPretty) - } -} - -package parsing { - - /** - * INTERNAL API - */ - private[parsing] class ParsingException( - val status: StatusCode, - val info: ErrorInfo) extends RuntimeException(info.formatPretty) { - def this(status: StatusCode, summary: String = "") = - this(status, ErrorInfo(if (summary.isEmpty) status.defaultMessage else summary)) - def this(summary: String) = - this(StatusCodes.BadRequest, ErrorInfo(summary)) - } - - /** - * INTERNAL API - */ - private[parsing] object NotEnoughDataException extends SingletonException -} - diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/rendering/BodyPartRenderer.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/rendering/BodyPartRenderer.scala deleted file mode 100644 index 6ba19d1913..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/rendering/BodyPartRenderer.scala +++ /dev/null @@ -1,152 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.rendering - -import java.nio.charset.Charset -import akka.parboiled2.util.Base64 - -import scala.collection.immutable -import akka.event.LoggingAdapter -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers._ -import akka.http.impl.engine.rendering.RenderSupport._ -import akka.http.impl.util._ -import akka.stream.scaladsl.Source -import akka.stream.stage._ -import akka.util.ByteString -import HttpEntity._ -import akka.stream.{ Attributes, FlowShape, Inlet, Outlet } - -import java.util.concurrent.ThreadLocalRandom - -/** - * INTERNAL API - */ -private[http] object BodyPartRenderer { - - def streamed( - boundary: String, - nioCharset: Charset, - partHeadersSizeHint: Int, - log: LoggingAdapter): GraphStage[FlowShape[Multipart.BodyPart, Source[ChunkStreamPart, Any]]] = - new GraphStage[FlowShape[Multipart.BodyPart, Source[ChunkStreamPart, Any]]] { - var firstBoundaryRendered = false - - val in: Inlet[Multipart.BodyPart] = Inlet("BodyPartRenderer.in") - val out: Outlet[Source[ChunkStreamPart, Any]] = Outlet("BodyPartRenderer.out") - override val shape: FlowShape[Multipart.BodyPart, Source[ChunkStreamPart, Any]] = FlowShape(in, out) - - override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = - new GraphStageLogic(shape) with InHandler with OutHandler { - override def onPush(): Unit = { - val r = new CustomCharsetByteStringRendering(nioCharset, partHeadersSizeHint) - - def bodyPartChunks(data: Source[ByteString, Any]): Source[ChunkStreamPart, Any] = { - val entityChunks = data.map[ChunkStreamPart](Chunk(_)) - (chunkStream(r.get) ++ entityChunks).mapMaterializedValue((_) ⇒ ()) - } - - def completePartRendering(entity: HttpEntity): Source[ChunkStreamPart, Any] = - entity match { - case x if x.isKnownEmpty ⇒ chunkStream(r.get) - case Strict(_, data) ⇒ chunkStream((r ~~ data).get) - case Default(_, _, data) ⇒ bodyPartChunks(data) - case IndefiniteLength(_, data) ⇒ bodyPartChunks(data) - } - - renderBoundary(r, boundary, suppressInitialCrLf = !firstBoundaryRendered) - firstBoundaryRendered = true - - val bodyPart = grab(in) - renderEntityContentType(r, bodyPart.entity) - renderHeaders(r, bodyPart.headers, log) - - push(out, completePartRendering(bodyPart.entity)) - } - - override def onPull(): Unit = - if (isClosed(in) && firstBoundaryRendered) - completeRendering() - else if (isClosed(in)) completeStage() - else pull(in) - - override def onUpstreamFinish(): Unit = - if (isAvailable(out) && firstBoundaryRendered) completeRendering() - - private def completeRendering(): Unit = { - val r = new ByteStringRendering(boundary.length + 4) - renderFinalBoundary(r, boundary) - push(out, chunkStream(r.get)) - completeStage() - } - - setHandlers(in, out, this) - } - - private def chunkStream(byteString: ByteString): Source[ChunkStreamPart, Any] = - Source.single(Chunk(byteString)) - - } - - def strict(parts: immutable.Seq[Multipart.BodyPart.Strict], boundary: String, nioCharset: Charset, - partHeadersSizeHint: Int, log: LoggingAdapter): ByteString = { - val r = new CustomCharsetByteStringRendering(nioCharset, partHeadersSizeHint) - if (parts.nonEmpty) { - for (part ← parts) { - renderBoundary(r, boundary, suppressInitialCrLf = part eq parts.head) - renderEntityContentType(r, part.entity) - renderHeaders(r, part.headers, log) - r ~~ part.entity.data - } - renderFinalBoundary(r, boundary) - } - r.get - } - - private def renderBoundary(r: Rendering, boundary: String, suppressInitialCrLf: Boolean): Unit = { - if (!suppressInitialCrLf) r ~~ CrLf - r ~~ '-' ~~ '-' ~~ boundary ~~ CrLf - } - - private def renderFinalBoundary(r: Rendering, boundary: String): Unit = - r ~~ CrLf ~~ '-' ~~ '-' ~~ boundary ~~ '-' ~~ '-' - - private def renderHeaders(r: Rendering, headers: immutable.Seq[HttpHeader], log: LoggingAdapter): Unit = { - headers foreach renderHeader(r, log) - r ~~ CrLf - } - - private def renderHeader(r: Rendering, log: LoggingAdapter): HttpHeader ⇒ Unit = { - case x: `Content-Length` ⇒ - suppressionWarning(log, x, "explicit `Content-Length` header is not allowed. Use the appropriate HttpEntity subtype.") - - case x: `Content-Type` ⇒ - suppressionWarning(log, x, "explicit `Content-Type` header is not allowed. Set `HttpRequest.entity.contentType` instead.") - - case x: RawHeader if (x is "content-type") || (x is "content-length") ⇒ - suppressionWarning(log, x, "illegal RawHeader") - - case x ⇒ r ~~ x ~~ CrLf - } - - /** - * Creates a new random number of the given length and base64 encodes it (using a custom "safe" alphabet). - */ - def randomBoundary(length: Int = 18, random: java.util.Random = ThreadLocalRandom.current()): String = { - val array = new Array[Byte](length) - random.nextBytes(array) - Base64.custom.encodeToString(array, false) - } - - /** - * Creates a new random number of default length and base64 encodes it (using a custom "safe" alphabet). - */ - def randomBoundaryWithDefaults(): String = randomBoundary() - - /** - * Creates a new random number of the given length and base64 encodes it (using a custom "safe" alphabet). - */ - def randomBoundaryWithDefaultRandom(length: Int): String = randomBoundary(length) -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/rendering/HttpRequestRendererFactory.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/rendering/HttpRequestRendererFactory.scala deleted file mode 100644 index ea797d9906..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/rendering/HttpRequestRendererFactory.scala +++ /dev/null @@ -1,181 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.rendering - -import akka.NotUsed -import akka.http.impl.engine.parsing.HttpResponseParser -import akka.http.scaladsl.settings.ClientConnectionSettings -import akka.http.scaladsl.model.RequestEntityAcceptance._ - -import scala.concurrent.Future -import scala.annotation.tailrec -import akka.event.LoggingAdapter -import akka.util.ByteString -import akka.stream.scaladsl.Source -import akka.http.scaladsl.model._ -import akka.http.impl.util._ -import RenderSupport._ -import headers._ - -/** - * INTERNAL API - */ -private[http] class HttpRequestRendererFactory( - userAgentHeader: Option[headers.`User-Agent`], - requestHeaderSizeHint: Int, - log: LoggingAdapter) { - import HttpRequestRendererFactory.RequestRenderingOutput - - def renderToSource(ctx: RequestRenderingContext): Source[ByteString, Any] = render(ctx).byteStream - - def render(ctx: RequestRenderingContext): RequestRenderingOutput = { - val r = new ByteStringRendering(requestHeaderSizeHint) - import ctx.request._ - - def renderRequestLine(): Unit = { - r ~~ method ~~ ' ' - val rawRequestUriRendered = headers.exists { - case `Raw-Request-URI`(rawUri) ⇒ - r ~~ rawUri; true - case _ ⇒ false - } - if (!rawRequestUriRendered) UriRendering.renderUriWithoutFragment(r, uri, UTF8) - r ~~ ' ' ~~ protocol ~~ CrLf - } - - def render(h: HttpHeader) = r ~~ h ~~ CrLf - - @tailrec def renderHeaders(remaining: List[HttpHeader], hostHeaderSeen: Boolean = false, - userAgentSeen: Boolean = false, transferEncodingSeen: Boolean = false): Unit = - remaining match { - case head :: tail ⇒ head match { - case x: `Content-Length` ⇒ - suppressionWarning(log, x, "explicit `Content-Length` header is not allowed. Use the appropriate HttpEntity subtype.") - renderHeaders(tail, hostHeaderSeen, userAgentSeen, transferEncodingSeen) - - case x: `Content-Type` ⇒ - suppressionWarning(log, x, "explicit `Content-Type` header is not allowed. Set `HttpRequest.entity.contentType` instead.") - renderHeaders(tail, hostHeaderSeen, userAgentSeen, transferEncodingSeen) - - case x: `Transfer-Encoding` ⇒ - x.withChunkedPeeled match { - case None ⇒ - suppressionWarning(log, head) - renderHeaders(tail, hostHeaderSeen, userAgentSeen, transferEncodingSeen) - case Some(te) ⇒ - // if the user applied some custom transfer-encoding we need to keep the header - render(if (entity.isChunked && !entity.isKnownEmpty) te.withChunked else te) - renderHeaders(tail, hostHeaderSeen, userAgentSeen, transferEncodingSeen = true) - } - - case x: `Host` ⇒ - render(x) - renderHeaders(tail, hostHeaderSeen = true, userAgentSeen, transferEncodingSeen) - - case x: `User-Agent` ⇒ - render(x) - renderHeaders(tail, hostHeaderSeen, userAgentSeen = true, transferEncodingSeen) - - case x: `Raw-Request-URI` ⇒ // we never render this header - renderHeaders(tail, hostHeaderSeen, userAgentSeen, transferEncodingSeen) - - case x: CustomHeader ⇒ - if (x.renderInRequests) render(x) - renderHeaders(tail, hostHeaderSeen, userAgentSeen, transferEncodingSeen) - - case x: RawHeader if (x is "content-type") || (x is "content-length") || (x is "transfer-encoding") || - (x is "host") || (x is "user-agent") ⇒ - suppressionWarning(log, x, "illegal RawHeader") - renderHeaders(tail, hostHeaderSeen, userAgentSeen, transferEncodingSeen) - - case x ⇒ - if (x.renderInRequests) render(x) - else log.warning("HTTP header '{}' is not allowed in requests", x) - renderHeaders(tail, hostHeaderSeen, userAgentSeen, transferEncodingSeen) - } - - case Nil ⇒ - if (!hostHeaderSeen) r ~~ ctx.hostHeader ~~ CrLf - if (!userAgentSeen && userAgentHeader.isDefined) r ~~ userAgentHeader.get ~~ CrLf - if (entity.isChunked && !entity.isKnownEmpty && !transferEncodingSeen) - r ~~ `Transfer-Encoding` ~~ ChunkedBytes ~~ CrLf - } - - def renderContentLength(contentLength: Long) = - if (method.isEntityAccepted && (contentLength > 0 || method.requestEntityAcceptance == Expected)) r ~~ `Content-Length` ~~ contentLength ~~ CrLf else r - - def renderStreamed(body: Source[ByteString, Any]): RequestRenderingOutput = { - val headerPart = Source.single(r.get) - val stream = ctx.sendEntityTrigger match { - case None ⇒ headerPart ++ body - case Some(future) ⇒ - val barrier = Source.fromFuture(future).drop(1).asInstanceOf[Source[ByteString, Any]] - (headerPart ++ barrier ++ body).recoverWith { case HttpResponseParser.OneHundredContinueError ⇒ Source.empty } - } - RequestRenderingOutput.Streamed(stream) - } - - def completeRequestRendering(): RequestRenderingOutput = - entity match { - case x if x.isKnownEmpty ⇒ - renderContentLength(0) ~~ CrLf - RequestRenderingOutput.Strict(r.get) - - case HttpEntity.Strict(_, data) ⇒ - renderContentLength(data.length) ~~ CrLf - if (ctx.sendEntityTrigger.isDefined) renderStreamed(Source.single(data)) - else RequestRenderingOutput.Strict(r.get ++ data) - - case HttpEntity.Default(_, contentLength, data) ⇒ - renderContentLength(contentLength) ~~ CrLf - renderStreamed(data.via(CheckContentLengthTransformer.flow(contentLength))) - - case HttpEntity.Chunked(_, chunks) ⇒ - r ~~ CrLf - renderStreamed(chunks.via(ChunkTransformer.flow)) - } - - renderRequestLine() - renderHeaders(headers.toList) - renderEntityContentType(r, entity) - completeRequestRendering() - } - - def renderStrict(ctx: RequestRenderingContext): ByteString = - render(ctx) match { - case RequestRenderingOutput.Strict(bytes) ⇒ bytes - case _: RequestRenderingOutput.Streamed ⇒ - throw new IllegalArgumentException(s"Request entity was not Strict but ${ctx.request.entity.getClass.getSimpleName}") - } -} - -private[http] object HttpRequestRendererFactory { - def renderStrict(ctx: RequestRenderingContext, settings: ClientConnectionSettings, log: LoggingAdapter): ByteString = - new HttpRequestRendererFactory(settings.userAgentHeader, settings.requestHeaderSizeHint, log).renderStrict(ctx) - - sealed trait RequestRenderingOutput { - def byteStream: Source[ByteString, Any] - } - object RequestRenderingOutput { - case class Strict(bytes: ByteString) extends RequestRenderingOutput { - def byteStream: Source[ByteString, Any] = Source.single(bytes) - } - case class Streamed(byteStream: Source[ByteString, Any]) extends RequestRenderingOutput - } -} - -/** - * INTERNAL API - * - * @param request the request to be rendered - * @param hostHeader the host header to render (not necessarily contained in the request.headers) - * @param sendEntityTrigger defined when the request has a `Expect: 100-continue` header; in this case the future will - * be completed successfully when the request entity is allowed to go out onto the wire; - * if the future is completed with an error the connection is to be closed. - */ -private[http] final case class RequestRenderingContext( - request: HttpRequest, - hostHeader: Host, - sendEntityTrigger: Option[Future[NotUsed]] = None) diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/rendering/HttpResponseRendererFactory.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/rendering/HttpResponseRendererFactory.scala deleted file mode 100644 index 3967ac5597..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/rendering/HttpResponseRendererFactory.scala +++ /dev/null @@ -1,294 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.rendering - -import akka.NotUsed -import akka.http.impl.engine.ws.{ FrameEvent, UpgradeToWebSocketResponseHeader } -import akka.http.scaladsl.model.ws.Message -import akka.stream.{ Attributes, FlowShape, Graph, Inlet, Outlet } - -import scala.annotation.tailrec -import akka.event.LoggingAdapter -import akka.util.{ ByteString, OptionVal } -import akka.stream.scaladsl.{ Flow, Source } -import akka.stream.stage._ -import akka.http.scaladsl.model._ -import akka.http.impl.util._ -import RenderSupport._ -import HttpProtocols._ -import headers._ - -import scala.concurrent.duration._ - -/** - * INTERNAL API - */ -private[http] class HttpResponseRendererFactory( - serverHeader: Option[headers.Server], - responseHeaderSizeHint: Int, - log: LoggingAdapter) { - - private val renderDefaultServerHeader: Rendering ⇒ Unit = - serverHeader match { - case Some(h) ⇒ - val bytes = (new ByteArrayRendering(32) ~~ h ~~ CrLf).get - _ ~~ bytes - case None ⇒ _ ⇒ () - } - - // as an optimization we cache the Date header of the last second here - @volatile private[this] var cachedDateHeader: (Long, Array[Byte]) = (0L, null) - - private def dateHeader: Array[Byte] = { - var (cachedSeconds, cachedBytes) = cachedDateHeader - val now = currentTimeMillis() - if (now / 1000 > cachedSeconds) { - cachedSeconds = now / 1000 - val r = new ByteArrayRendering(48) - DateTime(now).renderRfc1123DateTimeString(r ~~ headers.Date) ~~ CrLf - cachedBytes = r.get - cachedDateHeader = cachedSeconds → cachedBytes - } - cachedBytes - } - - // split out so we can stabilize by overriding in tests - protected def currentTimeMillis(): Long = System.currentTimeMillis() - - def renderer: Flow[ResponseRenderingContext, ResponseRenderingOutput, NotUsed] = Flow.fromGraph(HttpResponseRenderer) - - object HttpResponseRenderer extends GraphStage[FlowShape[ResponseRenderingContext, ResponseRenderingOutput]] { - val in = Inlet[ResponseRenderingContext]("in") - val out = Outlet[ResponseRenderingOutput]("out") - val shape: FlowShape[ResponseRenderingContext, ResponseRenderingOutput] = FlowShape(in, out) - - def createLogic(inheritedAttributes: Attributes): GraphStageLogic = - new GraphStageLogic(shape) { - var closeMode: CloseMode = DontClose // signals what to do after the current response - def close: Boolean = closeMode != DontClose - def closeIf(cond: Boolean): Unit = if (cond) closeMode = CloseConnection - var transferring = false - - setHandler(in, new InHandler { - override def onPush(): Unit = - render(grab(in)) match { - case Strict(outElement) ⇒ - push(out, outElement) - if (close) completeStage() - case Streamed(outStream) ⇒ transfer(outStream) - } - - override def onUpstreamFinish(): Unit = - if (transferring) closeMode = CloseConnection - else completeStage() - }) - val waitForDemandHandler = new OutHandler { - def onPull(): Unit = pull(in) - } - setHandler(out, waitForDemandHandler) - def transfer(outStream: Source[ResponseRenderingOutput, Any]): Unit = { - transferring = true - val sinkIn = new SubSinkInlet[ResponseRenderingOutput]("RenderingSink") - sinkIn.setHandler(new InHandler { - override def onPush(): Unit = push(out, sinkIn.grab()) - override def onUpstreamFinish(): Unit = - if (close) completeStage() - else { - transferring = false - setHandler(out, waitForDemandHandler) - if (isAvailable(out)) pull(in) - } - }) - setHandler(out, new OutHandler { - override def onPull(): Unit = sinkIn.pull() - override def onDownstreamFinish(): Unit = { - completeStage() - sinkIn.cancel() - } - }) - sinkIn.pull() - outStream.runWith(sinkIn.sink)(interpreter.subFusingMaterializer) - } - - def render(ctx: ResponseRenderingContext): StrictOrStreamed = { - val r = new ByteStringRendering(responseHeaderSizeHint) - - import ctx.response._ - val noEntity = entity.isKnownEmpty || ctx.requestMethod == HttpMethods.HEAD - - def renderStatusLine(): Unit = - protocol match { - case `HTTP/1.1` ⇒ if (status eq StatusCodes.OK) r ~~ DefaultStatusLineBytes else r ~~ StatusLineStartBytes ~~ status ~~ CrLf - case `HTTP/1.0` ⇒ r ~~ protocol ~~ ' ' ~~ status ~~ CrLf - } - - def render(h: HttpHeader) = r ~~ h ~~ CrLf - - def mustRenderTransferEncodingChunkedHeader = - entity.isChunked && (!entity.isKnownEmpty || ctx.requestMethod == HttpMethods.HEAD) && (ctx.requestProtocol == `HTTP/1.1`) - - @tailrec def renderHeaders(remaining: List[HttpHeader], alwaysClose: Boolean = false, - connHeader: Connection = null, serverSeen: Boolean = false, - transferEncodingSeen: Boolean = false, dateSeen: Boolean = false): Unit = { - remaining match { - case head :: tail ⇒ head match { - case x: Server ⇒ - render(x) - renderHeaders(tail, alwaysClose, connHeader, serverSeen = true, transferEncodingSeen, dateSeen) - - case x: Date ⇒ - render(x) - renderHeaders(tail, alwaysClose, connHeader, serverSeen, transferEncodingSeen, dateSeen = true) - - case x: `Content-Length` ⇒ - suppressionWarning(log, x, "explicit `Content-Length` header is not allowed. Use the appropriate HttpEntity subtype.") - renderHeaders(tail, alwaysClose, connHeader, serverSeen, transferEncodingSeen, dateSeen) - - case x: `Content-Type` ⇒ - suppressionWarning(log, x, "explicit `Content-Type` header is not allowed. Set `HttpResponse.entity.contentType` instead.") - renderHeaders(tail, alwaysClose, connHeader, serverSeen, transferEncodingSeen, dateSeen) - - case x: `Transfer-Encoding` ⇒ - x.withChunkedPeeled match { - case None ⇒ - suppressionWarning(log, head) - renderHeaders(tail, alwaysClose, connHeader, serverSeen, transferEncodingSeen, dateSeen) - case Some(te) ⇒ - // if the user applied some custom transfer-encoding we need to keep the header - render(if (mustRenderTransferEncodingChunkedHeader) te.withChunked else te) - renderHeaders(tail, alwaysClose, connHeader, serverSeen, transferEncodingSeen = true, dateSeen) - } - - case x: Connection ⇒ - val connectionHeader = if (connHeader eq null) x else Connection(x.tokens ++ connHeader.tokens) - renderHeaders(tail, alwaysClose, connectionHeader, serverSeen, transferEncodingSeen, dateSeen) - - case x: CustomHeader ⇒ - if (x.renderInResponses) render(x) - renderHeaders(tail, alwaysClose, connHeader, serverSeen, transferEncodingSeen, dateSeen) - - case x: RawHeader if (x is "content-type") || (x is "content-length") || (x is "transfer-encoding") || - (x is "date") || (x is "server") || (x is "connection") ⇒ - suppressionWarning(log, x, "illegal RawHeader") - renderHeaders(tail, alwaysClose, connHeader, serverSeen, transferEncodingSeen, dateSeen) - - case x ⇒ - if (x.renderInResponses) render(x) - else log.warning("HTTP header '{}' is not allowed in responses", x) - renderHeaders(tail, alwaysClose, connHeader, serverSeen, transferEncodingSeen, dateSeen) - } - - case Nil ⇒ - if (!serverSeen) renderDefaultServerHeader(r) - if (!dateSeen) r ~~ dateHeader - - // Do we close the connection after this response? - closeIf { - // if we are prohibited to keep-alive by the spec - alwaysClose || - // if the client wants to close and we don't override - (ctx.closeRequested && ((connHeader eq null) || !connHeader.hasKeepAlive)) || - // if the application wants to close explicitly - (protocol match { - case `HTTP/1.1` ⇒ (connHeader ne null) && connHeader.hasClose - case `HTTP/1.0` ⇒ if (connHeader eq null) ctx.requestProtocol == `HTTP/1.1` else !connHeader.hasKeepAlive - }) - } - - // Do we render an explicit Connection header? - val renderConnectionHeader = - protocol == `HTTP/1.0` && !close || protocol == `HTTP/1.1` && close || // if we don't follow the default behavior - close != ctx.closeRequested || // if we override the client's closing request - protocol != ctx.requestProtocol // if we reply with a mismatching protocol (let's be very explicit in this case) - - if (renderConnectionHeader) - r ~~ Connection ~~ (if (close) CloseBytes else KeepAliveBytes) ~~ CrLf - else if (connHeader != null && connHeader.hasUpgrade) { - r ~~ connHeader ~~ CrLf - HttpHeader.fastFind(classOf[UpgradeToWebSocketResponseHeader], headers) match { - case OptionVal.Some(header) ⇒ closeMode = SwitchToWebSocket(header.handler) - case _ ⇒ // nothing to do here... - } - } - if (mustRenderTransferEncodingChunkedHeader && !transferEncodingSeen) - r ~~ `Transfer-Encoding` ~~ ChunkedBytes ~~ CrLf - } - } - - def renderContentLengthHeader(contentLength: Long) = - if (status.allowsEntity) r ~~ `Content-Length` ~~ contentLength ~~ CrLf else r - - def byteStrings(entityBytes: ⇒ Source[ByteString, Any]): Source[ResponseRenderingOutput, Any] = - renderByteStrings(r, entityBytes, skipEntity = noEntity).map(ResponseRenderingOutput.HttpData(_)) - - @tailrec def completeResponseRendering(entity: ResponseEntity): StrictOrStreamed = - entity match { - case HttpEntity.Strict(_, data) ⇒ - renderHeaders(headers.toList) - renderEntityContentType(r, entity) - renderContentLengthHeader(data.length) ~~ CrLf - - if (!noEntity) r ~~ data - - Strict { - closeMode match { - case SwitchToWebSocket(handler) ⇒ ResponseRenderingOutput.SwitchToWebSocket(r.get, handler) - case _ ⇒ ResponseRenderingOutput.HttpData(r.get) - } - } - - case HttpEntity.Default(_, contentLength, data) ⇒ - renderHeaders(headers.toList) - renderEntityContentType(r, entity) - renderContentLengthHeader(contentLength) ~~ CrLf - Streamed(byteStrings(data.via(CheckContentLengthTransformer.flow(contentLength)))) - - case HttpEntity.CloseDelimited(_, data) ⇒ - renderHeaders(headers.toList, alwaysClose = ctx.requestMethod != HttpMethods.HEAD) - renderEntityContentType(r, entity) ~~ CrLf - Streamed(byteStrings(data)) - - case HttpEntity.Chunked(contentType, chunks) ⇒ - if (ctx.requestProtocol == `HTTP/1.0`) - completeResponseRendering(HttpEntity.CloseDelimited(contentType, chunks.map(_.data))) - else { - renderHeaders(headers.toList) - renderEntityContentType(r, entity) ~~ CrLf - Streamed(byteStrings(chunks.via(ChunkTransformer.flow))) - } - } - - renderStatusLine() - completeResponseRendering(entity) - } - } - - sealed trait StrictOrStreamed - case class Strict(bytes: ResponseRenderingOutput) extends StrictOrStreamed - case class Streamed(source: Source[ResponseRenderingOutput, Any]) extends StrictOrStreamed - } - - sealed trait CloseMode - case object DontClose extends CloseMode - case object CloseConnection extends CloseMode - case class SwitchToWebSocket(handler: Either[Graph[FlowShape[FrameEvent, FrameEvent], Any], Graph[FlowShape[Message, Message], Any]]) extends CloseMode -} - -/** - * INTERNAL API - */ -private[http] final case class ResponseRenderingContext( - response: HttpResponse, - requestMethod: HttpMethod = HttpMethods.GET, - requestProtocol: HttpProtocol = HttpProtocols.`HTTP/1.1`, - closeRequested: Boolean = false) - -/** INTERNAL API */ -private[http] sealed trait ResponseRenderingOutput -/** INTERNAL API */ -private[http] object ResponseRenderingOutput { - private[http] case class HttpData(bytes: ByteString) extends ResponseRenderingOutput - private[http] case class SwitchToWebSocket(httpResponseBytes: ByteString, handler: Either[Graph[FlowShape[FrameEvent, FrameEvent], Any], Graph[FlowShape[Message, Message], Any]]) extends ResponseRenderingOutput -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/rendering/RenderSupport.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/rendering/RenderSupport.scala deleted file mode 100644 index 0c2ddc5643..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/rendering/RenderSupport.scala +++ /dev/null @@ -1,167 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.rendering - -import akka.parboiled2.CharUtils -import akka.stream.{ Attributes, SourceShape } -import akka.util.ByteString -import akka.event.LoggingAdapter -import akka.stream.impl.fusing.GraphStages.SimpleLinearGraphStage -import akka.stream.scaladsl._ -import akka.stream.stage._ -import akka.http.scaladsl.model._ -import akka.http.impl.util._ -import akka.http.scaladsl.model.HttpEntity.ChunkStreamPart - -import akka.stream.stage.{ Context, GraphStage, SyncDirective, TerminationDirective } -import akka.stream._ -import akka.stream.scaladsl.{ Sink, Source, Flow, Keep } -/** - * INTERNAL API - */ -private object RenderSupport { - val DefaultStatusLineBytes = "HTTP/1.1 200 OK\r\n".asciiBytes - val StatusLineStartBytes = "HTTP/1.1 ".asciiBytes - val ChunkedBytes = "chunked".asciiBytes - val KeepAliveBytes = "Keep-Alive".asciiBytes - val CloseBytes = "close".asciiBytes - - private[this] final val PreRenderedContentTypes = { - val m = new java.util.HashMap[ContentType, Array[Byte]](16) - def preRenderContentType(ct: ContentType) = - m.put(ct, (new ByteArrayRendering(32) ~~ headers.`Content-Type` ~~ ct ~~ CrLf).get) - - import ContentTypes._ - preRenderContentType(`application/json`) - preRenderContentType(`text/plain(UTF-8)`) - preRenderContentType(`text/xml(UTF-8)`) - preRenderContentType(`text/html(UTF-8)`) - preRenderContentType(`text/csv(UTF-8)`) - m - } - - def CrLf = Rendering.CrLf - - implicit val trailerRenderer = Renderer.genericSeqRenderer[Renderable, HttpHeader](CrLf, Rendering.Empty) - - val defaultLastChunkBytes: ByteString = renderChunk(HttpEntity.LastChunk) - - def CancelSecond[T, Mat](first: Source[T, Mat], second: Source[T, Any]): Source[T, Mat] = { - Source.fromGraph(GraphDSL.create(first) { implicit b ⇒ frst ⇒ - import GraphDSL.Implicits._ - second ~> Sink.cancelled - SourceShape(frst.out) - }) - } - - def renderEntityContentType(r: Rendering, entity: HttpEntity) = { - val ct = entity.contentType - if (ct != ContentTypes.NoContentType) { - val preRendered = PreRenderedContentTypes.get(ct) - if (preRendered ne null) r ~~ preRendered // re-use pre-rendered - else r ~~ headers.`Content-Type` ~~ ct ~~ CrLf // render ad-hoc - } else r // don't render - } - - def renderByteStrings(r: ByteStringRendering, entityBytes: ⇒ Source[ByteString, Any], - skipEntity: Boolean = false): Source[ByteString, Any] = { - val messageStart = Source.single(r.get) - val messageBytes = - if (!skipEntity) (messageStart ++ entityBytes).mapMaterializedValue(_ ⇒ ()) - else CancelSecond(messageStart, entityBytes) - messageBytes - } - - object ChunkTransformer { - val flow = Flow.fromGraph(new ChunkTransformer).named("renderChunks") - } - - class ChunkTransformer extends GraphStage[FlowShape[HttpEntity.ChunkStreamPart, ByteString]] { - val out: Outlet[ByteString] = Outlet("ChunkTransformer.out") - val in: Inlet[HttpEntity.ChunkStreamPart] = Inlet("ChunkTransformer.in") - val shape: FlowShape[HttpEntity.ChunkStreamPart, ByteString] = FlowShape.of(in, out) - - override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = - new GraphStageLogic(shape) with InHandler with OutHandler { - override def onPush(): Unit = { - val chunk = grab(in) - val bytes = renderChunk(chunk) - push(out, bytes) - if (chunk.isLastChunk) completeStage() - } - - override def onPull(): Unit = pull(in) - - override def onUpstreamFinish(): Unit = { - emit(out, defaultLastChunkBytes) - completeStage() - } - setHandlers(in, out, this) - } - } - - object CheckContentLengthTransformer { - def flow(contentLength: Long) = Flow[ByteString].via(new CheckContentLengthTransformer(contentLength)) - } - - final class CheckContentLengthTransformer(length: Long) extends SimpleLinearGraphStage[ByteString] { - override def initialAttributes: Attributes = Attributes.name("CheckContentLength") - - override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = - new GraphStageLogic(shape) with InHandler with OutHandler { - override def toString = s"CheckContentLength(sent=$sent)" - - private var sent = 0L - - override def onPush(): Unit = { - val elem = grab(in) - sent += elem.length - if (sent <= length) { - push(out, elem) - } else { - failStage(InvalidContentLengthException(s"HTTP message had declared Content-Length $length but entity data stream amounts to more bytes")) - } - } - - override def onUpstreamFinish(): Unit = { - if (sent < length) { - failStage(InvalidContentLengthException(s"HTTP message had declared Content-Length $length but entity data stream amounts to ${length - sent} bytes less")) - } else { - completeStage() - } - } - - override def onPull(): Unit = pull(in) - - setHandlers(in, out, this) - } - - override def toString = "CheckContentLength" - } - - private def renderChunk(chunk: HttpEntity.ChunkStreamPart): ByteString = { - import chunk._ - val renderedSize = // buffer space required for rendering (without trailer) - CharUtils.numberOfHexDigits(data.length) + - (if (extension.isEmpty) 0 else extension.length + 1) + - data.length + - 2 + 2 - val r = new ByteStringRendering(renderedSize) - r ~~% data.length - if (extension.nonEmpty) r ~~ ';' ~~ extension - r ~~ CrLf - chunk match { - case HttpEntity.Chunk(data, _) ⇒ r ~~ data - case HttpEntity.LastChunk(_, Nil) ⇒ // nothing to do - case HttpEntity.LastChunk(_, trailer) ⇒ r ~~ trailer ~~ CrLf - } - r ~~ CrLf - r.get - } - - def suppressionWarning(log: LoggingAdapter, h: HttpHeader, - msg: String = "the akka-http-core layer sets this header automatically!"): Unit = - log.warning("Explicitly set HTTP header '{}' is ignored, {}", h, msg) -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/server/HttpAttributes.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/server/HttpAttributes.scala deleted file mode 100644 index dfc4ba5132..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/server/HttpAttributes.scala +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.server - -import java.net.InetSocketAddress - -import akka.stream.Attributes - -/** - * INTERNAL API - * Internally used attributes set in the HTTP pipeline. - * May potentially be opened up in the future. - */ -private[akka] object HttpAttributes { - import Attributes._ - - private[akka] final case class RemoteAddress(address: Option[InetSocketAddress]) extends Attribute - - private[akka] def remoteAddress(address: Option[InetSocketAddress]) = - Attributes(RemoteAddress(address)) - -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/server/HttpServerBluePrint.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/server/HttpServerBluePrint.scala deleted file mode 100644 index 80414e2be3..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/server/HttpServerBluePrint.scala +++ /dev/null @@ -1,702 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.server - -import java.net.InetSocketAddress -import java.util.concurrent.atomic.AtomicReference -import scala.concurrent.{ Promise, Future } -import scala.concurrent.duration.{ Deadline, FiniteDuration, Duration } -import scala.collection.immutable -import scala.util.control.NonFatal -import akka.NotUsed -import akka.actor.Cancellable -import akka.japi.Function -import akka.event.LoggingAdapter -import akka.util.ByteString -import akka.stream._ -import akka.stream.TLSProtocol._ -import akka.stream.scaladsl._ -import akka.stream.stage._ -import akka.http.scaladsl.settings.ServerSettings -import akka.http.impl.engine.HttpConnectionTimeoutException -import akka.http.impl.engine.parsing.ParserOutput._ -import akka.http.impl.engine.parsing._ -import akka.http.impl.engine.rendering.{ HttpResponseRendererFactory, ResponseRenderingContext, ResponseRenderingOutput } -import akka.http.impl.engine.ws._ -import akka.http.impl.util._ -import akka.http.scaladsl.util.FastFuture.EnhancedFuture -import akka.http.scaladsl.{ TimeoutAccess, Http } -import akka.http.scaladsl.model.headers.`Timeout-Access` -import akka.http.javadsl.model -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.ws.Message - -/** - * INTERNAL API - * - * - * HTTP pipeline setup (without the underlying SSL/TLS (un)wrapping and the websocket switch): - * - * +----------+ +-------------+ +-------------+ +-----------+ - * HttpRequest | | Http- | request- | Request- | | Request- | request- | ByteString - * | <------------+ <----------+ Preparation <----------+ <-------------+ Parsing <----------- - * | | | Request | | Output | | Output | | - * | | | +-------------+ | | +-----------+ - * | | | | | - * | Application- | One2One- | | controller- | - * | Flow | Bidi | | Stage | - * | | | | | - * | | | | | +-----------+ - * | HttpResponse | | HttpResponse | | Response- | renderer- | ByteString - * v -------------> +-----------------------------------> +-------------> Pipeline +----------> - * | | | | Rendering- | | - * +----------+ +-------------+ Context +-----------+ - */ -private[http] object HttpServerBluePrint { - def apply(settings: ServerSettings, remoteAddress: Option[InetSocketAddress], log: LoggingAdapter): Http.ServerLayer = { - val theStack = - userHandlerGuard(settings.pipeliningLimit) atop - requestTimeoutSupport(settings.timeouts.requestTimeout) atop - requestPreparation(settings) atop - controller(settings, log) atop - parsingRendering(settings, log) atop - websocketSupport(settings, log) atop - tlsSupport - - if (settings.remoteAddressHeader && remoteAddress.isDefined) theStack.withAttributes(HttpAttributes.remoteAddress(remoteAddress)) - else theStack - } - - val tlsSupport: BidiFlow[ByteString, SslTlsOutbound, SslTlsInbound, SessionBytes, NotUsed] = - BidiFlow.fromFlows(Flow[ByteString].map(SendBytes), Flow[SslTlsInbound].collect { case x: SessionBytes ⇒ x }) - - def websocketSupport(settings: ServerSettings, log: LoggingAdapter): BidiFlow[ResponseRenderingOutput, ByteString, SessionBytes, SessionBytes, NotUsed] = - BidiFlow.fromGraph(new ProtocolSwitchStage(settings, log)) - - def parsingRendering(settings: ServerSettings, log: LoggingAdapter): BidiFlow[ResponseRenderingContext, ResponseRenderingOutput, SessionBytes, RequestOutput, NotUsed] = - BidiFlow.fromFlows(rendering(settings, log), parsing(settings, log)) - - def controller(settings: ServerSettings, log: LoggingAdapter): BidiFlow[HttpResponse, ResponseRenderingContext, RequestOutput, RequestOutput, NotUsed] = - BidiFlow.fromGraph(new ControllerStage(settings, log)).reversed - - def requestPreparation(settings: ServerSettings): BidiFlow[HttpResponse, HttpResponse, RequestOutput, HttpRequest, NotUsed] = - BidiFlow.fromFlows(Flow[HttpResponse], new PrepareRequests(settings)) - - def requestTimeoutSupport(timeout: Duration): BidiFlow[HttpResponse, HttpResponse, HttpRequest, HttpRequest, NotUsed] = - BidiFlow.fromGraph(new RequestTimeoutSupport(timeout)).reversed - - /** - * Two state stage, either transforms an incoming RequestOutput into a HttpRequest with strict entity and then pushes - * that (the "idle" inHandler) or creates a HttpRequest with a streamed entity and switch to a state which will push - * incoming chunks into the streaming entity until end of request is reached (the StreamedEntityCreator case in create - * entity). - */ - final class PrepareRequests(settings: ServerSettings) extends GraphStage[FlowShape[RequestOutput, HttpRequest]] { - val in = Inlet[RequestOutput]("PrepareRequests.in") - val out = Outlet[HttpRequest]("PrepareRequests.out") - override val shape: FlowShape[RequestOutput, HttpRequest] = FlowShape.of(in, out) - - override def createLogic(inheritedAttributes: Attributes) = new GraphStageLogic(shape) with InHandler with OutHandler { - val remoteAddress = inheritedAttributes.get[HttpAttributes.RemoteAddress].flatMap(_.address) - var downstreamPullWaiting = false - var completionDeferred = false - var entitySource: SubSourceOutlet[RequestOutput] = _ - - // optimization: to avoid allocations the "idle" case in and out handlers are put directly on the GraphStageLogic itself - override def onPull(): Unit = { - pull(in) - } - - // optimization: this callback is used to handle entity substream cancellation to avoid allocating a dedicated handler - override def onDownstreamFinish(): Unit = { - if (entitySource ne null) { - // application layer has cancelled or only partially consumed response entity: - // connection will be closed - entitySource.complete() - completeStage() - } - } - - override def onPush(): Unit = grab(in) match { - case RequestStart(method, uri, protocol, hdrs, entityCreator, _, _) ⇒ - val effectiveMethod = if (method == HttpMethods.HEAD && settings.transparentHeadRequests) HttpMethods.GET else method - val effectiveHeaders = - if (settings.remoteAddressHeader && remoteAddress.isDefined) - headers.`Remote-Address`(RemoteAddress(remoteAddress.get)) +: hdrs - else hdrs - - val entity = createEntity(entityCreator) withSizeLimit settings.parserSettings.maxContentLength - push(out, HttpRequest(effectiveMethod, uri, effectiveHeaders, entity, protocol)) - case other ⇒ - throw new IllegalStateException(s"unexpected element of type ${other.getClass}") - } - - setIdleHandlers() - - def setIdleHandlers(): Unit = { - if (completionDeferred) { - completeStage() - } else { - setHandler(in, this) - setHandler(out, this) - if (downstreamPullWaiting) { - downstreamPullWaiting = false - pull(in) - } - } - } - - def createEntity(creator: EntityCreator[RequestOutput, RequestEntity]): RequestEntity = - creator match { - case StrictEntityCreator(entity) ⇒ entity - case StreamedEntityCreator(creator) ⇒ streamRequestEntity(creator) - } - - def streamRequestEntity(creator: (Source[ParserOutput.RequestOutput, NotUsed]) ⇒ RequestEntity): RequestEntity = { - // stream incoming chunks into the request entity until we reach the end of it - // and then toggle back to "idle" - - entitySource = new SubSourceOutlet[RequestOutput]("EntitySource") - // optimization: re-use the idle outHandler - entitySource.setHandler(this) - - // optimization: handlers are combined to reduce allocations - val chunkedRequestHandler = new InHandler with OutHandler { - def onPush(): Unit = { - grab(in) match { - case MessageEnd ⇒ - entitySource.complete() - entitySource = null - setIdleHandlers() - - case x ⇒ entitySource.push(x) - } - } - override def onUpstreamFinish(): Unit = { - entitySource.complete() - completeStage() - } - override def onUpstreamFailure(ex: Throwable): Unit = { - entitySource.fail(ex) - failStage(ex) - } - override def onPull(): Unit = { - // remember this until we are done with the chunked entity - // so can pull downstream then - downstreamPullWaiting = true - } - override def onDownstreamFinish(): Unit = { - // downstream signalled not wanting any more requests - // we should keep processing the entity stream and then - // when it completes complete the stage - completionDeferred = true - } - } - - setHandler(in, chunkedRequestHandler) - setHandler(out, chunkedRequestHandler) - creator(Source.fromGraph(entitySource.source)) - } - - } - } - - def parsing(settings: ServerSettings, log: LoggingAdapter): Flow[SessionBytes, RequestOutput, NotUsed] = { - import settings._ - - // the initial header parser we initially use for every connection, - // will not be mutated, all "shared copy" parsers copy on first-write into the header cache - val rootParser = new HttpRequestParser(parserSettings, rawRequestUriHeader, - HttpHeaderParser(parserSettings, log) { info ⇒ - if (parserSettings.illegalHeaderWarnings) - logParsingError(info withSummaryPrepended "Illegal request header", log, parserSettings.errorLoggingVerbosity) - }) - - def establishAbsoluteUri(requestOutput: RequestOutput): RequestOutput = requestOutput match { - case start: RequestStart ⇒ - try { - val effectiveUri = HttpRequest.effectiveUri(start.uri, start.headers, securedConnection = false, defaultHostHeader) - start.copy(uri = effectiveUri) - } catch { - case e: IllegalUriException ⇒ - MessageStartError(StatusCodes.BadRequest, ErrorInfo("Request is missing required `Host` header", e.getMessage)) - } - case x ⇒ x - } - - Flow[SessionBytes].via(rootParser).map(establishAbsoluteUri) - } - - def rendering(settings: ServerSettings, log: LoggingAdapter): Flow[ResponseRenderingContext, ResponseRenderingOutput, NotUsed] = { - import settings._ - - val responseRendererFactory = new HttpResponseRendererFactory(serverHeader, responseHeaderSizeHint, log) - - val errorHandler: PartialFunction[Throwable, Throwable] = { - // idle timeouts should not result in errors in the log. See 19058. - case timeout: HttpConnectionTimeoutException ⇒ - log.debug(s"Closing HttpConnection due to timeout: ${timeout.getMessage}"); timeout - case t ⇒ log.error(t, "Outgoing response stream error"); t - } - - Flow[ResponseRenderingContext] - .via(responseRendererFactory.renderer.named("renderer")) - .via(MapError[ResponseRenderingOutput](errorHandler).named("errorLogger")) - } - - class RequestTimeoutSupport(initialTimeout: Duration) - extends GraphStage[BidiShape[HttpRequest, HttpRequest, HttpResponse, HttpResponse]] { - private val requestIn = Inlet[HttpRequest]("requestIn") - private val requestOut = Outlet[HttpRequest]("requestOut") - private val responseIn = Inlet[HttpResponse]("responseIn") - private val responseOut = Outlet[HttpResponse]("responseOut") - - override def initialAttributes = Attributes.name("RequestTimeoutSupport") - - val shape = new BidiShape(requestIn, requestOut, responseIn, responseOut) - - def createLogic(effectiveAttributes: Attributes) = new GraphStageLogic(shape) { - var openTimeouts = immutable.Queue[TimeoutAccessImpl]() - setHandler(requestIn, new InHandler { - def onPush(): Unit = { - val request = grab(requestIn) - val (entity, requestEnd) = HttpEntity.captureTermination(request.entity) - val access = new TimeoutAccessImpl(request, initialTimeout, requestEnd, - getAsyncCallback(emitTimeoutResponse), interpreter.materializer) - openTimeouts = openTimeouts.enqueue(access) - push(requestOut, request.copy(headers = request.headers :+ `Timeout-Access`(access), entity = entity)) - } - override def onUpstreamFinish() = complete(requestOut) - override def onUpstreamFailure(ex: Throwable) = fail(requestOut, ex) - def emitTimeoutResponse(response: (TimeoutAccess, HttpResponse)) = - // the application response might has already arrived after we scheduled the timeout response (which is close but ok) - // or current head (same reason) is not for response the timeout has been scheduled for - if (openTimeouts.headOption.exists(_ eq response._1)) { - emit(responseOut, response._2, () ⇒ completeStage()) - } - }) - // TODO: provide and use default impl for simply connecting an input and an output port as we do here - setHandler(requestOut, new OutHandler { - def onPull(): Unit = pull(requestIn) - override def onDownstreamFinish() = cancel(requestIn) - }) - setHandler(responseIn, new InHandler { - def onPush(): Unit = { - openTimeouts.head.clear() - openTimeouts = openTimeouts.tail - push(responseOut, grab(responseIn)) - } - override def onUpstreamFinish() = complete(responseOut) - override def onUpstreamFailure(ex: Throwable) = fail(responseOut, ex) - }) - setHandler(responseOut, new OutHandler { - def onPull(): Unit = pull(responseIn) - override def onDownstreamFinish() = cancel(responseIn) - }) - } - } - - private class TimeoutSetup( - val timeoutBase: Deadline, - val scheduledTask: Cancellable, - val timeout: Duration, - val handler: HttpRequest ⇒ HttpResponse) - - private object DummyCancellable extends Cancellable { - override def isCancelled: Boolean = true - override def cancel(): Boolean = true - } - - private class TimeoutAccessImpl(request: HttpRequest, initialTimeout: Duration, requestEnd: Future[Unit], - trigger: AsyncCallback[(TimeoutAccess, HttpResponse)], materializer: Materializer) - extends AtomicReference[Future[TimeoutSetup]] with TimeoutAccess with (HttpRequest ⇒ HttpResponse) { self ⇒ - import materializer.executionContext - - initialTimeout match { - case timeout: FiniteDuration ⇒ set { - requestEnd.fast.map(_ ⇒ new TimeoutSetup(Deadline.now, schedule(timeout, this), timeout, this)) - } - case _ ⇒ set { - requestEnd.fast.map(_ ⇒ new TimeoutSetup(Deadline.now, DummyCancellable, Duration.Inf, this)) - } - } - - override def apply(request: HttpRequest) = - //#default-request-timeout-httpresponse - HttpResponse(StatusCodes.ServiceUnavailable, entity = "The server was not able " + - "to produce a timely response to your request.\r\nPlease try again in a short while!") - //# - - def clear(): Unit = // best effort timeout cancellation - get.fast.foreach(setup ⇒ if (setup.scheduledTask ne null) setup.scheduledTask.cancel()) - - override def updateTimeout(timeout: Duration): Unit = update(timeout, null: HttpRequest ⇒ HttpResponse) - override def updateHandler(handler: HttpRequest ⇒ HttpResponse): Unit = update(null, handler) - override def update(timeout: Duration, handler: HttpRequest ⇒ HttpResponse): Unit = { - val promise = Promise[TimeoutSetup]() - for (old ← getAndSet(promise.future).fast) - promise.success { - if ((old.scheduledTask eq null) || old.scheduledTask.cancel()) { - val newHandler = if (handler eq null) old.handler else handler - val newTimeout = if (timeout eq null) old.timeout else timeout - val newScheduling = newTimeout match { - case x: FiniteDuration ⇒ schedule(old.timeoutBase + x - Deadline.now, newHandler) - case _ ⇒ null // don't schedule a new timeout - } - new TimeoutSetup(old.timeoutBase, newScheduling, newTimeout, newHandler) - } else old // too late, the previously set timeout cannot be cancelled anymore - } - } - private def schedule(delay: FiniteDuration, handler: HttpRequest ⇒ HttpResponse): Cancellable = - materializer.scheduleOnce(delay, new Runnable { def run() = trigger.invoke((self, handler(request))) }) - - import akka.http.impl.util.JavaMapping.Implicits._ - /** JAVA API **/ - def update(timeout: Duration, handler: Function[model.HttpRequest, model.HttpResponse]): Unit = - update(timeout, handler(_: HttpRequest).asScala) - def updateHandler(handler: Function[model.HttpRequest, model.HttpResponse]): Unit = - updateHandler(handler(_: HttpRequest).asScala) - } - - class ControllerStage(settings: ServerSettings, log: LoggingAdapter) - extends GraphStage[BidiShape[RequestOutput, RequestOutput, HttpResponse, ResponseRenderingContext]] { - private val requestParsingIn = Inlet[RequestOutput]("requestParsingIn") - private val requestPrepOut = Outlet[RequestOutput]("requestPrepOut") - private val httpResponseIn = Inlet[HttpResponse]("httpResponseIn") - private val responseCtxOut = Outlet[ResponseRenderingContext]("responseCtxOut") - - override def initialAttributes = Attributes.name("ControllerStage") - - val shape = new BidiShape(requestParsingIn, requestPrepOut, httpResponseIn, responseCtxOut) - - def createLogic(effectiveAttributes: Attributes) = new GraphStageLogic(shape) { - val pullHttpResponseIn = () ⇒ pull(httpResponseIn) - var openRequests = immutable.Queue[RequestStart]() - var oneHundredContinueResponsePending = false - var pullSuppressed = false - var messageEndPending = false - - setHandler(requestParsingIn, new InHandler { - def onPush(): Unit = - grab(requestParsingIn) match { - case r: RequestStart ⇒ - openRequests = openRequests.enqueue(r) - messageEndPending = r.createEntity.isInstanceOf[StreamedEntityCreator[_, _]] - val rs = if (r.expect100Continue) { - oneHundredContinueResponsePending = true - r.copy(createEntity = with100ContinueTrigger(r.createEntity)) - } else r - push(requestPrepOut, rs) - case MessageEnd ⇒ - messageEndPending = false - push(requestPrepOut, MessageEnd) - case MessageStartError(status, info) ⇒ finishWithIllegalRequestError(status, info) - case x: EntityStreamError if messageEndPending && openRequests.isEmpty ⇒ - // client terminated the connection after receiving an early response to 100-continue - completeStage() - case x ⇒ - push(requestPrepOut, x) - } - override def onUpstreamFinish() = - if (openRequests.isEmpty) completeStage() - else complete(requestPrepOut) - }) - - setHandler(requestPrepOut, new OutHandler { - def onPull(): Unit = - if (oneHundredContinueResponsePending) pullSuppressed = true - else if (!hasBeenPulled(requestParsingIn)) pull(requestParsingIn) - override def onDownstreamFinish() = cancel(requestParsingIn) - }) - - setHandler(httpResponseIn, new InHandler { - def onPush(): Unit = { - val response = grab(httpResponseIn) - val requestStart = openRequests.head - openRequests = openRequests.tail - val isEarlyResponse = messageEndPending && openRequests.isEmpty - if (isEarlyResponse && response.status.isSuccess) - log.warning( - "Sending an 2xx 'early' response before end of request was received... " + - "Note that the connection will be closed after this response. Also, many clients will not read early responses! " + - "Consider only issuing this response after the request data has been completely read!") - val close = requestStart.closeRequested || - (requestStart.expect100Continue && oneHundredContinueResponsePending) || - (isClosed(requestParsingIn) && openRequests.isEmpty) || - isEarlyResponse - - emit(responseCtxOut, ResponseRenderingContext(response, requestStart.method, requestStart.protocol, close), - pullHttpResponseIn) - if (!isClosed(requestParsingIn) && close && requestStart.expect100Continue) pull(requestParsingIn) - } - override def onUpstreamFinish() = - if (openRequests.isEmpty && isClosed(requestParsingIn)) completeStage() - else complete(responseCtxOut) - override def onUpstreamFailure(ex: Throwable): Unit = - ex match { - case EntityStreamException(errorInfo) ⇒ - // the application has forwarded a request entity stream error to the response stream - finishWithIllegalRequestError(StatusCodes.BadRequest, errorInfo) - - case EntityStreamSizeException(limit, contentLength) ⇒ - val summary = contentLength match { - case Some(cl) ⇒ s"Request Content-Length of $cl bytes exceeds the configured limit of $limit bytes" - case None ⇒ s"Aggregated data length of request entity exceeds the configured limit of $limit bytes" - } - val info = ErrorInfo(summary, "Consider increasing the value of akka.http.server.parsing.max-content-length") - finishWithIllegalRequestError(StatusCodes.RequestEntityTooLarge, info) - - case NonFatal(e) ⇒ - log.error(e, "Internal server error, sending 500 response") - emitErrorResponse(HttpResponse(StatusCodes.InternalServerError)) - } - }) - - class ResponseCtxOutHandler extends OutHandler { - override def onPull() = {} - override def onDownstreamFinish() = - cancel(httpResponseIn) // we cannot fully completeState() here as the websocket pipeline would not complete properly - } - setHandler(responseCtxOut, new ResponseCtxOutHandler { - override def onPull() = { - pull(httpResponseIn) - // after the initial pull here we only ever pull after having emitted in `onPush` of `httpResponseIn` - setHandler(responseCtxOut, new ResponseCtxOutHandler) - } - }) - - def finishWithIllegalRequestError(status: StatusCode, info: ErrorInfo): Unit = { - logParsingError( - info withSummaryPrepended s"Illegal request, responding with status '$status'", - log, settings.parserSettings.errorLoggingVerbosity) - val msg = if (settings.verboseErrorMessages) info.formatPretty else info.summary - emitErrorResponse(HttpResponse(status, entity = msg)) - } - - def emitErrorResponse(response: HttpResponse): Unit = - emit(responseCtxOut, ResponseRenderingContext(response, closeRequested = true), () ⇒ completeStage()) - - /** - * The `Expect: 100-continue` header has a special status in HTTP. - * It allows the client to send an `Expect: 100-continue` header with the request and then pause request sending - * (i.e. hold back sending the request entity). The server reads the request headers, determines whether it wants to - * accept the request and responds with - * - * - `417 Expectation Failed`, if it doesn't support the `100-continue` expectation - * (or if the `Expect` header contains other, unsupported expectations). - * - a `100 Continue` response, - * if it is ready to accept the request entity and the client should go ahead with sending it - * - a final response (like a 4xx to signal some client-side error - * (e.g. if the request entity length is beyond the configured limit) or a 3xx redirect) - * - * Only if the client receives a `100 Continue` response from the server is it allowed to continue sending the request - * entity. In this case it will receive another response after having completed request sending. - * So this special feature breaks the normal "one request - one response" logic of HTTP! - * It therefore requires special handling in all HTTP stacks (client- and server-side). - * - * For us this means: - * - * - on the server-side: - * After having read a `Expect: 100-continue` header with the request we package up an `HttpRequest` instance and send - * it through to the application. Only when (and if) the application then requests data from the entity stream do we - * send out a `100 Continue` response and continue reading the request entity. - * The application can therefore determine itself whether it wants the client to send the request entity - * by deciding whether to look at the request entity data stream or not. - * If the application sends a response *without* having looked at the request entity the client receives this - * response *instead of* the `100 Continue` response and the server closes the connection afterwards. - * - * - on the client-side: - * If the user adds a `Expect: 100-continue` header to the request we need to hold back sending the entity until - * we've received a `100 Continue` response. - */ - val emit100ContinueResponse = - getAsyncCallback[Unit] { _ ⇒ - oneHundredContinueResponsePending = false - emit(responseCtxOut, ResponseRenderingContext(HttpResponse(StatusCodes.Continue))) - if (pullSuppressed) { - pullSuppressed = false - pull(requestParsingIn) - } - } - - case object OneHundredContinueStage extends GraphStage[FlowShape[ParserOutput, ParserOutput]] { - val in: Inlet[ParserOutput] = Inlet("GraphStage.in") - val out: Outlet[ParserOutput] = Outlet("GraphStage.out") - override val shape: FlowShape[ParserOutput, ParserOutput] = FlowShape(in, out) - - override def initialAttributes = Attributes.name("expect100continueTrigger") - - override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = - new GraphStageLogic(shape) with InHandler with OutHandler { - private var oneHundredContinueSent = false - - override def onPush(): Unit = push(out, grab(in)) - override def onPull(): Unit = { - if (!oneHundredContinueSent) { - oneHundredContinueSent = true - emit100ContinueResponse.invoke(()) - } - pull(in) - } - - setHandlers(in, out, this) - } - } - - def with100ContinueTrigger[T <: ParserOutput](createEntity: EntityCreator[T, RequestEntity]) = - StreamedEntityCreator { - createEntity.compose[Source[T, NotUsed]] { - _.via(OneHundredContinueStage.asInstanceOf[GraphStage[FlowShape[T, T]]]) - } - } - } - } - - /** - * Ensures that the user handler - * - produces exactly one response per request - * - has not more than `pipeliningLimit` responses outstanding - */ - def userHandlerGuard(pipeliningLimit: Int): BidiFlow[HttpResponse, HttpResponse, HttpRequest, HttpRequest, NotUsed] = - One2OneBidiFlow[HttpRequest, HttpResponse](pipeliningLimit).reversed - - private class ProtocolSwitchStage(settings: ServerSettings, log: LoggingAdapter) - extends GraphStage[BidiShape[ResponseRenderingOutput, ByteString, SessionBytes, SessionBytes]] { - - private val fromNet = Inlet[SessionBytes]("fromNet") - private val toNet = Outlet[ByteString]("toNet") - - private val toHttp = Outlet[SessionBytes]("toHttp") - private val fromHttp = Inlet[ResponseRenderingOutput]("fromHttp") - - override def initialAttributes = Attributes.name("ProtocolSwitchStage") - - override val shape = BidiShape(fromHttp, toNet, fromNet, toHttp) - - def createLogic(inheritedAttributes: Attributes): GraphStageLogic = new TimerGraphStageLogic(shape) { - import akka.http.impl.engine.rendering.ResponseRenderingOutput._ - - /* - * These handlers are in charge until a switch command comes in, then they - * are replaced. - */ - - setHandler(fromHttp, new InHandler { - override def onPush(): Unit = - grab(fromHttp) match { - case HttpData(b) ⇒ push(toNet, b) - case SwitchToWebSocket(bytes, handlerFlow) ⇒ - push(toNet, bytes) - complete(toHttp) - cancel(fromHttp) - switchToWebSocket(handlerFlow) - } - override def onUpstreamFinish(): Unit = complete(toNet) - override def onUpstreamFailure(ex: Throwable): Unit = fail(toNet, ex) - }) - setHandler(toNet, new OutHandler { - override def onPull(): Unit = pull(fromHttp) - override def onDownstreamFinish(): Unit = completeStage() - }) - - setHandler(fromNet, new InHandler { - override def onPush(): Unit = push(toHttp, grab(fromNet)) - override def onUpstreamFinish(): Unit = complete(toHttp) - override def onUpstreamFailure(ex: Throwable): Unit = fail(toHttp, ex) - }) - setHandler(toHttp, new OutHandler { - override def onPull(): Unit = pull(fromNet) - override def onDownstreamFinish(): Unit = cancel(fromNet) - }) - - private var activeTimers = 0 - private def timeout = ActorMaterializerHelper.downcast(materializer).settings.subscriptionTimeoutSettings.timeout - private def addTimeout(s: SubscriptionTimeout): Unit = { - if (activeTimers == 0) setKeepGoing(true) - activeTimers += 1 - scheduleOnce(s, timeout) - } - private def cancelTimeout(s: SubscriptionTimeout): Unit = - if (isTimerActive(s)) { - activeTimers -= 1 - if (activeTimers == 0) setKeepGoing(false) - cancelTimer(s) - } - override def onTimer(timerKey: Any): Unit = timerKey match { - case SubscriptionTimeout(f) ⇒ - activeTimers -= 1 - if (activeTimers == 0) setKeepGoing(false) - f() - } - - /* - * WebSocket support - */ - def switchToWebSocket(handlerFlow: Either[Graph[FlowShape[FrameEvent, FrameEvent], Any], Graph[FlowShape[Message, Message], Any]]): Unit = { - val frameHandler = handlerFlow match { - case Left(frameHandler) ⇒ frameHandler - case Right(messageHandler) ⇒ - WebSocket.stack(serverSide = true, maskingRandomFactory = settings.websocketRandomFactory, log = log).join(messageHandler) - } - - val sinkIn = new SubSinkInlet[ByteString]("FrameSink") - sinkIn.setHandler(new InHandler { - override def onPush(): Unit = push(toNet, sinkIn.grab()) - override def onUpstreamFinish(): Unit = complete(toNet) - override def onUpstreamFailure(ex: Throwable): Unit = fail(toNet, ex) - }) - - if (isClosed(fromNet)) { - setHandler(toNet, new OutHandler { - override def onPull(): Unit = sinkIn.pull() - override def onDownstreamFinish(): Unit = { - completeStage() - sinkIn.cancel() - } - }) - WebSocket.framing.join(frameHandler).runWith(Source.empty, sinkIn.sink)(subFusingMaterializer) - } else { - val sourceOut = new SubSourceOutlet[ByteString]("FrameSource") - - val timeoutKey = SubscriptionTimeout(() ⇒ { - sourceOut.timeout(timeout) - if (sourceOut.isClosed) completeStage() - }) - addTimeout(timeoutKey) - - setHandler(toNet, new OutHandler { - override def onPull(): Unit = sinkIn.pull() - override def onDownstreamFinish(): Unit = { - completeStage() - sinkIn.cancel() - sourceOut.complete() - } - }) - - setHandler(fromNet, new InHandler { - override def onPush(): Unit = sourceOut.push(grab(fromNet).bytes) - override def onUpstreamFinish(): Unit = sourceOut.complete() - override def onUpstreamFailure(ex: Throwable): Unit = sourceOut.fail(ex) - }) - sourceOut.setHandler(new OutHandler { - override def onPull(): Unit = { - if (!hasBeenPulled(fromNet)) pull(fromNet) - cancelTimeout(timeoutKey) - sourceOut.setHandler(new OutHandler { - override def onPull(): Unit = if (!hasBeenPulled(fromNet)) pull(fromNet) - override def onDownstreamFinish(): Unit = cancel(fromNet) - }) - } - override def onDownstreamFinish(): Unit = cancel(fromNet) - }) - - WebSocket.framing.join(frameHandler).runWith(sourceOut.source, sinkIn.sink)(subFusingMaterializer) - } - } - } - } - - private case class SubscriptionTimeout(andThen: () ⇒ Unit) -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/FrameEvent.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/ws/FrameEvent.scala deleted file mode 100644 index 6146056cff..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/FrameEvent.scala +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.ws - -import akka.http.impl.engine.ws.Protocol.Opcode -import akka.util.ByteString - -private[http] sealed trait FrameEventOrError - -private[http] final case class FrameError(p: ProtocolException) extends FrameEventOrError - -/** - * The low-level WebSocket framing model. - * - * INTERNAL API - */ -private[http] sealed trait FrameEvent extends FrameEventOrError { - def data: ByteString - def lastPart: Boolean - def withData(data: ByteString): FrameEvent -} - -/** - * Starts a frame. Contains the frame's headers. May contain all the data of the frame if `lastPart == true`. Otherwise, - * following events will be `FrameData` events that contain the remaining data of the frame. - */ -private[http] final case class FrameStart(header: FrameHeader, data: ByteString) extends FrameEvent { - def lastPart: Boolean = data.size == header.length - def withData(data: ByteString): FrameStart = copy(data = data) - - def isFullMessage: Boolean = header.fin && header.length == data.length -} - -/** - * Frame data that was received after the start of the frame.. - */ -private[http] final case class FrameData(data: ByteString, lastPart: Boolean) extends FrameEvent { - def withData(data: ByteString): FrameData = copy(data = data) -} - -/** Model of the frame header */ -private[http] final case class FrameHeader( - opcode: Protocol.Opcode, - mask: Option[Int], - length: Long, - fin: Boolean, - rsv1: Boolean = false, - rsv2: Boolean = false, - rsv3: Boolean = false) - -private[http] object FrameEvent { - def empty( - opcode: Protocol.Opcode, - fin: Boolean, - rsv1: Boolean = false, - rsv2: Boolean = false, - rsv3: Boolean = false): FrameStart = - fullFrame(opcode, None, ByteString.empty, fin, rsv1, rsv2, rsv3) - def fullFrame(opcode: Protocol.Opcode, mask: Option[Int], data: ByteString, - fin: Boolean, - rsv1: Boolean = false, - rsv2: Boolean = false, - rsv3: Boolean = false): FrameStart = - FrameStart(FrameHeader(opcode, mask, data.length, fin, rsv1, rsv2, rsv3), data) - val emptyLastContinuationFrame: FrameStart = - empty(Protocol.Opcode.Continuation, fin = true) - - def closeFrame(closeCode: Int, reason: String = "", mask: Option[Int] = None): FrameStart = { - require(closeCode >= 1000, s"Invalid close code: $closeCode") - val body = ByteString( - ((closeCode & 0xff00) >> 8).toByte, - (closeCode & 0xff).toByte) ++ ByteString(reason, "UTF8") - - fullFrame(Opcode.Close, mask, FrameEventParser.mask(body, mask), fin = true) - } -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/FrameEventParser.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/ws/FrameEventParser.scala deleted file mode 100644 index 3fea4f3ab8..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/FrameEventParser.scala +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.ws - -import akka.stream.impl.io.ByteStringParser -import akka.util.ByteString -import scala.annotation.tailrec -import akka.stream.Attributes - -/** - * Streaming parser for the WebSocket framing protocol as defined in RFC6455 - * - * http://tools.ietf.org/html/rfc6455 - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-------+-+-------------+-------------------------------+ - * |F|R|R|R| opcode|M| Payload len | Extended payload length | - * |I|S|S|S| (4) |A| (7) | (16/64) | - * |N|V|V|V| |S| | (if payload len==126/127) | - * | |1|2|3| |K| | | - * +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + - * | Extended payload length continued, if payload len == 127 | - * + - - - - - - - - - - - - - - - +-------------------------------+ - * | |Masking-key, if MASK set to 1 | - * +-------------------------------+-------------------------------+ - * | Masking-key (continued) | Payload Data | - * +-------------------------------- - - - - - - - - - - - - - - - + - * : Payload Data continued ... : - * + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - * | Payload Data continued ... | - * +---------------------------------------------------------------+ - * - * INTERNAL API - */ -private[http] object FrameEventParser extends ByteStringParser[FrameEvent] { - import ByteStringParser._ - - override def createLogic(attr: Attributes) = new ParsingLogic { - startWith(ReadFrameHeader) - - trait Step extends ParseStep[FrameEvent] { - override def onTruncation(): Unit = failStage(new ProtocolException("Data truncated")) - } - - object ReadFrameHeader extends Step { - override def parse(reader: ByteReader): ParseResult[FrameEvent] = { - import Protocol._ - - val flagsAndOp = reader.readByte() - val maskAndLength = reader.readByte() - - val flags = flagsAndOp & FLAGS_MASK - val op = flagsAndOp & OP_MASK - - val maskBit = (maskAndLength & MASK_MASK) != 0 - val length7 = maskAndLength & LENGTH_MASK - - val length = - length7 match { - case 126 ⇒ reader.readShortBE().toLong - case 127 ⇒ reader.readLongBE() - case x ⇒ x.toLong - } - - if (length < 0) throw new ProtocolException("Highest bit of 64bit length was set") - - val mask = - if (maskBit) Some(reader.readIntBE()) - else None - - def isFlagSet(mask: Int): Boolean = (flags & mask) != 0 - val header = - FrameHeader( - Opcode.forCode(op.toByte), - mask, - length, - fin = isFlagSet(FIN_MASK), - rsv1 = isFlagSet(RSV1_MASK), - rsv2 = isFlagSet(RSV2_MASK), - rsv3 = isFlagSet(RSV3_MASK)) - - val takeNow = (header.length min reader.remainingSize).toInt - val thisFrameData = reader.take(takeNow) - val noMoreData = thisFrameData.length == length - - val nextState = - if (noMoreData) ReadFrameHeader - else new ReadData(length - thisFrameData.length) - - ParseResult(Some(FrameStart(header, thisFrameData.compact)), nextState, true) - } - } - - class ReadData(_remaining: Long) extends Step { - override def canWorkWithPartialData = true - var remaining = _remaining - override def parse(reader: ByteReader): ParseResult[FrameEvent] = - if (reader.remainingSize < remaining) { - remaining -= reader.remainingSize - ParseResult(Some(FrameData(reader.takeAll(), lastPart = false)), this, true) - } else { - ParseResult(Some(FrameData(reader.take(remaining.toInt), lastPart = true)), ReadFrameHeader, true) - } - } - } - - def mask(bytes: ByteString, _mask: Option[Int]): ByteString = - _mask match { - case Some(m) ⇒ mask(bytes, m)._1 - case None ⇒ bytes - } - - def mask(bytes: ByteString, mask: Int): (ByteString, Int) = { - @tailrec def rec(bytes: Array[Byte], offset: Int, mask: Int): Int = - if (offset >= bytes.length) mask - else { - val newMask = Integer.rotateLeft(mask, 8) // we cycle through the mask in BE order - bytes(offset) = (bytes(offset) ^ (newMask & 0xff)).toByte - rec(bytes, offset + 1, newMask) - } - - val buffer = bytes.toArray[Byte] - val newMask = rec(buffer, 0, mask) - (ByteString(buffer), newMask) - } - - def parseCloseCode(data: ByteString): Option[(Int, String)] = { - def invalid(reason: String) = Some((Protocol.CloseCodes.ProtocolError, s"Peer sent illegal close frame ($reason).")) - - if (data.length >= 2) { - val code = ((data(0) & 0xff) << 8) | (data(1) & 0xff) - val message = Utf8Decoder.decode(data.drop(2)) - if (!Protocol.CloseCodes.isValid(code)) invalid(s"invalid close code '$code'") - else if (message.isFailure) invalid("close reason message is invalid UTF8") - else Some((code, message.get)) - } else if (data.length == 1) invalid("close code must be length 2 but was 1") // must be >= length 2 if not empty - else None - } - - override def toString: String = "FrameEventParser" -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/FrameEventRenderer.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/ws/FrameEventRenderer.scala deleted file mode 100644 index 0702e42a22..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/FrameEventRenderer.scala +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.ws - -import akka.util.ByteString -import akka.stream.stage.{ StatefulStage, SyncDirective, Context } - -import scala.annotation.tailrec - -/** - * Renders FrameEvents to ByteString. - * - * INTERNAL API - */ -private[http] class FrameEventRenderer extends StatefulStage[FrameEvent, ByteString] { - def initial: State = Idle - - object Idle extends State { - def onPush(elem: FrameEvent, ctx: Context[ByteString]): SyncDirective = elem match { - case start @ FrameStart(header, data) ⇒ - require(header.length >= data.size) - if (!start.lastPart && header.length > 0) become(renderData(header.length - data.length, this)) - - ctx.push(renderStart(start)) - - case f: FrameData ⇒ - ctx.fail(new IllegalStateException("unexpected FrameData (need FrameStart first)")) - } - } - - def renderData(initialRemaining: Long, nextState: State): State = - new State { - var remaining: Long = initialRemaining - - def onPush(elem: FrameEvent, ctx: Context[ByteString]): SyncDirective = elem match { - case FrameData(data, lastPart) ⇒ - if (data.size > remaining) - throw new IllegalStateException(s"Expected $remaining frame bytes but got ${data.size}") - else if (data.size == remaining) { - if (!lastPart) throw new IllegalStateException(s"Frame data complete but `lastPart` flag not set") - become(nextState) - ctx.push(data) - } else { - remaining -= data.size - ctx.push(data) - } - - case f: FrameStart ⇒ - ctx.fail(new IllegalStateException("unexpected FrameStart (need more FrameData first)")) - } - } - - def renderStart(start: FrameStart): ByteString = renderHeader(start.header) ++ start.data - def renderHeader(header: FrameHeader): ByteString = { - import Protocol._ - - val length = header.length - val (lengthBits, extraLengthBytes) = length match { - case x if x < 126 ⇒ (x.toInt, 0) - case x if x <= 0xFFFF ⇒ (126, 2) - case _ ⇒ (127, 8) - } - - val maskBytes = if (header.mask.isDefined) 4 else 0 - val totalSize = 2 + extraLengthBytes + maskBytes - - val data = new Array[Byte](totalSize) - - def bool(b: Boolean, mask: Int): Int = if (b) mask else 0 - val flags = - bool(header.fin, FIN_MASK) | - bool(header.rsv1, RSV1_MASK) | - bool(header.rsv2, RSV2_MASK) | - bool(header.rsv3, RSV3_MASK) - - data(0) = (flags | header.opcode.code).toByte - data(1) = (bool(header.mask.isDefined, MASK_MASK) | lengthBits).toByte - - extraLengthBytes match { - case 0 ⇒ - case 2 ⇒ - data(2) = ((length & 0xFF00) >> 8).toByte - data(3) = ((length & 0x00FF) >> 0).toByte - case 8 ⇒ - @tailrec def addLongBytes(l: Long, writtenBytes: Int): Unit = - if (writtenBytes < 8) { - data(2 + writtenBytes) = (l & 0xff).toByte - addLongBytes(java.lang.Long.rotateLeft(l, 8), writtenBytes + 1) - } - - addLongBytes(java.lang.Long.rotateLeft(length, 8), 0) - } - - val maskOffset = 2 + extraLengthBytes - header.mask.foreach { mask ⇒ - data(maskOffset + 0) = ((mask & 0xFF000000) >> 24).toByte - data(maskOffset + 1) = ((mask & 0x00FF0000) >> 16).toByte - data(maskOffset + 2) = ((mask & 0x0000FF00) >> 8).toByte - data(maskOffset + 3) = ((mask & 0x000000FF) >> 0).toByte - } - - ByteString(data) - } -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/FrameHandler.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/ws/FrameHandler.scala deleted file mode 100644 index 7803b21545..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/FrameHandler.scala +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.ws - -import akka.NotUsed -import akka.stream.scaladsl.Flow -import akka.util.ByteString -import Protocol.Opcode -import akka.event.Logging -import akka.stream.stage.{ GraphStage, GraphStageLogic, InHandler, OutHandler } -import akka.stream.{ Attributes, FlowShape, Inlet, Outlet } - -import scala.util.control.NonFatal - -/** - * The frame handler validates frames, multiplexes data to the user handler or to the bypass and - * UTF-8 decodes text frames. - * - * INTERNAL API - */ -private[http] object FrameHandler { - - def create(server: Boolean): Flow[FrameEventOrError, Output, NotUsed] = - Flow[FrameEventOrError].via(new HandlerStage(server)) - - private class HandlerStage(server: Boolean) extends GraphStage[FlowShape[FrameEventOrError, Output]] { - val in = Inlet[FrameEventOrError](Logging.simpleName(this) + ".in") - val out = Outlet[Output](Logging.simpleName(this) + ".out") - override val shape = FlowShape(in, out) - - override def toString: String = s"HandlerStage(server=$server)" - - override def createLogic(attributes: Attributes): GraphStageLogic = - new GraphStageLogic(shape) with OutHandler { - setHandler(out, this) - setHandler(in, IdleHandler) - - override def onPull(): Unit = pull(in) - - private object IdleHandler extends ControlFrameStartHandler { - def setAndHandleFrameStartWith(newHandler: ControlFrameStartHandler, start: FrameStart): Unit = { - setHandler(in, newHandler) - newHandler.handleFrameStart(start) - } - - override def handleRegularFrameStart(start: FrameStart): Unit = - (start.header.opcode, start.isFullMessage) match { - case (Opcode.Binary, true) ⇒ publishMessagePart(BinaryMessagePart(start.data, last = true)) - case (Opcode.Binary, false) ⇒ setAndHandleFrameStartWith(new BinaryMessagehandler, start) - case (Opcode.Text, _) ⇒ setAndHandleFrameStartWith(new TextMessageHandler, start) - case x ⇒ pushProtocolError() - } - } - - private class BinaryMessagehandler extends MessageHandler(Opcode.Binary) { - override def createMessagePart(data: ByteString, last: Boolean): MessageDataPart = - BinaryMessagePart(data, last) - } - - private class TextMessageHandler extends MessageHandler(Opcode.Text) { - val decoder = Utf8Decoder.create() - - override def createMessagePart(data: ByteString, last: Boolean): MessageDataPart = - TextMessagePart(decoder.decode(data, endOfInput = last).get, last) - } - - private abstract class MessageHandler(expectedOpcode: Opcode) extends ControlFrameStartHandler { - var expectFirstHeader = true - var finSeen = false - def createMessagePart(data: ByteString, last: Boolean): MessageDataPart - - override def handleRegularFrameStart(start: FrameStart): Unit = { - if ((expectFirstHeader && start.header.opcode == expectedOpcode) // first opcode must be the expected - || start.header.opcode == Opcode.Continuation) { // further ones continuations - expectFirstHeader = false - - if (start.header.fin) finSeen = true - publish(start) - } else pushProtocolError() - } - - override def handleFrameData(data: FrameData): Unit = publish(data) - - def publish(part: FrameEvent): Unit = try { - publishMessagePart(createMessagePart(part.data, last = finSeen && part.lastPart)) - } catch { - case NonFatal(e) ⇒ closeWithCode(Protocol.CloseCodes.InconsistentData) - } - } - - private trait ControlFrameStartHandler extends FrameHandler { - def handleRegularFrameStart(start: FrameStart): Unit - - override def handleFrameStart(start: FrameStart): Unit = start.header match { - case h: FrameHeader if h.mask.isDefined && !server ⇒ pushProtocolError() - case h: FrameHeader if h.rsv1 || h.rsv2 || h.rsv3 ⇒ pushProtocolError() - case FrameHeader(op, _, length, fin, _, _, _) if op.isControl && (length > 125 || !fin) ⇒ pushProtocolError() - case h: FrameHeader if h.opcode.isControl ⇒ - if (start.isFullMessage) handleControlFrame(h.opcode, start.data, this) - else collectControlFrame(start, this) - case _ ⇒ handleRegularFrameStart(start) - } - - override def handleFrameData(data: FrameData): Unit = - throw new IllegalStateException("Expected FrameStart") - } - - private class ControlFrameDataHandler(opcode: Opcode, _data: ByteString, nextHandler: InHandler) extends FrameHandler { - var data = _data - - override def handleFrameData(data: FrameData): Unit = { - this.data ++= data.data - if (data.lastPart) handleControlFrame(opcode, this.data, nextHandler) - else pull(in) - } - - override def handleFrameStart(start: FrameStart): Unit = - throw new IllegalStateException("Expected FrameData") - } - - private trait FrameHandler extends InHandler { - def handleFrameData(data: FrameData): Unit - def handleFrameStart(start: FrameStart): Unit - - def handleControlFrame(opcode: Opcode, data: ByteString, nextHandler: InHandler): Unit = { - setHandler(in, nextHandler) - opcode match { - case Opcode.Ping ⇒ publishDirectResponse(FrameEvent.fullFrame(Opcode.Pong, None, data, fin = true)) - case Opcode.Pong ⇒ - // ignore unsolicited Pong frame - pull(in) - case Opcode.Close ⇒ - setHandler(in, WaitForPeerTcpClose) - push(out, PeerClosed.parse(data)) - case Opcode.Other(o) ⇒ closeWithCode(Protocol.CloseCodes.ProtocolError, "Unsupported opcode") - case other ⇒ failStage( - new IllegalStateException(s"unexpected message of type [${other.getClass.getName}] when expecting ControlFrame") - ) - } - } - - def pushProtocolError(): Unit = closeWithCode(Protocol.CloseCodes.ProtocolError) - - def closeWithCode(closeCode: Int, reason: String = ""): Unit = { - setHandler(in, CloseAfterPeerClosed) - push(out, ActivelyCloseWithCode(Some(closeCode), reason)) - } - - def collectControlFrame(start: FrameStart, nextHandler: InHandler): Unit = { - require(!start.isFullMessage) - setHandler(in, new ControlFrameDataHandler(start.header.opcode, start.data, nextHandler)) - pull(in) - } - - def publishMessagePart(part: MessageDataPart): Unit = - if (part.last) emitMultiple(out, Iterator(part, MessageEnd), () ⇒ setHandler(in, IdleHandler)) - else push(out, part) - - def publishDirectResponse(frame: FrameStart): Unit = push(out, DirectAnswer(frame)) - - override def onPush(): Unit = grab(in) match { - case data: FrameData ⇒ handleFrameData(data) - case start: FrameStart ⇒ handleFrameStart(start) - case FrameError(ex) ⇒ failStage(ex) - } - } - - private object CloseAfterPeerClosed extends InHandler { - override def onPush(): Unit = grab(in) match { - case FrameStart(FrameHeader(Opcode.Close, _, length, _, _, _, _), data) ⇒ - setHandler(in, WaitForPeerTcpClose) - push(out, PeerClosed.parse(data)) - case _ ⇒ pull(in) // ignore all other data - } - } - - private object WaitForPeerTcpClose extends InHandler { - override def onPush(): Unit = pull(in) // ignore - } - } - } - - sealed trait Output - - sealed trait MessagePart extends Output { - def isMessageEnd: Boolean - } - sealed trait MessageDataPart extends MessagePart { - def isMessageEnd = false - def last: Boolean - } - final case class TextMessagePart(data: String, last: Boolean) extends MessageDataPart - final case class BinaryMessagePart(data: ByteString, last: Boolean) extends MessageDataPart - case object MessageEnd extends MessagePart { - def isMessageEnd: Boolean = true - } - final case class PeerClosed(code: Option[Int], reason: String = "") extends MessagePart with BypassEvent { - def isMessageEnd: Boolean = true - } - object PeerClosed { - def parse(data: ByteString): PeerClosed = - FrameEventParser.parseCloseCode(data) match { - case Some((code, reason)) ⇒ PeerClosed(Some(code), reason) - case None ⇒ PeerClosed(None) - } - } - - sealed trait BypassEvent extends Output - final case class DirectAnswer(frame: FrameStart) extends BypassEvent - final case class ActivelyCloseWithCode(code: Option[Int], reason: String = "") extends MessagePart with BypassEvent { - def isMessageEnd: Boolean = true - } - case object UserHandlerCompleted extends BypassEvent - case class UserHandlerErredOut(cause: Throwable) extends BypassEvent -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/FrameOutHandler.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/ws/FrameOutHandler.scala deleted file mode 100644 index b719a1163e..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/FrameOutHandler.scala +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.ws - -import akka.NotUsed -import akka.event.LoggingAdapter -import akka.stream.scaladsl.Flow -import scala.concurrent.duration.FiniteDuration -import akka.stream.stage._ -import akka.http.impl.util.Timestamp -import akka.http.impl.engine.ws.FrameHandler._ -import WebSocket.Tick -import akka.http.impl.engine.ws.FrameHandler.UserHandlerErredOut - -/** - * Implements the transport connection close handling at the end of the pipeline. - * - * INTERNAL API - */ -private[http] class FrameOutHandler(serverSide: Boolean, _closeTimeout: FiniteDuration, log: LoggingAdapter) extends StatefulStage[FrameOutHandler.Input, FrameStart] { - def initial: StageState[AnyRef, FrameStart] = Idle - def closeTimeout: Timestamp = Timestamp.now + _closeTimeout - - private object Idle extends CompletionHandlingState { - def onPush(elem: AnyRef, ctx: Context[FrameStart]): SyncDirective = elem match { - case start: FrameStart ⇒ ctx.push(start) - case DirectAnswer(frame) ⇒ ctx.push(frame) - case PeerClosed(code, reason) if !code.exists(Protocol.CloseCodes.isError) ⇒ - // let user complete it, FIXME: maybe make configurable? immediately, or timeout - become(new WaitingForUserHandlerClosed(FrameEvent.closeFrame(code.getOrElse(Protocol.CloseCodes.Regular), reason))) - ctx.pull() - case PeerClosed(code, reason) ⇒ - val closeFrame = FrameEvent.closeFrame(code.getOrElse(Protocol.CloseCodes.Regular), reason) - if (serverSide) ctx.pushAndFinish(closeFrame) - else { - become(new WaitingForTransportClose) - ctx.push(closeFrame) - } - case ActivelyCloseWithCode(code, reason) ⇒ - val closeFrame = FrameEvent.closeFrame(code.getOrElse(Protocol.CloseCodes.Regular), reason) - become(new WaitingForPeerCloseFrame()) - ctx.push(closeFrame) - case UserHandlerCompleted ⇒ - become(new WaitingForPeerCloseFrame()) - ctx.push(FrameEvent.closeFrame(Protocol.CloseCodes.Regular)) - case UserHandlerErredOut(e) ⇒ - log.error(e, s"WebSocket handler failed with ${e.getMessage}") - become(new WaitingForPeerCloseFrame()) - ctx.push(FrameEvent.closeFrame(Protocol.CloseCodes.UnexpectedCondition, "internal error")) - case Tick ⇒ ctx.pull() // ignore - } - - def onComplete(ctx: Context[FrameStart]): TerminationDirective = { - become(new SendOutCloseFrameAndComplete(FrameEvent.closeFrame(Protocol.CloseCodes.Regular))) - ctx.absorbTermination() - } - } - - /** - * peer has closed, we want to wait for user handler to close as well - */ - private class WaitingForUserHandlerClosed(closeFrame: FrameStart) extends CompletionHandlingState { - def onPush(elem: AnyRef, ctx: Context[FrameStart]): SyncDirective = elem match { - case UserHandlerCompleted ⇒ sendOutLastFrame(ctx) - case UserHandlerErredOut(e) ⇒ - log.error(e, s"WebSocket handler failed while waiting for handler completion with ${e.getMessage}") - sendOutLastFrame(ctx) - case start: FrameStart ⇒ ctx.push(start) - case _ ⇒ ctx.pull() // ignore - } - - def sendOutLastFrame(ctx: Context[FrameStart]): SyncDirective = - if (serverSide) ctx.pushAndFinish(closeFrame) - else { - become(new WaitingForTransportClose()) - ctx.push(closeFrame) - } - - def onComplete(ctx: Context[FrameStart]): TerminationDirective = - ctx.fail(new IllegalStateException("Mustn't complete before user has completed")) - } - - /** - * we have sent out close frame and wait for peer to sent its close frame - */ - private class WaitingForPeerCloseFrame(timeout: Timestamp = closeTimeout) extends CompletionHandlingState { - def onPush(elem: AnyRef, ctx: Context[FrameStart]): SyncDirective = elem match { - case Tick ⇒ - if (timeout.isPast) ctx.finish() - else ctx.pull() - case PeerClosed(code, reason) ⇒ - if (serverSide) ctx.finish() - else { - become(new WaitingForTransportClose()) - ctx.pull() - } - case _ ⇒ ctx.pull() // ignore - } - - def onComplete(ctx: Context[FrameStart]): TerminationDirective = ctx.finish() - } - - /** - * Both side have sent their close frames, server should close the connection first - */ - private class WaitingForTransportClose(timeout: Timestamp = closeTimeout) extends CompletionHandlingState { - def onPush(elem: AnyRef, ctx: Context[FrameStart]): SyncDirective = elem match { - case Tick ⇒ - if (timeout.isPast) ctx.finish() - else ctx.pull() - case _ ⇒ ctx.pull() // ignore - } - - def onComplete(ctx: Context[FrameStart]): TerminationDirective = ctx.finish() - } - - /** If upstream has already failed we just wait to be able to deliver our close frame and complete */ - private class SendOutCloseFrameAndComplete(closeFrame: FrameStart) extends CompletionHandlingState { - def onPush(elem: AnyRef, ctx: Context[FrameStart]): SyncDirective = - ctx.fail(new IllegalStateException("Didn't expect push after completion")) - - override def onPull(ctx: Context[FrameStart]): SyncDirective = - ctx.pushAndFinish(closeFrame) - - def onComplete(ctx: Context[FrameStart]): TerminationDirective = - ctx.absorbTermination() - } - - private trait CompletionHandlingState extends State { - def onComplete(ctx: Context[FrameStart]): TerminationDirective - } - - override def onUpstreamFinish(ctx: Context[FrameStart]): TerminationDirective = - current.asInstanceOf[CompletionHandlingState].onComplete(ctx) - - override def onUpstreamFailure(cause: scala.Throwable, ctx: Context[FrameStart]): TerminationDirective = cause match { - case p: ProtocolException ⇒ - become(new SendOutCloseFrameAndComplete(FrameEvent.closeFrame(Protocol.CloseCodes.ProtocolError))) - ctx.absorbTermination() - case _ ⇒ super.onUpstreamFailure(cause, ctx) - } -} - -private[http] object FrameOutHandler { - type Input = AnyRef - - def create(serverSide: Boolean, closeTimeout: FiniteDuration, log: LoggingAdapter): Flow[Input, FrameStart, NotUsed] = - Flow[Input].transform(() ⇒ new FrameOutHandler(serverSide, closeTimeout, log)) -} \ No newline at end of file diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/Handshake.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/ws/Handshake.scala deleted file mode 100644 index dd704f7d2c..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/Handshake.scala +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.ws - -import java.util.Random - -import scala.collection.immutable -import scala.collection.immutable.Seq -import scala.reflect.ClassTag -import akka.http.impl.util._ -import akka.http.scaladsl.model.headers._ -import akka.http.scaladsl.model.ws.{ Message, UpgradeToWebSocket } -import akka.http.scaladsl.model._ -import akka.stream.{ FlowShape, Graph } -import akka.util.OptionVal - -/** - * Server-side implementation of the WebSocket handshake - * - * INTERNAL API - */ -private[http] object Handshake { - val CurrentWebSocketVersion = 13 - - object Server { - /** - * Validates a client WebSocket handshake. Returns either `Right(UpgradeToWebSocket)` or - * `Left(MessageStartError)`. - * - * From: http://tools.ietf.org/html/rfc6455#section-4.2.1 - * - * 1. An HTTP/1.1 or higher GET request, including a "Request-URI" - * [RFC2616] that should be interpreted as a /resource name/ - * defined in Section 3 (or an absolute HTTP/HTTPS URI containing - * the /resource name/). - * - * 2. A |Host| header field containing the server's authority. - * - * 3. An |Upgrade| header field containing the value "websocket", - * treated as an ASCII case-insensitive value. - * - * 4. A |Connection| header field that includes the token "Upgrade", - * treated as an ASCII case-insensitive value. - * - * 5. A |Sec-WebSocket-Key| header field with a base64-encoded (see - * Section 4 of [RFC4648]) value that, when decoded, is 16 bytes in - * length. - * - * 6. A |Sec-WebSocket-Version| header field, with a value of 13. - * - * 7. Optionally, an |Origin| header field. This header field is sent - * by all browser clients. A connection attempt lacking this - * header field SHOULD NOT be interpreted as coming from a browser - * client. - * - * 8. Optionally, a |Sec-WebSocket-Protocol| header field, with a list - * of values indicating which protocols the client would like to - * speak, ordered by preference. - * - * 9. Optionally, a |Sec-WebSocket-Extensions| header field, with a - * list of values indicating which extensions the client would like - * to speak. The interpretation of this header field is discussed - * in Section 9.1. - */ - def websocketUpgrade(headers: List[HttpHeader], hostHeaderPresent: Boolean): OptionVal[UpgradeToWebSocket] = { - - // notes on Headers that re REQUIRE to be present here: - // - Host header is validated in general HTTP logic - // - Origin header is optional and, if required, should be validated - // on higher levels (routing, application logic) - // - // TODO See #18709 Extension support is optional in WS and currently unsupported. - // - // these are not needed directly, we verify their presence and correctness only: - // - Upgrade - // - Connection - // - `Sec-WebSocket-Version` - def hasAllRequiredWebsocketUpgradeHeaders: Boolean = { - // single-pass through the headers list while collecting all needed requirements - // this way we avoid scanning the requirements list 3 times (as we would with collect/find) - val it = headers.iterator - var requirementsMet = 0 - val targetRequirements = 3 - while (it.hasNext && (requirementsMet != targetRequirements)) it.next() match { - case u: Upgrade ⇒ if (u.hasWebSocket) requirementsMet += 1 - case c: Connection ⇒ if (c.hasUpgrade) requirementsMet += 1 - case v: `Sec-WebSocket-Version` ⇒ if (v.hasVersion(CurrentWebSocketVersion)) requirementsMet += 1 - case _ ⇒ // continue... - } - requirementsMet == targetRequirements - } - - if (hasAllRequiredWebsocketUpgradeHeaders) { - val key = HttpHeader.fastFind(classOf[`Sec-WebSocket-Key`], headers) - if (key.isDefined && key.get.isValid) { - val protocol = HttpHeader.fastFind(classOf[`Sec-WebSocket-Protocol`], headers) - - val clientSupportedSubprotocols = protocol match { - case OptionVal.Some(p) ⇒ p.protocols - case _ ⇒ Nil - } - - val header = new UpgradeToWebSocketLowLevel { - def requestedProtocols: Seq[String] = clientSupportedSubprotocols - - def handle(handler: Either[Graph[FlowShape[FrameEvent, FrameEvent], Any], Graph[FlowShape[Message, Message], Any]], subprotocol: Option[String]): HttpResponse = { - require( - subprotocol.forall(chosen ⇒ clientSupportedSubprotocols.contains(chosen)), - s"Tried to choose invalid subprotocol '$subprotocol' which wasn't offered by the client: [${requestedProtocols.mkString(", ")}]") - buildResponse(key.get, handler, subprotocol) - } - - def handleFrames(handlerFlow: Graph[FlowShape[FrameEvent, FrameEvent], Any], subprotocol: Option[String]): HttpResponse = - handle(Left(handlerFlow), subprotocol) - - override def handleMessages(handlerFlow: Graph[FlowShape[Message, Message], Any], subprotocol: Option[String] = None): HttpResponse = - handle(Right(handlerFlow), subprotocol) - } - OptionVal.Some(header) - } else OptionVal.None - } else OptionVal.None - } - - /* - From: http://tools.ietf.org/html/rfc6455#section-4.2.2 - - 1. A Status-Line with a 101 response code as per RFC 2616 - [RFC2616]. Such a response could look like "HTTP/1.1 101 - Switching Protocols". - - 2. An |Upgrade| header field with value "websocket" as per RFC - 2616 [RFC2616]. - - 3. A |Connection| header field with value "Upgrade". - - 4. A |Sec-WebSocket-Accept| header field. The value of this - header field is constructed by concatenating /key/, defined - above in step 4 in Section 4.2.2, with the string "258EAFA5- - E914-47DA-95CA-C5AB0DC85B11", taking the SHA-1 hash of this - concatenated value to obtain a 20-byte value and base64- - encoding (see Section 4 of [RFC4648]) this 20-byte hash. - */ - def buildResponse(key: `Sec-WebSocket-Key`, handler: Either[Graph[FlowShape[FrameEvent, FrameEvent], Any], Graph[FlowShape[Message, Message], Any]], subprotocol: Option[String]): HttpResponse = - HttpResponse( - StatusCodes.SwitchingProtocols, - subprotocol.map(p ⇒ `Sec-WebSocket-Protocol`(Seq(p))).toList ::: - List( - UpgradeHeader, - ConnectionUpgradeHeader, - `Sec-WebSocket-Accept`.forKey(key), - UpgradeToWebSocketResponseHeader(handler))) - } - - object Client { - case class NegotiatedWebSocketSettings(subprotocol: Option[String]) - - /** - * Builds a WebSocket handshake request. - */ - def buildRequest(uri: Uri, extraHeaders: immutable.Seq[HttpHeader], subprotocols: Seq[String], random: Random): (HttpRequest, `Sec-WebSocket-Key`) = { - val keyBytes = new Array[Byte](16) - random.nextBytes(keyBytes) - val key = `Sec-WebSocket-Key`(keyBytes) - val protocol = - if (subprotocols.nonEmpty) `Sec-WebSocket-Protocol`(subprotocols) :: Nil - else Nil - //version, protocol, extensions, origin - - val headers = Seq( - UpgradeHeader, - ConnectionUpgradeHeader, - key, - SecWebSocketVersionHeader) ++ protocol ++ extraHeaders - - (HttpRequest(HttpMethods.GET, uri.toRelative, headers), key) - } - - /** - * Tries to validate the HTTP response. Returns either Right(settings) or an error message if - * the response cannot be validated. - */ - def validateResponse(response: HttpResponse, subprotocols: Seq[String], key: `Sec-WebSocket-Key`): Either[String, NegotiatedWebSocketSettings] = { - /* - From http://tools.ietf.org/html/rfc6455#section-4.1 - - 1. If the status code received from the server is not 101, the - client handles the response per HTTP [RFC2616] procedures. In - particular, the client might perform authentication if it - receives a 401 status code; the server might redirect the client - using a 3xx status code (but clients are not required to follow - them), etc. Otherwise, proceed as follows. - - 2. If the response lacks an |Upgrade| header field or the |Upgrade| - header field contains a value that is not an ASCII case- - insensitive match for the value "websocket", the client MUST - _Fail the WebSocket Connection_. - - 3. If the response lacks a |Connection| header field or the - |Connection| header field doesn't contain a token that is an - ASCII case-insensitive match for the value "Upgrade", the client - MUST _Fail the WebSocket Connection_. - - 4. If the response lacks a |Sec-WebSocket-Accept| header field or - the |Sec-WebSocket-Accept| contains a value other than the - base64-encoded SHA-1 of the concatenation of the |Sec-WebSocket- - Key| (as a string, not base64-decoded) with the string "258EAFA5- - E914-47DA-95CA-C5AB0DC85B11" but ignoring any leading and - trailing whitespace, the client MUST _Fail the WebSocket - Connection_. - - 5. If the response includes a |Sec-WebSocket-Extensions| header - field and this header field indicates the use of an extension - that was not present in the client's handshake (the server has - indicated an extension not requested by the client), the client - MUST _Fail the WebSocket Connection_. (The parsing of this - header field to determine which extensions are requested is - discussed in Section 9.1.) - - 6. If the response includes a |Sec-WebSocket-Protocol| header field - and this header field indicates the use of a subprotocol that was - not present in the client's handshake (the server has indicated a - subprotocol not requested by the client), the client MUST _Fail - the WebSocket Connection_. - */ - - trait Expectation extends (HttpResponse ⇒ Option[String]) { outer ⇒ - def &&(other: HttpResponse ⇒ Option[String]): Expectation = - new Expectation { - def apply(v1: HttpResponse): Option[String] = - outer(v1).orElse(other(v1)) - } - } - - def check[T](value: HttpResponse ⇒ T)(condition: T ⇒ Boolean, msg: T ⇒ String): Expectation = - new Expectation { - def apply(resp: HttpResponse): Option[String] = { - val v = value(resp) - if (condition(v)) None - else Some(msg(v)) - } - } - - def compare(candidate: HttpHeader, caseInsensitive: Boolean): Option[HttpHeader] ⇒ Boolean = { - case Some(`candidate`) if !caseInsensitive ⇒ true - case Some(header) if caseInsensitive && candidate.value.toRootLowerCase == header.value.toRootLowerCase ⇒ true - case _ ⇒ false - } - - def headerExists(candidate: HttpHeader, showExactOther: Boolean = true, caseInsensitive: Boolean = false): Expectation = - check(_.headers.find(_.name == candidate.name))(compare(candidate, caseInsensitive), { - case Some(other) if showExactOther ⇒ s"response that was missing required `$candidate` header. Found `$other` with the wrong value." - case Some(_) ⇒ s"response with invalid `${candidate.name}` header." - case None ⇒ s"response that was missing required `${candidate.name}` header." - }) - - val expectations: Expectation = - check(_.status)(_ == StatusCodes.SwitchingProtocols, "unexpected status code: " + _) && - headerExists(UpgradeHeader, caseInsensitive = true) && - headerExists(ConnectionUpgradeHeader, caseInsensitive = true) && - headerExists(`Sec-WebSocket-Accept`.forKey(key), showExactOther = false) - - expectations(response) match { - case None ⇒ - val subs = response.header[`Sec-WebSocket-Protocol`].flatMap(_.protocols.headOption) - - if (subprotocols.isEmpty && subs.isEmpty) Right(NegotiatedWebSocketSettings(None)) // no specific one selected - else if (subs.nonEmpty && subprotocols.contains(subs.get)) Right(NegotiatedWebSocketSettings(Some(subs.get))) - else Left(s"response that indicated that the given subprotocol was not supported. (client supported: ${subprotocols.mkString(", ")}, server supported: $subs)") - case Some(problem) ⇒ Left(problem) - } - } - } - - val UpgradeHeader = Upgrade(List(UpgradeProtocol("websocket"))) - val ConnectionUpgradeHeader = Connection(List("upgrade")) - val SecWebSocketVersionHeader = `Sec-WebSocket-Version`(Seq(CurrentWebSocketVersion)) -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/Masking.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/ws/Masking.scala deleted file mode 100644 index 095b6f14e1..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/Masking.scala +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.ws - -import java.util.Random - -import akka.NotUsed -import akka.stream.{ Attributes, Outlet, Inlet, FlowShape } -import akka.stream.scaladsl.{ Keep, BidiFlow, Flow } -import akka.stream.stage._ - -/** - * Implements WebSocket Frame masking. - * - * INTERNAL API - */ -private[http] object Masking { - def apply(serverSide: Boolean, maskRandom: () ⇒ Random): BidiFlow[ /* net in */ FrameEvent, /* app out */ FrameEventOrError, /* app in */ FrameEvent, /* net out */ FrameEvent, NotUsed] = - BidiFlow.fromFlowsMat(unmaskIf(serverSide), maskIf(!serverSide, maskRandom))(Keep.none) - - def maskIf(condition: Boolean, maskRandom: () ⇒ Random): Flow[FrameEvent, FrameEvent, NotUsed] = - if (condition) { - Flow[FrameEvent] - .via(new Masking(maskRandom())) // new random per materialization - .map { - case f: FrameEvent ⇒ f - case FrameError(ex) ⇒ throw ex - } - } else Flow[FrameEvent] - - def unmaskIf(condition: Boolean): Flow[FrameEvent, FrameEventOrError, NotUsed] = - if (condition) Flow[FrameEvent].via(Unmasking) - else Flow[FrameEvent] - - private final class Masking(random: Random) extends Masker { - def extractMask(header: FrameHeader): Int = random.nextInt() - def setNewMask(header: FrameHeader, mask: Int): FrameHeader = { - if (header.mask.isDefined) throw new ProtocolException("Frame mustn't already be masked") - header.copy(mask = Some(mask)) - } - override def toString: String = s"Masking($random)" - } - - private object Unmasking extends Masker { - def extractMask(header: FrameHeader): Int = header.mask match { - case Some(mask) ⇒ mask - case None ⇒ throw new ProtocolException("Frame wasn't masked") - } - def setNewMask(header: FrameHeader, mask: Int): FrameHeader = header.copy(mask = None) - override def toString: String = "Unmasking" - } - - /** Implements both masking and unmasking which is mostly symmetric (because of XOR) */ - private abstract class Masker extends GraphStage[FlowShape[FrameEvent, FrameEventOrError]] { - def extractMask(header: FrameHeader): Int - def setNewMask(header: FrameHeader, mask: Int): FrameHeader - - val in = Inlet[FrameEvent](s"${toString}-in") - val out = Outlet[FrameEventOrError](s"${toString}-out") - override val shape: FlowShape[FrameEvent, FrameEventOrError] = FlowShape(in, out) - - override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = new GraphStageLogic(shape) with OutHandler with InHandler { - - def onPush(): Unit = grab(in) match { - case start @ FrameStart(header, data) ⇒ - try { - val mask = extractMask(header) - - val (masked, newMask) = FrameEventParser.mask(data, mask) - if (!start.lastPart) { - setHandler(in, runningHandler(newMask, this)) - } - push(out, start.copy(header = setNewMask(header, mask), data = masked)) - } catch { - case p: ProtocolException ⇒ { - setHandler(in, doneHandler) - push(out, FrameError(p)) - } - } - case _: FrameData ⇒ fail(out, new IllegalStateException("unexpected FrameData (need FrameStart first)")) - } - - private def doneHandler = new InHandler { - override def onPush(): Unit = pull(in) - } - - private def runningHandler(initialMask: Int, nextState: InHandler): InHandler = new InHandler { - var mask = initialMask - - override def onPush(): Unit = { - val part = grab(in) - if (part.lastPart) { - setHandler(in, nextState) - } - - val (masked, newMask) = FrameEventParser.mask(part.data, mask) - mask = newMask - push(out, part.withData(data = masked)) - } - } - - def onPull(): Unit = pull(in) - - setHandler(in, this) - setHandler(out, this) - - } - } -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/MessageToFrameRenderer.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/ws/MessageToFrameRenderer.scala deleted file mode 100644 index 3f35812b07..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/MessageToFrameRenderer.scala +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.ws - -import akka.NotUsed -import akka.util.ByteString -import akka.stream.scaladsl.{ Source, Flow } - -import Protocol.Opcode -import akka.http.scaladsl.model.ws._ - -/** - * Renders messages to full frames. - * - * INTERNAL API - */ -private[http] object MessageToFrameRenderer { - def create(serverSide: Boolean): Flow[Message, FrameStart, NotUsed] = { - def strictFrames(opcode: Opcode, data: ByteString): Source[FrameStart, _] = - // FIXME: fragment? - Source.single(FrameEvent.fullFrame(opcode, None, data, fin = true)) - - def streamedFrames[M](opcode: Opcode, data: Source[ByteString, M]): Source[FrameStart, NotUsed] = - Source.single(FrameEvent.empty(opcode, fin = false)) ++ - data.map(FrameEvent.fullFrame(Opcode.Continuation, None, _, fin = false)) ++ - Source.single(FrameEvent.emptyLastContinuationFrame) - - Flow[Message] - .flatMapConcat { - case BinaryMessage.Strict(data) ⇒ strictFrames(Opcode.Binary, data) - case bm: BinaryMessage ⇒ streamedFrames(Opcode.Binary, bm.dataStream) - case TextMessage.Strict(text) ⇒ strictFrames(Opcode.Text, ByteString(text, "UTF-8")) - case tm: TextMessage ⇒ streamedFrames(Opcode.Text, tm.textStream.via(Utf8Encoder)) - } - } -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/Protocol.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/ws/Protocol.scala deleted file mode 100644 index 4819dcf330..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/Protocol.scala +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.ws - -/** - * Contains WebSocket protocol constants - * - * INTERNAL API - */ -private[http] object Protocol { - val FIN_MASK = 0x80 - val RSV1_MASK = 0x40 - val RSV2_MASK = 0x20 - val RSV3_MASK = 0x10 - - val FLAGS_MASK = 0xF0 - val OP_MASK = 0x0F - - val MASK_MASK = 0x80 - val LENGTH_MASK = 0x7F - - sealed trait Opcode { - def code: Byte - def isControl: Boolean - } - object Opcode { - def forCode(code: Byte): Opcode = code match { - case 0x0 ⇒ Continuation - case 0x1 ⇒ Text - case 0x2 ⇒ Binary - - case 0x8 ⇒ Close - case 0x9 ⇒ Ping - case 0xA ⇒ Pong - - case b if (b & 0xf0) == 0 ⇒ Other(code) - case _ ⇒ throw new IllegalArgumentException(f"Opcode must be 4bit long but was 0x$code%02X") - } - - sealed abstract class AbstractOpcode private[Opcode] (val code: Byte) extends Opcode { - def isControl: Boolean = (code & 0x8) != 0 - } - - case object Continuation extends AbstractOpcode(0x0) - case object Text extends AbstractOpcode(0x1) - case object Binary extends AbstractOpcode(0x2) - - case object Close extends AbstractOpcode(0x8) - case object Ping extends AbstractOpcode(0x9) - case object Pong extends AbstractOpcode(0xA) - - case class Other(override val code: Byte) extends AbstractOpcode(code) - } - - /** - * Close status codes as defined at http://tools.ietf.org/html/rfc6455#section-7.4.1 - */ - object CloseCodes { - def isError(code: Int): Boolean = !(code == Regular || code == GoingAway) - def isValid(code: Int): Boolean = - ((code >= 1000) && (code <= 1003)) || - (code >= 1007) && (code <= 1011) || - (code >= 3000) && (code <= 4999) - - val Regular = 1000 - val GoingAway = 1001 - val ProtocolError = 1002 - val Unacceptable = 1003 - // Reserved = 1004 - // NoCodePresent = 1005 - val ConnectionAbort = 1006 - val InconsistentData = 1007 - val PolicyViolated = 1008 - val TooBig = 1009 - val ClientRejectsExtension = 1010 - val UnexpectedCondition = 1011 - val TLSHandshakeFailure = 1015 - } -} - -/** INTERNAL API */ -private[http] case class ProtocolException(cause: String) extends RuntimeException(cause) \ No newline at end of file diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/Randoms.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/ws/Randoms.scala deleted file mode 100644 index dd33527b80..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/Randoms.scala +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.ws - -import java.security.SecureRandom -import java.util.Random - -object Randoms { - /** A factory that creates SecureRandom instances */ - private[http] case object SecureRandomInstances extends (() ⇒ Random) { - override def apply(): Random = new SecureRandom() - } -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/UpgradeToWebSocketLowLevel.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/ws/UpgradeToWebSocketLowLevel.scala deleted file mode 100644 index 3c34096045..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/UpgradeToWebSocketLowLevel.scala +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.ws - -import akka.http.scaladsl.model.HttpResponse -import akka.http.scaladsl.model.ws.UpgradeToWebSocket -import akka.stream.{ Graph, FlowShape } - -/** - * Currently internal API to handle FrameEvents directly. - * - * INTERNAL API - */ -private[http] abstract class UpgradeToWebSocketLowLevel extends InternalCustomHeader("UpgradeToWebSocket") with UpgradeToWebSocket { - /** - * The low-level interface to create WebSocket server based on "frames". - * The user needs to handle control frames manually in this case. - * - * Returns a response to return in a request handler that will signal the - * low-level HTTP implementation to upgrade the connection to WebSocket and - * use the supplied handler to handle incoming WebSocket frames. - * - * INTERNAL API (for now) - */ - private[http] def handleFrames(handlerFlow: Graph[FlowShape[FrameEvent, FrameEvent], Any], subprotocol: Option[String] = None): HttpResponse -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/UpgradeToWebSocketsResponseHeader.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/ws/UpgradeToWebSocketsResponseHeader.scala deleted file mode 100644 index 772763fdf5..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/UpgradeToWebSocketsResponseHeader.scala +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.ws - -import akka.http.scaladsl.model.headers.CustomHeader -import akka.http.scaladsl.model.ws.Message -import akka.stream.{ Graph, FlowShape } - -private[http] final case class UpgradeToWebSocketResponseHeader(handler: Either[Graph[FlowShape[FrameEvent, FrameEvent], Any], Graph[FlowShape[Message, Message], Any]]) - extends InternalCustomHeader("UpgradeToWebSocketResponseHeader") - -private[http] abstract class InternalCustomHeader(val name: String) extends CustomHeader { - final def renderInRequests = false - final def renderInResponses = false - def value: String = "" -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/Utf8Decoder.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/ws/Utf8Decoder.scala deleted file mode 100644 index ec84adbf8c..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/Utf8Decoder.scala +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.ws - -import akka.util.ByteString - -import scala.util.Try - -/** - * A Utf8 -> Utf16 (= Java char) decoder. - * - * This decoder is based on the one of Bjoern Hoehrmann from - * - * http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ - * - * which is licensed under this license: - * - * Copyright (C) 2008-2016 Bjoern Hoehrmann - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * INTERNAL API - */ -private[http] object Utf8Decoder extends StreamingCharsetDecoder { - private[this] val Utf8Accept = 0 - private[this] val Utf8Reject = 12 - - val characterClasses = - Array[Byte]( - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 11, 6, 6, 6, 5, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8) - - val states = - Array[Byte]( - 0, 12, 24, 36, 60, 96, 84, 12, 12, 12, 48, 72, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, - 12, 0, 12, 12, 12, 12, 12, 0, 12, 0, 12, 12, 12, 24, 12, 12, 12, 12, 12, 24, 12, 24, 12, 12, - 12, 12, 12, 12, 12, 12, 12, 24, 12, 12, 12, 12, 12, 24, 12, 12, 12, 12, 12, 12, 12, 24, 12, 12, - 12, 12, 12, 12, 12, 12, 12, 36, 12, 36, 12, 12, 12, 36, 12, 12, 12, 12, 12, 36, 12, 36, 12, 12, - 12, 36, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12) - - def create(): StreamingCharsetDecoderInstance = - new StreamingCharsetDecoderInstance { - var currentCodePoint = 0 - var currentState = Utf8Accept - - def decode(bytes: ByteString, endOfInput: Boolean): Try[String] = Try { - val result = new StringBuilder(bytes.size) - val length = bytes.size - - def step(byte: Int): Unit = { - val chClass = characterClasses(byte) - currentCodePoint = - if (currentState == Utf8Accept) // first byte - (0xff >> chClass) & byte // take as much bits as the characterClass says - else // continuation byte - (0x3f & byte) | (currentCodePoint << 6) // take 6 bits - currentState = states(currentState + chClass) - - currentState match { - case Utf8Accept ⇒ - if (currentCodePoint <= 0xffff) - // fits in single UTF-16 char - result.append(currentCodePoint.toChar) - else { - // create surrogate pair - result.append((0xD7C0 + (currentCodePoint >> 10)).toChar) - result.append((0xDC00 + (currentCodePoint & 0x3FF)).toChar) - } - case Utf8Reject ⇒ fail("Invalid UTF-8 input") - case _ ⇒ // valid intermediate state, need more input - } - } - - var offset = 0 - while (offset < length) { - step(bytes(offset) & 0xff) - offset += 1 - } - - if (endOfInput && currentState != Utf8Accept) fail("Truncated UTF-8 input") - else - result.toString() - } - - def fail(msg: String): Nothing = throw new IllegalArgumentException(msg) - } -} - -private[http] trait StreamingCharsetDecoder { - def create(): StreamingCharsetDecoderInstance - def decode(bytes: ByteString): Try[String] = create().decode(bytes, endOfInput = true) -} -private[http] trait StreamingCharsetDecoderInstance { - def decode(bytes: ByteString, endOfInput: Boolean): Try[String] -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/Utf8Encoder.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/ws/Utf8Encoder.scala deleted file mode 100644 index d7b7ec3270..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/Utf8Encoder.scala +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.ws - -import akka.stream.{ Outlet, Inlet, FlowShape, Attributes } -import akka.stream.stage._ -import akka.util.{ ByteStringBuilder, ByteString } - -/** - * A utf16 (= Java char) to utf8 encoder. - * - * INTERNAL API - */ -private[http] object Utf8Encoder extends GraphStage[FlowShape[String, ByteString]] { - val SurrogateFirst = 0xd800 - val SurrogateSecond = 0xdc00 - - val Utf8OneByteLimit = lowerNBitsSet(7) - val Utf8TwoByteLimit = lowerNBitsSet(11) - val Utf8ThreeByteLimit = lowerNBitsSet(16) - - def lowerNBitsSet(n: Int): Long = (1L << n) - 1 - - val in = Inlet[String]("Utf8Encoder.in") - val out = Outlet[ByteString]("Utf8Encoder.out") - override val shape = FlowShape(in, out) - override val initialAttributes: Attributes = Attributes.name("utf8Encoder") - - override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = new GraphStageLogic(shape) with InHandler with OutHandler { - var surrogateValue: Int = 0 - def inSurrogatePair: Boolean = surrogateValue != 0 - - override def onPush(): Unit = { - val builder = new ByteStringBuilder - - def b(v: Int): Unit = { - builder += v.toByte - } - - def step(char: Int): Unit = - if (!inSurrogatePair) - if (char <= Utf8OneByteLimit) builder += char.toByte - else if (char <= Utf8TwoByteLimit) { - b(0xc0 | ((char & 0x7c0) >> 6)) // upper 5 bits - b(0x80 | (char & 0x3f)) // lower 6 bits - } else if (char >= SurrogateFirst && char < SurrogateSecond) - surrogateValue = 0x10000 | ((char ^ SurrogateFirst) << 10) - else if (char >= SurrogateSecond && char < 0xdfff) - throw new IllegalArgumentException(f"Unexpected UTF-16 surrogate continuation") - else if (char <= Utf8ThreeByteLimit) { - b(0xe0 | ((char & 0xf000) >> 12)) // upper 4 bits - b(0x80 | ((char & 0x0fc0) >> 6)) // middle 6 bits - b(0x80 | (char & 0x3f)) // lower 6 bits - } else - throw new IllegalStateException("Char cannot be >= 2^16") // char value was converted from 16bit value - else if (char >= SurrogateSecond && char <= 0xdfff) { - surrogateValue |= (char & 0x3ff) - b(0xf0 | ((surrogateValue & 0x1c0000) >> 18)) // upper 3 bits - b(0x80 | ((surrogateValue & 0x3f000) >> 12)) // first middle 6 bits - b(0x80 | ((surrogateValue & 0x0fc0) >> 6)) // second middle 6 bits - b(0x80 | (surrogateValue & 0x3f)) // lower 6 bits - surrogateValue = 0 - } else throw new IllegalArgumentException(f"Expected UTF-16 surrogate continuation") - - var offset = 0 - val input = grab(in) - while (offset < input.length) { - step(input(offset)) - offset += 1 - } - - if (builder.length > 0) push(out, builder.result()) - else pull(in) - } - - override def onUpstreamFinish(): Unit = - if (inSurrogatePair) failStage(new IllegalArgumentException("Truncated String input (ends in the middle of surrogate pair)")) - else completeStage() - - override def onPull(): Unit = pull(in) - - setHandlers(in, out, this) - } - - override def toString: String = "Utf8Encoder" -} \ No newline at end of file diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/WebSocket.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/ws/WebSocket.scala deleted file mode 100644 index 1abe009a30..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/WebSocket.scala +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.ws - -import java.util.Random -import akka.NotUsed -import akka.event.LoggingAdapter -import akka.util.ByteString -import scala.concurrent.duration._ -import akka.stream._ -import akka.stream.scaladsl._ -import akka.stream.stage._ -import akka.http.scaladsl.model.ws._ -import akka.stream.impl.fusing.SubSource - -/** - * INTERNAL API - * - * Defines components of the websocket stack. - */ -private[http] object WebSocket { - import FrameHandler._ - - /** - * A stack of all the higher WS layers between raw frames and the user API. - */ - def stack( - serverSide: Boolean, - maskingRandomFactory: () ⇒ Random, - closeTimeout: FiniteDuration = 3.seconds, - log: LoggingAdapter): BidiFlow[FrameEvent, Message, Message, FrameEvent, NotUsed] = - masking(serverSide, maskingRandomFactory) atop - frameHandling(serverSide, closeTimeout, log) atop - messageAPI(serverSide, closeTimeout) - - /** The lowest layer that implements the binary protocol */ - def framing: BidiFlow[ByteString, FrameEvent, FrameEvent, ByteString, NotUsed] = - BidiFlow.fromFlows( - Flow[ByteString].via(FrameEventParser), - Flow[FrameEvent].transform(() ⇒ new FrameEventRenderer)) - .named("ws-framing") - - /** The layer that handles masking using the rules defined in the specification */ - def masking(serverSide: Boolean, maskingRandomFactory: () ⇒ Random): BidiFlow[FrameEvent, FrameEventOrError, FrameEvent, FrameEvent, NotUsed] = - Masking(serverSide, maskingRandomFactory) - .named("ws-masking") - - /** - * The layer that implements all low-level frame handling, like handling control frames, collecting messages - * from frames, decoding text messages, close handling, etc. - */ - def frameHandling( - serverSide: Boolean = true, - closeTimeout: FiniteDuration, - log: LoggingAdapter): BidiFlow[FrameEventOrError, FrameHandler.Output, FrameOutHandler.Input, FrameStart, NotUsed] = - BidiFlow.fromFlows( - FrameHandler.create(server = serverSide), - FrameOutHandler.create(serverSide, closeTimeout, log)) - .named("ws-frame-handling") - - /* Completes this branch of the flow if no more messages are expected and converts close codes into errors */ - private object PrepareForUserHandler extends GraphStage[FlowShape[MessagePart, MessagePart]] { - val in = Inlet[MessagePart]("prepareForUserHandler.in") - val out = Outlet[MessagePart]("prepareForUserHandler.out") - override val shape = FlowShape(in, out) - override def initialAttributes: Attributes = Attributes.name("PrepareForUserHandler") - - override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = new GraphStageLogic(shape) with InHandler with OutHandler { - var inMessage = false - override def onPush(): Unit = grab(in) match { - case PeerClosed(code, reason) ⇒ - if (code.exists(Protocol.CloseCodes.isError)) failStage(new PeerClosedConnectionException(code.get, reason)) - else if (inMessage) failStage(new ProtocolException(s"Truncated message, peer closed connection in the middle of message.")) - else completeStage() - case ActivelyCloseWithCode(code, reason) ⇒ - if (code.exists(Protocol.CloseCodes.isError)) failStage(new ProtocolException(s"Closing connection with error code $code")) - else failStage(new IllegalStateException("Regular close from FrameHandler is unexpected")) - case x: MessageDataPart ⇒ - inMessage = !x.last - push(out, x) - case x ⇒ push(out, x) - } - override def onPull(): Unit = pull(in) - setHandlers(in, out, this) - } - } - - /** - * The layer that provides the high-level user facing API on top of frame handling. - */ - def messageAPI( - serverSide: Boolean, - closeTimeout: FiniteDuration): BidiFlow[FrameHandler.Output, Message, Message, FrameOutHandler.Input, NotUsed] = { - /* Collects user-level API messages from MessageDataParts */ - val collectMessage: Flow[MessageDataPart, Message, NotUsed] = - Flow[MessageDataPart] - .prefixAndTail(1) - .mapConcat { - // happens if we get a MessageEnd first which creates a new substream but which is then - // filtered out by collect in `prepareMessages` below - case (Nil, _) ⇒ Nil - case (first +: Nil, remaining) ⇒ (first match { - case TextMessagePart(text, true) ⇒ - SubSource.kill(remaining) - TextMessage.Strict(text) - case first @ TextMessagePart(text, false) ⇒ - TextMessage( - (Source.single(first) ++ remaining) - .collect { - case t: TextMessagePart if t.data.nonEmpty ⇒ t.data - }) - case BinaryMessagePart(data, true) ⇒ - SubSource.kill(remaining) - BinaryMessage.Strict(data) - case first @ BinaryMessagePart(data, false) ⇒ - BinaryMessage( - (Source.single(first) ++ remaining) - .collect { - case t: BinaryMessagePart if t.data.nonEmpty ⇒ t.data - }) - }) :: Nil - } - - def prepareMessages: Flow[MessagePart, Message, NotUsed] = - Flow[MessagePart] - .via(PrepareForUserHandler) - .splitWhen(_.isMessageEnd) // FIXME using splitAfter from #16885 would simplify protocol a lot - .collect { - case m: MessageDataPart ⇒ m - } - .via(collectMessage) - .concatSubstreams - .named("ws-prepare-messages") - - def renderMessages: Flow[Message, FrameStart, NotUsed] = - MessageToFrameRenderer.create(serverSide) - .named("ws-render-messages") - - BidiFlow.fromGraph(GraphDSL.create() { implicit b ⇒ - import GraphDSL.Implicits._ - - val split = b.add(BypassRouter) - val tick = Source.tick(closeTimeout, closeTimeout, Tick) - val merge = b.add(BypassMerge) - val messagePreparation = b.add(prepareMessages) - val messageRendering = b.add(renderMessages.via(LiftCompletions)) - - // user handler - split.out1 ~> messagePreparation - messageRendering.outlet ~> merge.in1 - - // bypass - split.out0 ~> merge.in0 - - // timeout support - tick ~> merge.in2 - - BidiShape( - split.in, - messagePreparation.out, - messageRendering.in, - merge.out) - }.named("ws-message-api")) - } - - private case object BypassRouter extends GraphStage[FanOutShape2[Output, BypassEvent, MessagePart]] { - private val in = Inlet[Output]("in") - private val bypass = Outlet[BypassEvent]("bypass-out") - private val user = Outlet[MessagePart]("message-out") - - override def initialAttributes = Attributes.name("BypassRouter") - - val shape = new FanOutShape2(in, bypass, user) - - def createLogic(effectiveAttributes: Attributes) = new GraphStageLogic(shape) { - - setHandler(in, new InHandler { - override def onPush(): Unit = { - grab(in) match { - case b: BypassEvent with MessagePart ⇒ emit(bypass, b, () ⇒ emit(user, b, pullIn)) - case b: BypassEvent ⇒ emit(bypass, b, pullIn) - case m: MessagePart ⇒ emit(user, m, pullIn) - } - } - }) - val pullIn = () ⇒ tryPull(in) - - setHandler(bypass, eagerTerminateOutput) - setHandler(user, ignoreTerminateOutput) - - override def preStart(): Unit = { - pullIn() - } - } - } - - private case object BypassMerge extends GraphStage[FanInShape3[BypassEvent, AnyRef, Tick.type, AnyRef]] { - private val bypass = Inlet[BypassEvent]("bypass-in") - private val user = Inlet[AnyRef]("message-in") - private val tick = Inlet[Tick.type]("tick-in") - private val out = Outlet[AnyRef]("out") - - override def initialAttributes = Attributes.name("BypassMerge") - - val shape = new FanInShape3(bypass, user, tick, out) - - def createLogic(effectiveAttributes: Attributes) = new GraphStageLogic(shape) { - - class PassAlong[T <: AnyRef](from: Inlet[T]) extends InHandler with (() ⇒ Unit) { - override def apply(): Unit = tryPull(from) - override def onPush(): Unit = emit(out, grab(from), this) - override def onUpstreamFinish(): Unit = - if (isClosed(bypass) && isClosed(user)) completeStage() - } - setHandler(bypass, new PassAlong(bypass)) - setHandler(user, new PassAlong(user)) - passAlong(tick, out, doFinish = false, doFail = false) - - setHandler(out, eagerTerminateOutput) - - override def preStart(): Unit = { - pull(bypass) - pull(user) - pull(tick) - } - } - } - - private case object LiftCompletions extends GraphStage[FlowShape[FrameStart, AnyRef]] { - private val in = Inlet[FrameStart]("in") - private val out = Outlet[AnyRef]("out") - - override def initialAttributes = Attributes.name("LiftCompletions") - - val shape = new FlowShape(in, out) - - def createLogic(effectiveAttributes: Attributes) = new GraphStageLogic(shape) { - setHandler(out, new OutHandler { - override def onPull(): Unit = pull(in) - }) - setHandler(in, new InHandler { - override def onPush(): Unit = push(out, grab(in)) - override def onUpstreamFinish(): Unit = emit(out, UserHandlerCompleted, () ⇒ completeStage()) - override def onUpstreamFailure(ex: Throwable): Unit = emit(out, UserHandlerErredOut(ex), () ⇒ completeStage()) - }) - } - } - - case object Tick -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/WebSocketClientBlueprint.scala b/akka-http-core/src/main/scala/akka/http/impl/engine/ws/WebSocketClientBlueprint.scala deleted file mode 100644 index 7cdfda7fc1..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/engine/ws/WebSocketClientBlueprint.scala +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.ws - -import akka.NotUsed -import akka.http.scaladsl.model.ws._ - -import scala.concurrent.{ Future, Promise } -import akka.util.ByteString -import akka.event.LoggingAdapter -import akka.stream.stage._ -import akka.stream._ -import akka.stream.TLSProtocol._ -import akka.stream.scaladsl._ -import akka.http.scaladsl.settings.ClientConnectionSettings -import akka.http.scaladsl.Http -import akka.http.scaladsl.model.{ HttpMethods, HttpResponse } -import akka.http.scaladsl.model.headers.Host -import akka.http.impl.engine.parsing.HttpMessageParser.StateResult -import akka.http.impl.engine.parsing.ParserOutput.{ NeedMoreData, RemainingBytes, ResponseStart } -import akka.http.impl.engine.parsing.{ HttpHeaderParser, HttpResponseParser, ParserOutput } -import akka.http.impl.engine.rendering.{ HttpRequestRendererFactory, RequestRenderingContext } -import akka.http.impl.engine.ws.Handshake.Client.NegotiatedWebSocketSettings -import akka.http.impl.util.StreamUtils -import akka.stream.impl.fusing.GraphStages.SimpleLinearGraphStage - -object WebSocketClientBlueprint { - /** - * Returns a WebSocketClientLayer that can be materialized once. - */ - def apply( - request: WebSocketRequest, - settings: ClientConnectionSettings, - log: LoggingAdapter): Http.WebSocketClientLayer = - (simpleTls.atopMat(handshake(request, settings, log))(Keep.right) atop - WebSocket.framing atop - WebSocket.stack(serverSide = false, maskingRandomFactory = settings.websocketRandomFactory, log = log)).reversed - - /** - * A bidi flow that injects and inspects the WS handshake and then goes out of the way. This BidiFlow - * can only be materialized once. - */ - def handshake( - request: WebSocketRequest, - settings: ClientConnectionSettings, - log: LoggingAdapter): BidiFlow[ByteString, ByteString, ByteString, ByteString, Future[WebSocketUpgradeResponse]] = { - import request._ - val result = Promise[WebSocketUpgradeResponse]() - - val valve = StreamUtils.OneTimeValve() - - val (initialRequest, key) = Handshake.Client.buildRequest(uri, extraHeaders, subprotocol.toList, settings.websocketRandomFactory()) - val hostHeader = Host(uri.authority.normalizedFor(uri.scheme)) - val renderedInitialRequest = - HttpRequestRendererFactory.renderStrict(RequestRenderingContext(initialRequest, hostHeader), settings, log) - - class UpgradeStage extends SimpleLinearGraphStage[ByteString] { - - override def createLogic(attributes: Attributes): GraphStageLogic = - new GraphStageLogic(shape) with InHandler with OutHandler { - // a special version of the parser which only parses one message and then reports the remaining data - // if some is available - val parser = new HttpResponseParser(settings.parserSettings, HttpHeaderParser(settings.parserSettings, log)()) { - var first = true - override def handleInformationalResponses = false - override protected def parseMessage(input: ByteString, offset: Int): StateResult = { - if (first) { - first = false - super.parseMessage(input, offset) - } else { - emit(RemainingBytes(input.drop(offset))) - terminate() - } - } - } - parser.setContextForNextResponse(HttpResponseParser.ResponseContext(HttpMethods.GET, None)) - - override def onPush(): Unit = { - parser.parseBytes(grab(in)) match { - case NeedMoreData ⇒ pull(in) - case ResponseStart(status, protocol, headers, entity, close) ⇒ - val response = HttpResponse(status, headers, protocol = protocol) - Handshake.Client.validateResponse(response, subprotocol.toList, key) match { - case Right(NegotiatedWebSocketSettings(protocol)) ⇒ - result.success(ValidUpgrade(response, protocol)) - - setHandler(in, new InHandler { - override def onPush(): Unit = push(out, grab(in)) - }) - valve.open() - - val parseResult = parser.onPull() - require(parseResult == ParserOutput.MessageEnd, s"parseResult should be MessageEnd but was $parseResult") - parser.onPull() match { - case NeedMoreData ⇒ pull(in) - case RemainingBytes(bytes) ⇒ push(out, bytes) - case other ⇒ - throw new IllegalStateException(s"unexpected element of type ${other.getClass}") - } - case Left(problem) ⇒ - result.success(InvalidUpgradeResponse(response, s"WebSocket server at $uri returned $problem")) - failStage(new IllegalArgumentException(s"WebSocket upgrade did not finish because of '$problem'")) - } - case other ⇒ - throw new IllegalStateException(s"unexpected element of type ${other.getClass}") - } - } - - override def onPull(): Unit = pull(in) - - setHandlers(in, out, this) - - override def onUpstreamFailure(ex: Throwable): Unit = { - result.tryFailure(new RuntimeException("Connection failed.", ex)) - super.onUpstreamFailure(ex) - } - } - - override def toString = "UpgradeStage" - } - - BidiFlow.fromGraph(GraphDSL.create() { implicit b ⇒ - import GraphDSL.Implicits._ - - val networkIn = b.add(Flow[ByteString].via(new UpgradeStage)) - val wsIn = b.add(Flow[ByteString]) - - val handshakeRequestSource = b.add(Source.single(renderedInitialRequest) ++ valve.source) - val httpRequestBytesAndThenWSBytes = b.add(Concat[ByteString]()) - - handshakeRequestSource ~> httpRequestBytesAndThenWSBytes - wsIn.outlet ~> httpRequestBytesAndThenWSBytes - - BidiShape( - networkIn.in, - networkIn.out, - wsIn.in, - httpRequestBytesAndThenWSBytes.out) - }) mapMaterializedValue (_ ⇒ result.future) - } - - def simpleTls: BidiFlow[SslTlsInbound, ByteString, ByteString, SendBytes, NotUsed] = - BidiFlow.fromFlowsMat( - Flow[SslTlsInbound].collect { case SessionBytes(_, bytes) ⇒ bytes }, - Flow[ByteString].map(SendBytes))(Keep.none) -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/model/JavaInitialization.scala b/akka-http-core/src/main/scala/akka/http/impl/model/JavaInitialization.scala deleted file mode 100644 index 5fc9d5a3d0..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/model/JavaInitialization.scala +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.model - -import akka.util.Unsafe - -/** - * Provides workarounds around JavaDSL initialisation edge cases. - * - * FIXME: Exists only to fix JavaDSL init problem: #19162. Remove in Akka 3.x when "ALL" static fields moved - */ -private[akka] object JavaInitialization { - - private[this] val u = Unsafe.instance - - def initializeStaticFieldWith[T](value: T, f: java.lang.reflect.Field): Unit = - u.putObject(u.staticFieldBase(f), u.staticFieldOffset(f), value) -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/model/JavaQuery.scala b/akka-http-core/src/main/scala/akka/http/impl/model/JavaQuery.scala deleted file mode 100644 index aaf5719f8a..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/model/JavaQuery.scala +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.model - -import java.util.Optional -import java.{ util ⇒ ju } -import akka.http.impl.model.parser.CharacterClasses -import akka.http.impl.util.StringRendering -import akka.http.javadsl.model.HttpCharset -import akka.http.javadsl.{ model ⇒ jm } -import akka.http.scaladsl.model.UriRendering -import akka.http.scaladsl.{ model ⇒ sm } -import akka.japi.Pair -import akka.parboiled2.CharPredicate - -import scala.collection.JavaConverters._ -import akka.http.impl.util.JavaMapping.Implicits._ - -/** INTERNAL API */ -case class JavaQuery(query: sm.Uri.Query) extends jm.Query { - override def get(key: String): Optional[String] = query.get(key).asJava - override def toMap: ju.Map[String, String] = query.toMap.asJava - override def toList: ju.List[Pair[String, String]] = query.map(_.asJava).asJava - override def getOrElse(key: String, _default: String): String = query.getOrElse(key, _default) - override def toMultiMap: ju.Map[String, ju.List[String]] = query.toMultiMap.map { case (k, v) ⇒ (k, v.asJava) }.asJava - override def getAll(key: String): ju.List[String] = query.getAll(key).asJava - override def toString = query.toString - override def withParam(key: String, value: String): jm.Query = jm.Query.create(query.map(_.asJava) :+ Pair(key, value): _*) - override def render(charset: HttpCharset): String = - UriRendering.renderQuery(new StringRendering, query, charset.nioCharset, CharacterClasses.unreserved).get - override def render(charset: HttpCharset, keep: CharPredicate): String = render(charset, keep) -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/model/JavaUri.scala b/akka-http-core/src/main/scala/akka/http/impl/model/JavaUri.scala deleted file mode 100644 index 80f53411be..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/model/JavaUri.scala +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.model - -import java.nio.charset.Charset -import java.util.Optional -import java.{ lang ⇒ jl } -import akka.http.scaladsl.model.Uri.ParsingMode -import akka.http.javadsl.{ model ⇒ jm } -import akka.http.scaladsl.{ model ⇒ sm } -import akka.http.impl.util.JavaMapping.Implicits._ - -/** INTERNAL API */ -case class JavaUri(uri: sm.Uri) extends jm.Uri { - def isRelative: Boolean = uri.isRelative - def isAbsolute: Boolean = uri.isAbsolute - def isEmpty: Boolean = uri.isEmpty - - def scheme(): String = uri.scheme - def host(): jm.Host = uri.authority.host - def port(): Int = uri.effectivePort - def userInfo(): String = uri.authority.userinfo - - def path(): String = uri.path.toString - - def pathSegments(): jl.Iterable[String] = { - import sm.Uri.Path._ - def gatherSegments(path: sm.Uri.Path): List[String] = path match { - case Empty ⇒ Nil - case Segment(head, tail) ⇒ head :: gatherSegments(tail) - case Slash(tail) ⇒ gatherSegments(tail) - } - import collection.JavaConverters._ - gatherSegments(uri.path).asJava - } - - def rawQueryString: Optional[String] = uri.rawQueryString.asJava - def queryString(charset: Charset): Optional[String] = uri.queryString(charset).asJava - def query: jm.Query = uri.query().asJava - def query(charset: Charset, mode: ParsingMode): jm.Query = uri.query(charset, mode).asJava - - def fragment: Optional[String] = uri.fragment.asJava - - // Modification methods - - def t(f: sm.Uri ⇒ sm.Uri): jm.Uri = JavaUri(f(uri)) - - def scheme(scheme: String): jm.Uri = t(_.withScheme(scheme)) - - def host(host: jm.Host): jm.Uri = t(_.withHost(host.asScala)) - def host(host: String): jm.Uri = t(_.withHost(host)) - def port(port: Int): jm.Uri = t(_.withPort(port)) - def userInfo(userInfo: String): jm.Uri = t(_.withUserInfo(userInfo)) - - def path(path: String): jm.Uri = t(_.withPath(sm.Uri.Path(path))) - - def toRelative: jm.Uri = t(_.toRelative) - - def rawQueryString(rawQuery: String): jm.Uri = t(_.withRawQueryString(rawQuery)) - def query(query: jm.Query): jm.Uri = t(_.withQuery(query.asScala)) - - def addPathSegment(segment: String): jm.Uri = t { u ⇒ - val newPath = - if (u.path.endsWithSlash) u.path ++ sm.Uri.Path(segment) - else u.path ++ sm.Uri.Path./(segment) - - u.withPath(newPath) - } - - def fragment(fragment: Optional[String]): jm.Uri = t(_.copy(fragment = fragment.asScala)) - def fragment(fragment: String): jm.Uri = t(_.withFragment(fragment)) - - override def toString: String = uri.toString -} \ No newline at end of file diff --git a/akka-http-core/src/main/scala/akka/http/impl/model/parser/AcceptCharsetHeader.scala b/akka-http-core/src/main/scala/akka/http/impl/model/parser/AcceptCharsetHeader.scala deleted file mode 100644 index 62a7a2a65a..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/model/parser/AcceptCharsetHeader.scala +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.model.parser - -import akka.parboiled2.Parser -import akka.http.scaladsl.model.headers.`Accept-Charset` -import akka.http.scaladsl.model.HttpCharsetRange - -private[parser] trait AcceptCharsetHeader { this: Parser with CommonRules with CommonActions ⇒ - - // http://tools.ietf.org/html/rfc7231#section-5.3.3 - def `accept-charset` = rule { - oneOrMore(`charset-range-decl`).separatedBy(listSep) ~ EOI ~> (`Accept-Charset`(_)) - } - - def `charset-range-decl` = rule { - `charset-range-def` ~ optional(weight) ~> { (range, optQ) ⇒ - optQ match { - case None ⇒ range - case Some(q) ⇒ range withQValue q - } - } - } - - def `charset-range-def` = rule { - ws('*') ~ push(HttpCharsetRange.`*`) | token ~> (s ⇒ HttpCharsetRange(getCharset(s))) - } -} \ No newline at end of file diff --git a/akka-http-core/src/main/scala/akka/http/impl/model/parser/AcceptEncodingHeader.scala b/akka-http-core/src/main/scala/akka/http/impl/model/parser/AcceptEncodingHeader.scala deleted file mode 100644 index 50748dd8b5..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/model/parser/AcceptEncodingHeader.scala +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.model.parser - -import akka.parboiled2.Parser -import akka.http.scaladsl.model.headers._ - -private[parser] trait AcceptEncodingHeader { this: Parser with CommonRules with CommonActions ⇒ - - // http://tools.ietf.org/html/rfc7231#section-5.3.4 - def `accept-encoding` = rule { - zeroOrMore(`encoding-range-decl`).separatedBy(listSep) ~ EOI ~> (`Accept-Encoding`(_)) - } - - def `encoding-range-decl` = rule { - codings ~ optional(weight) ~> { (range, optQ) ⇒ - optQ match { - case None ⇒ range - case Some(q) ⇒ range withQValue q - } - } - } - - def codings = rule { ws('*') ~ push(HttpEncodingRange.`*`) | token ~> getEncoding } - - private val getEncoding: String ⇒ HttpEncodingRange = - name ⇒ HttpEncodingRange(HttpEncodings.getForKeyCaseInsensitive(name) getOrElse HttpEncoding.custom(name)) -} \ No newline at end of file diff --git a/akka-http-core/src/main/scala/akka/http/impl/model/parser/AcceptHeader.scala b/akka-http-core/src/main/scala/akka/http/impl/model/parser/AcceptHeader.scala deleted file mode 100644 index 614d5764f0..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/model/parser/AcceptHeader.scala +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.model.parser - -import akka.parboiled2.Parser -import akka.http.scaladsl.model.headers._ -import akka.http.scaladsl.model.{ MediaRange, MediaRanges } -import akka.http.impl.util._ - -private[parser] trait AcceptHeader { this: Parser with CommonRules with CommonActions ⇒ - import CharacterClasses._ - - // http://tools.ietf.org/html/rfc7231#section-5.3.2 - def accept = rule { - zeroOrMore(`media-range-decl`).separatedBy(listSep) ~ EOI ~> (Accept(_)) - } - - def `media-range-decl` = rule { - `media-range-def` ~ OWS ~ zeroOrMore(ws(';') ~ parameter) ~> { (main, sub, params) ⇒ - if (sub == "*") { - val mainLower = main.toRootLowerCase - MediaRanges.getForKey(mainLower) match { - case Some(registered) ⇒ if (params.isEmpty) registered else registered.withParams(params.toMap) - case None ⇒ MediaRange.custom(mainLower, params.toMap) - } - } else { - val (p, q) = MediaRange.splitOffQValue(params.toMap) - MediaRange(getMediaType(main, sub, p contains "charset", p), q) - } - } - } - - def `media-range-def` = rule { - "*/*" ~ push("*") ~ push("*") | `type` ~ '/' ~ ('*' ~ !tchar ~ push("*") | subtype) | '*' ~ push("*") ~ push("*") - } -} - diff --git a/akka-http-core/src/main/scala/akka/http/impl/model/parser/AcceptLanguageHeader.scala b/akka-http-core/src/main/scala/akka/http/impl/model/parser/AcceptLanguageHeader.scala deleted file mode 100644 index 1fe396813b..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/model/parser/AcceptLanguageHeader.scala +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.model.parser - -import akka.parboiled2.Parser -import akka.http.scaladsl.model.headers._ - -private[parser] trait AcceptLanguageHeader { this: Parser with CommonRules with CommonActions ⇒ - - // http://tools.ietf.org/html/rfc7231#section-5.3.5 - def `accept-language` = rule { - oneOrMore(`language-range-decl`).separatedBy(listSep) ~ EOI ~> (`Accept-Language`(_)) - } - - def `language-range-decl` = rule { - `language-range` ~ optional(weight) ~> { (range, optQ) ⇒ - optQ match { - case None ⇒ range - case Some(q) ⇒ range withQValue q - } - } - } - - def `language-range` = rule { ws('*') ~ push(LanguageRange.`*`) | language ~> (LanguageRange(_)) } -} \ No newline at end of file diff --git a/akka-http-core/src/main/scala/akka/http/impl/model/parser/Base64Parsing.scala b/akka-http-core/src/main/scala/akka/http/impl/model/parser/Base64Parsing.scala deleted file mode 100644 index 3de083f213..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/model/parser/Base64Parsing.scala +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2009-2016 Mathias Doenitz, Alexander Myltsev - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package akka.http.impl.model.parser - -import akka.parboiled2.util.Base64 -import akka.parboiled2._ - -/** - * Rules for parsing Base-64 encoded strings. - */ -private[parser] trait Base64Parsing { this: Parser ⇒ - import Base64Parsing._ - - /** - * Parses an RFC4045-encoded string and decodes it onto the value stack. - */ - def rfc2045String: Rule1[Array[Byte]] = base64StringOrBlock(rfc2045Alphabet, rfc2045StringDecoder) - - /** - * Parses an RFC4045-encoded string potentially containing newlines and decodes it onto the value stack. - */ - def rfc2045Block: Rule1[Array[Byte]] = base64StringOrBlock(rfc2045Alphabet, rfc2045BlockDecoder) - - /** - * Parses a org.parboiled2.util.Base64.custom()-encoded string and decodes it onto the value stack. - */ - def base64CustomString: Rule1[Array[Byte]] = base64StringOrBlock(customAlphabet, customStringDecoder) - - /** - * Parses a org.parboiled2.util.Base64.custom()-encoded string potentially containing newlines - * and decodes it onto the value stack. - */ - def base64CustomBlock: Rule1[Array[Byte]] = base64StringOrBlock(customAlphabet, customBlockDecoder) - - /** - * Parses a BASE64-encoded string with the given alphabet and decodes it onto the value - * stack using the given codec. - */ - def base64StringOrBlock(alphabet: CharPredicate, decoder: Decoder): Rule1[Array[Byte]] = { - val start = cursor - rule { - oneOrMore(alphabet) ~ run { - decoder(input.sliceCharArray(start, cursor)) match { - case null ⇒ MISMATCH - case bytes ⇒ push(bytes) - } - } - } - } -} - -object Base64Parsing { - type Decoder = Array[Char] ⇒ Array[Byte] - - val rfc2045Alphabet = CharPredicate(Base64.rfc2045().getAlphabet).asMaskBased - val customAlphabet = CharPredicate(Base64.custom().getAlphabet).asMaskBased - - val rfc2045StringDecoder: Decoder = decodeString(Base64.rfc2045()) - val customStringDecoder: Decoder = decodeString(Base64.custom()) - - val rfc2045BlockDecoder: Decoder = decodeBlock(Base64.rfc2045()) - val customBlockDecoder: Decoder = decodeBlock(Base64.custom()) - - def decodeString(codec: Base64)(chars: Array[Char]): Array[Byte] = codec.decodeFast(chars) - def decodeBlock(codec: Base64)(chars: Array[Char]): Array[Byte] = codec.decode(chars) -} \ No newline at end of file diff --git a/akka-http-core/src/main/scala/akka/http/impl/model/parser/CacheControlHeader.scala b/akka-http-core/src/main/scala/akka/http/impl/model/parser/CacheControlHeader.scala deleted file mode 100644 index aef439a86a..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/model/parser/CacheControlHeader.scala +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.model.parser - -import akka.parboiled2.Parser -import akka.http.scaladsl.model.headers._ -import CacheDirectives._ - -private[parser] trait CacheControlHeader { this: Parser with CommonRules with CommonActions with StringBuilding ⇒ - - // http://tools.ietf.org/html/rfc7234#section-5.2 - def `cache-control` = rule { - oneOrMore(`cache-directive`).separatedBy(listSep) ~ EOI ~> (`Cache-Control`(_)) - } - - def `cache-directive` = rule( - "no-store" ~ push(`no-store`) - | "no-transform" ~ push(`no-transform`) - | "max-age=" ~ `delta-seconds` ~> (`max-age`(_)) - | "max-stale" ~ optional(ws('=') ~ `delta-seconds`) ~> (`max-stale`(_)) - | "min-fresh=" ~ `delta-seconds` ~> (`min-fresh`(_)) - | "only-if-cached" ~ push(`only-if-cached`) - | "public" ~ push(`public`) - | "private" ~ (ws('=') ~ `field-names` ~> (`private`(_: _*)) | push(`private`(Nil: _*))) - | "no-cache" ~ (ws('=') ~ `field-names` ~> (`no-cache`(_: _*)) | push(`no-cache`)) - | "must-revalidate" ~ push(`must-revalidate`) - | "proxy-revalidate" ~ push(`proxy-revalidate`) - | "s-maxage=" ~ `delta-seconds` ~> (`s-maxage`(_)) - | token ~ optional(ws('=') ~ word) ~> (CacheDirective.custom(_, _))) - - def `field-names` = rule { `quoted-tokens` | token ~> (Seq(_)) } - - def `quoted-tokens` = rule { '"' ~ zeroOrMore(`quoted-tokens-elem`).separatedBy(listSep) ~ '"' } - - def `quoted-tokens-elem` = rule { - clearSB() ~ zeroOrMore(!'"' ~ !',' ~ qdtext ~ appendSB() | `quoted-pair`) ~ push(sb.toString) - } -} \ No newline at end of file diff --git a/akka-http-core/src/main/scala/akka/http/impl/model/parser/CharacterClasses.scala b/akka-http-core/src/main/scala/akka/http/impl/model/parser/CharacterClasses.scala deleted file mode 100644 index 73a58199da..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/model/parser/CharacterClasses.scala +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.model.parser - -import akka.parboiled2.CharPredicate - -// efficient encoding of *7-bit* ASCII characters -private[http] object CharacterClasses { - - // http://tools.ietf.org/html/rfc7230#section-1.2 referencing - // http://tools.ietf.org/html/rfc5234#appendix-B.1 - def ALPHA = CharPredicate.Alpha - def LOWER_ALPHA = CharPredicate.LowerAlpha - def UPPER_ALPHA = CharPredicate.UpperAlpha - def CR = '\r' - val CTL = CharPredicate('\u0000' to '\u001F', '\u007F') - def DIGIT = CharPredicate.Digit - def ALPHANUM = CharPredicate.AlphaNum - def DQUOTE = '"' - def HEXDIG = CharPredicate.HexDigit - def HTAB = '\t' - def LF = '\n' - def SP = ' ' - def VCHAR = CharPredicate.Visible - val WSP = CharPredicate(SP, HTAB) - val WSPCRLF = WSP ++ CR ++ LF - - // http://tools.ietf.org/html/rfc7230#section-3.2.6 - val special = CharPredicate("""()<>@,;:\"/[]?={}""") - val tchar = VCHAR -- special // token-char - - // http://tools.ietf.org/html/rfc3986#appendix-A - val unreserved = ALPHA ++ DIGIT ++ "-._~" - val `gen-delims` = CharPredicate(":/?#[]@") - val `sub-delims` = CharPredicate("!$&'()*+,;=") - val reserved = `gen-delims` ++ `sub-delims` - - // URI FRAGMENT/QUERY and PATH characters have two classes of acceptable characters: one that strictly - // follows rfc3986, which should be used for rendering urls, and one relaxed, which accepts all visible - // 7-bit ASCII characters, even if they're not percent-encoded. - val `pchar-base-nc` = unreserved ++ `sub-delims` ++ '@' - val `pchar-base` = `pchar-base-nc` ++ ':' // pchar without percent - val `query-fragment-char` = `pchar-base` ++ "/?" - val `strict-query-char` = `query-fragment-char` -- "&=;" - val `strict-query-char-np` = `strict-query-char` -- '+' - - val `relaxed-fragment-char` = VCHAR -- '%' - val `relaxed-path-segment-char` = VCHAR -- "%/?#" - val `relaxed-query-char` = VCHAR -- "%&=#" - val `raw-query-char` = VCHAR -- '#' - val `scheme-char` = ALPHA ++ DIGIT ++ '+' ++ '-' ++ '.' - - val `userinfo-char` = unreserved ++ `sub-delims` ++ ':' - val `reg-name-char` = unreserved ++ `sub-delims` - val `lower-reg-name-char` = `reg-name-char` -- UPPER_ALPHA - - // http://tools.ietf.org/html/rfc7235#section-2.1 - val `token68-start` = ALPHA ++ DIGIT ++ "-._~+/" - - // https://tools.ietf.org/html/rfc6265#section-4.1.1 - val `cookie-octet-rfc-6265` = CharPredicate('\u0021', '\u0023' to '\u002b', '\u002d' to '\u003a', '\u003c' to '\u005b', '\u005d' to '\u007e') - val `cookie-separator` = CharPredicate(akka.parboiled2.EOI, ';') - val `cookie-octet-raw` = - CharPredicate('\u0020' to '\u007e') ++ - CharPredicate((x: Char) ⇒ x > 0x7f && java.lang.Character.isDefined(x)) -- `cookie-separator` - val `av-octet` = CharPredicate('\u0020' to '\u003a', '\u003c' to '\u007e') // http://www.rfc-editor.org/errata_search.php?rfc=6265 - - // http://tools.ietf.org/html/rfc5988#section-5 - val `reg-rel-type-octet` = LOWER_ALPHA ++ DIGIT ++ '.' ++ '-' - - // helpers - val `qdtext-base` = CharPredicate(HTAB, SP, '\u0021', '\u0023' to '\u005B', '\u005D' to '\u007E') - val `ctext-base` = CharPredicate(HTAB, SP, '\u0021' to '\u0027', '\u002A' to '\u005B', '\u005D' to '\u007E') - val `quotable-base` = CharPredicate(HTAB, SP, VCHAR) - val `etagc-base` = VCHAR -- '"' - val DIGIT04 = CharPredicate('0' to '4') - val DIGIT05 = CharPredicate('0' to '5') - def DIGIT19 = CharPredicate.Digit19 - val colonSlashEOI = CharPredicate(':', '/', akka.parboiled2.EOI) - val `date-sep` = CharPredicate("""- """) - - require(`qdtext-base`.isMaskBased) // make sure we didn't introduce any non-7bit-chars by accident which - require(`ctext-base`.isMaskBased) // would make the CharPredicate fall back to the much slower - require(`quotable-base`.isMaskBased) // ArrayBasedPredicate or GeneralCharPredicate implementations - require(CTL.isMaskBased) -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/model/parser/CommonActions.scala b/akka-http-core/src/main/scala/akka/http/impl/model/parser/CommonActions.scala deleted file mode 100644 index 8d6c939fde..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/model/parser/CommonActions.scala +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.model.parser - -import akka.http.impl.util._ -import akka.http.scaladsl.model.MediaType.Binary -import akka.http.scaladsl.model._ -import akka.stream.impl.ConstantFun - -/** INTERNAL API */ -private[parser] trait CommonActions { - - def customMediaTypes: MediaTypes.FindCustom - - type StringMapBuilder = scala.collection.mutable.Builder[(String, String), Map[String, String]] - - def getMediaType(mainType: String, subType: String, charsetDefined: Boolean, - params: Map[String, String]): MediaType = { - import MediaTypes._ - val subLower = subType.toRootLowerCase - mainType.toRootLowerCase match { - case "multipart" ⇒ subLower match { - case "mixed" ⇒ multipart.mixed(params) - case "alternative" ⇒ multipart.alternative(params) - case "related" ⇒ multipart.related(params) - case "form-data" ⇒ multipart.`form-data`(params) - case "signed" ⇒ multipart.signed(params) - case "encrypted" ⇒ multipart.encrypted(params) - case custom ⇒ MediaType.customMultipart(custom, params) - } - case mainLower ⇒ - // attempt fetching custom media type if configured - if (areCustomMediaTypesDefined) - customMediaTypes(mainLower, subType) getOrElse fallbackMediaType(subType, params, mainLower) - else MediaTypes.getForKey((mainLower, subLower)) match { - case Some(registered) ⇒ if (params.isEmpty) registered else registered.withParams(params) - case None ⇒ - if (charsetDefined) - MediaType.customWithOpenCharset(mainLower, subType, params = params, allowArbitrarySubtypes = true) - else - fallbackMediaType(subType, params, mainLower) - } - } - } - - /** Provide a generic MediaType when no known-ones matched. */ - private def fallbackMediaType(subType: String, params: Map[String, String], mainLower: String): Binary = - MediaType.customBinary(mainLower, subType, MediaType.Compressible, params = params, allowArbitrarySubtypes = true) - - def getCharset(name: String): HttpCharset = - HttpCharsets - .getForKeyCaseInsensitive(name) - .getOrElse(HttpCharset.custom(name)) - - @inline private def areCustomMediaTypesDefined: Boolean = customMediaTypes ne ConstantFun.two2none - -} \ No newline at end of file diff --git a/akka-http-core/src/main/scala/akka/http/impl/model/parser/CommonRules.scala b/akka-http-core/src/main/scala/akka/http/impl/model/parser/CommonRules.scala deleted file mode 100644 index e3b8c95f4a..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/model/parser/CommonRules.scala +++ /dev/null @@ -1,452 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.model.parser - -import scala.collection.immutable -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers._ -import akka.parboiled2._ -import akka.shapeless._ - -private[parser] trait CommonRules { this: Parser with StringBuilding ⇒ - import CharacterClasses._ - - // ****************************************************************************************** - // http://tools.ietf.org/html/rfc7230#section-1.2 referencing - // http://tools.ietf.org/html/rfc5234#appendix-B.1 - // ****************************************************************************************** - def CRLF = rule { CR ~ LF } - - def OCTET = rule { ANY } - - // ****************************************************************************************** - // http://tools.ietf.org/html/rfc7230#section-3.2.3 - // ****************************************************************************************** - - def OWS = rule { zeroOrMore(optional(CRLF) ~ oneOrMore(WSP)) } // extended with `obs-fold` - - def RWS = rule { oneOrMore(optional(CRLF) ~ oneOrMore(WSP)) } // extended with `obs-fold` - - // ****************************************************************************************** - // http://tools.ietf.org/html/rfc7230#section-3.2.6 - // ****************************************************************************************** - def word = rule { token | `quoted-string` } - - def token: Rule1[String] = rule { capture(token0) ~ OWS } - - def `quoted-string`: Rule1[String] = rule { - DQUOTE ~ clearSB() ~ zeroOrMore(qdtext ~ appendSB() | `quoted-pair`) ~ push(sb.toString) ~ DQUOTE ~ OWS - } - - def qdtext = rule { `qdtext-base` | `obs-text` } - - def `obs-text` = rule { "\u0080" - "\uFFFE" } - - def `quoted-pair` = rule { '\\' ~ (`quotable-base` | `obs-text`) ~ appendSB() } - - // builds a string via the StringBuilding StringBuilder - def comment: Rule0 = rule { - ws('(') ~ clearSB() ~ zeroOrMore(ctext | `quoted-cpair` | `nested-comment`) ~ ws(')') - } - - def `nested-comment` = { - var saved: String = null - rule { &('(') ~ run(saved = sb.toString) ~ (comment ~ prependSB(saved + " (") ~ appendSB(')') | setSB(saved) ~ test(false)) } - } - - def ctext = rule { (`ctext-base` | `obs-text`) ~ appendSB() } - - def `quoted-cpair` = `quoted-pair` - - // ****************************************************************************************** - // http://tools.ietf.org/html/rfc7234#section-5.3 - // ****************************************************************************************** - - def `expires-date`: Rule1[DateTime] = rule { - (`HTTP-date` | zeroOrMore(ANY) ~ push(DateTime.MinValue)) ~ OWS - } - - // ****************************************************************************************** - // http://tools.ietf.org/html/rfc7231#section-7.1.1.1 - // but more lenient where we have already seen differing implementations in the field - // ****************************************************************************************** - - def `HTTP-date`: Rule1[DateTime] = rule { - (`IMF-fixdate` | `asctime-date` | '0' ~ push(DateTime.MinValue)) ~ OWS - } - - def `IMF-fixdate` = rule { // mixture of the spec-ed `IMF-fixdate` and `rfc850-date` - (`day-name-l` | `day-name`) ~ ", " ~ (date1 | date2) ~ ' ' ~ `time-of-day` ~ ' ' ~ ("GMT" | "UTC") ~> { - (wkday, day, month, year, hour, min, sec) ⇒ createDateTime(year, month, day, hour, min, sec, wkday) - } - } - - def `day-name` = rule( - "Sun" ~ push(0) | "Mon" ~ push(1) | "Tue" ~ push(2) | "Wed" ~ push(3) | "Thu" ~ push(4) | "Fri" ~ push(5) | "Sat" ~ push(6)) - - def date1 = rule { day ~ `date-sep` ~ month ~ `date-sep` ~ year } - - def day = rule { digit2 } - - def month = rule( - "Jan" ~ push(1) | "Feb" ~ push(2) | "Mar" ~ push(3) | "Apr" ~ push(4) | "May" ~ push(5) | "Jun" ~ push(6) | "Jul" ~ push(7) | - "Aug" ~ push(8) | "Sep" ~ push(9) | "Oct" ~ push(10) | "Nov" ~ push(11) | "Dec" ~ push(12)) - - def year = rule { digit4 } - - def `time-of-day` = rule { hour ~ ':' ~ minute ~ ':' ~ second } - def hour = rule { digit2 } - def minute = rule { digit2 } - def second = rule { digit2 } - - // def `obs-date` = rule { `rfc850-date` | `asctime-date` } - - // def `rfc850-date` = rule { `day-name-l` ~ ", " ~ date2 ~ ' ' ~ `time-of-day` ~ " GMT" } - - // per #17714, parse two digit year to https://tools.ietf.org/html/rfc6265#section-5.1.1 - def date2 = rule { day ~ '-' ~ month ~ '-' ~ digit2 ~> (y ⇒ if (y <= 69) y + 2000 else y + 1900) } - - def `day-name-l` = rule( - "Sunday" ~ push(0) | "Monday" ~ push(1) | "Tuesday" ~ push(2) | "Wednesday" ~ push(3) | "Thursday" ~ push(4) | - "Friday" ~ push(5) | "Saturday" ~ push(6)) - - def `asctime-date` = rule { - `day-name` ~ ' ' ~ date3 ~ ' ' ~ `time-of-day` ~ ' ' ~ year ~> { - (wkday, month, day, hour, min, sec, year) ⇒ createDateTime(year, month, day, hour, min, sec, wkday) - } - } - - def date3 = rule { month ~ ' ' ~ (digit2 | ' ' ~ digit) } - - // ****************************************************************************************** - // http://tools.ietf.org/html/rfc7231#section-5.3.1 - // ****************************************************************************************** - - def weight = rule { ws(';') ~ ws('q') ~ ws('=') ~ qvalue } // a bit more lenient than the spec - - def qvalue = rule { // a bit more lenient than the spec - capture('0' ~ optional('.' ~ zeroOrMore(DIGIT)) - | '.' ~ oneOrMore(DIGIT) - | '1' ~ optional('.' ~ zeroOrMore('0'))) ~> (_.toFloat) ~ OWS - } - - // ****************************************************************************************** - // http://tools.ietf.org/html/rfc7231#section-3.1.1.1 - // ****************************************************************************************** - - def `media-type`: RuleN[String :: String :: Seq[(String, String)] :: HNil] = rule { - `type` ~ '/' ~ subtype ~ zeroOrMore(ws(';') ~ parameter) - } - - def `type` = rule { token } - - def subtype = rule { token } - - def parameter = rule { attribute ~ ws('=') ~ value ~> ((_, _)) } - - def attribute = rule { token } - - def value = rule { word } - - // ****************************************************************************************** - // http://tools.ietf.org/html/rfc4647#section-2.1 - // ****************************************************************************************** - def language = rule { - `primary-tag` ~ zeroOrMore('-' ~ `sub-tag`) ~> (Language(_, _: _*)) - } - - def `primary-tag` = rule { capture(oneOrMore(ALPHA)) ~ OWS } - - def `sub-tag` = rule { capture(oneOrMore(ALPHANUM)) ~ OWS } - - // ****************************************************************************************** - // http://tools.ietf.org/html/rfc4647#section-2.1 - // ****************************************************************************************** - - def `auth-scheme` = rule { token } - - def `auth-param` = rule { token ~ ws('=') ~ word } - - def `token68` = rule { capture(oneOrMore(`token68-start`) ~ zeroOrMore('=')) ~ OWS } - - def challenge = rule { - `challenge-or-credentials` ~> { (scheme, params) ⇒ - val (realms, otherParams) = params.partition(_._1 equalsIgnoreCase "realm") - HttpChallenge(scheme, realms.headOption.map(_._2), otherParams.toMap) - } - } - - def `challenge-or-credentials`: Rule2[String, Seq[(String, String)]] = rule { - `auth-scheme` ~ ( - oneOrMore(`auth-param` ~> (_ → _)).separatedBy(listSep) - | `token68` ~> (x ⇒ ("" → x) :: Nil) - | push(Nil)) - } - - // ****************************************************************************************** - // http://tools.ietf.org/html/rfc7234#section-1.2.1 - // ****************************************************************************************** - - def `delta-seconds` = rule { longNumberCappedAtIntMaxValue } - - // ****************************************************************************************** - // http://tools.ietf.org/html/rfc7232#section-2.3 - // ****************************************************************************************** - - def `entity-tag` = rule { - ("W/" ~ push(true) | push(false)) ~ `opaque-tag` ~> ((weak, tag) ⇒ EntityTag(tag, weak)) - } - - def `opaque-tag` = rule { '"' ~ capture(zeroOrMore(`etagc-base` | `obs-text`)) ~ '"' } - - // ****************************************************************************************** - // http://tools.ietf.org/html/rfc7235#section-2.1 - // ****************************************************************************************** - def credentials = rule { - `basic-credential-def` | `oauth2-bearer-token` | `generic-credentials` - } - - def `basic-credential-def` = rule { - ignoreCase("basic") ~ OWS ~ `basic-cookie` ~> (BasicHttpCredentials(_)) - } - - def `basic-cookie` = rule { `token68` } - - // http://tools.ietf.org/html/rfc6750#section-2.1 - def `oauth2-bearer-token` = rule { - ignoreCase("bearer") ~ OWS ~ `token68` ~> OAuth2BearerToken - } - - def `generic-credentials` = rule { - `challenge-or-credentials` ~> ((scheme, params) ⇒ GenericHttpCredentials(scheme, params.toMap)) - } - - /** - * Either `Some(cookiePair)` if the cookie pair is parsable using the giving cookie parsing mode - * or None, otherwise. - */ - def `optional-cookie-pair`: Rule1[Option[HttpCookiePair]] = rule { - (`cookie-pair` ~ &(`cookie-separator`) ~> (Some(_: HttpCookiePair))) | - // fallback that parses and discards everything until the next semicolon - (zeroOrMore(!`cookie-separator` ~ ANY) ~ &(`cookie-separator`) ~ push(None)) - } - - def `cookie-pair`: Rule1[HttpCookiePair] = rule { - `cookie-name` ~ ws('=') ~ `cookie-value` ~> (createCookiePair _) - } - - def `cookie-name` = rule { token } - - // abstract methods need to be implemented depending on actual cookie parsing mode - def `cookie-value`: Rule1[String] - def createCookiePair(name: String, value: String): HttpCookiePair - - // ****************************************************************************************** - // https://tools.ietf.org/html/rfc6265#section-4.1.1 - // ****************************************************************************************** - def `cookie-value-rfc-6265` = rule { - ('"' ~ capture(zeroOrMore(`cookie-octet-rfc-6265`)) ~ '"' | capture(zeroOrMore(`cookie-octet-rfc-6265`))) ~ OWS - } - - def `cookie-value-raw` = rule { - capture(zeroOrMore(`cookie-octet-raw`)) ~ OWS - } - - def `cookie-av` = rule { - `expires-av` | `max-age-av` | `domain-av` | `path-av` | `secure-av` | `httponly-av` | `extension-av` - } - - def `expires-av` = rule { - ignoreCase("expires=") ~ OWS ~ `expires-date` ~> { (c: HttpCookie, dt: DateTime) ⇒ c.copy(expires = Some(dt)) } - } - - def `max-age-av` = rule { - ignoreCase("max-age=") ~ OWS ~ longNumberCappedAtIntMaxValue ~> { (c: HttpCookie, seconds: Long) ⇒ c.copy(maxAge = Some(seconds)) } - } - - def `domain-av` = rule { - ignoreCase("domain=") ~ OWS ~ `domain-value` ~> { (c: HttpCookie, domainName: String) ⇒ c.copy(domain = Some(domainName)) } - } - - // https://tools.ietf.org/html/rfc1034#section-3.5 relaxed by https://tools.ietf.org/html/rfc1123#section-2 - // to also allow digits at the start of a label - def `domain-value` = rule { - optional('.') ~ capture(oneOrMore(oneOrMore(oneOrMore(ALPHANUM)).separatedBy('-')).separatedBy('.')) ~ OWS - } - - def `path-av` = rule { - ignoreCase("path=") ~ OWS ~ `path-value` ~> { (c: HttpCookie, pathValue: String) ⇒ c.copy(path = Some(pathValue)) } - } - - // http://www.rfc-editor.org/errata_search.php?rfc=6265 - def `path-value` = rule { - capture(zeroOrMore(`av-octet`)) ~ OWS - } - - def `secure-av` = rule { - ignoreCase("secure") ~ OWS ~> { (cookie: HttpCookie) ⇒ cookie.copy(secure = true) } - } - - def `httponly-av` = rule { - ignoreCase("httponly") ~ OWS ~> { (cookie: HttpCookie) ⇒ cookie.copy(httpOnly = true) } - } - - // http://www.rfc-editor.org/errata_search.php?rfc=6265 - def `extension-av` = rule { - !(ignoreCase("expires=") - | ignoreCase("max-age=") - | ignoreCase("domain=") - | ignoreCase("path=") - | ignoreCase("secure") - | ignoreCase("httponly")) ~ - capture(zeroOrMore(`av-octet`)) ~ OWS ~> { (c: HttpCookie, s: String) ⇒ c.copy(extension = Some(s)) } - } - - // ****************************************************************************************** - // http://tools.ietf.org/html/rfc6454#section-7.1 - // ****************************************************************************************** - def `origin-list-or-null` = rule { - "null" ~ OWS ~ push(immutable.Seq.empty[HttpOrigin]) | `origin-list` - } - - def `origin-list` = rule { - oneOrMore(capture(oneOrMore(VCHAR)) ~> (HttpOrigin(_))).separatedBy(SP) ~ OWS // offload to URL parser - } - - // ****************************************************************************************** - // http://tools.ietf.org/html/rfc7233#appendix-D - // ****************************************************************************************** - - def `byte-content-range` = rule { `bytes-unit` ~ (`byte-range-resp` | `unsatisfied-range`) } - - def `byte-range` = rule { - `first-byte-pos` ~ ws('-') ~ `last-byte-pos` - } - - def `byte-range-resp` = rule { - `byte-range` ~ ws('/') ~ (`complete-length` ~> (Some(_)) | ws('*') ~ push(None)) ~> (ContentRange(_, _, _)) - } - - def `byte-range-set` = rule { - zeroOrMore(ws(',')) ~ oneOrMore(`byte-range-spec` | `suffix-byte-range-spec`).separatedBy(listSep) - } - - def `byte-range-spec` = rule { - `first-byte-pos` ~ ws('-') ~ (`last-byte-pos` ~> (ByteRange(_: Long, _)) | run(ByteRange.fromOffset(_))) - } - - def `byte-ranges-specifier` = rule { `bytes-unit` ~ ws('=') ~ `byte-range-set` } - - def `bytes-unit` = rule { "bytes" ~ OWS ~ push(RangeUnits.Bytes) } - - def `complete-length` = rule { longNumberCapped } - - def `first-byte-pos` = rule { longNumberCapped } - - def `last-byte-pos` = rule { longNumberCapped } - - def `other-content-range` = rule { `other-range-unit` ~ `other-range-resp` } - - def `other-range-resp` = rule { capture(zeroOrMore(ANY)) ~> ContentRange.Other } - - def `other-range-set` = rule { oneOrMore(VCHAR) ~ OWS } - - def `other-range-unit` = rule { token ~> RangeUnits.Other } - - def `other-ranges-specifier` = rule { `other-range-unit` ~ ws('=') ~ `other-range-set` } - - def `range-unit` = rule { `bytes-unit` | `other-range-unit` } - - def `suffix-byte-range-spec` = rule { '-' ~ `suffix-length` ~> (ByteRange.suffix(_)) } - - def `suffix-length` = rule { longNumberCapped } - - def `unsatisfied-range` = rule { '*' ~ '/' ~ `complete-length` ~> (ContentRange.Unsatisfiable(_)) } - - // ****************************************************************************************** - // http://tools.ietf.org/html/rfc7231#section-5.5.3 - // ****************************************************************************************** - - def product = rule { token ~ (ws('/') ~ `product-version` | push("")) } - - def `product-version` = rule { token } - - def `product-or-comment` = rule( - product ~ comment ~> (ProductVersion(_, _, sb.toString)) - | product ~> (ProductVersion(_, _)) - | comment ~ push(ProductVersion("", "", sb.toString))) - - def products = rule { - `product-or-comment` ~ zeroOrMore(`product-or-comment`) ~> (_ +: _) - } - - // ****************************************************************************************** - // http://tools.ietf.org/html/rfc7230#section-4 - // ****************************************************************************************** - - def `transfer-coding` = rule( - ignoreCase("chunked") ~ OWS ~ push(TransferEncodings.chunked) - | ignoreCase("gzip") ~ OWS ~ push(TransferEncodings.gzip) - | ignoreCase("deflate") ~ OWS ~ push(TransferEncodings.deflate) - | ignoreCase("compress") ~ OWS ~ push(TransferEncodings.compress) - | `transfer-extension`) - - def `transfer-extension` = rule { - token ~ zeroOrMore(ws(';') ~ `transfer-parameter`) ~> (_.toMap) ~> (TransferEncodings.Extension(_, _)) - } - - def `transfer-parameter` = rule { token ~ ws('=') ~ word ~> (_ → _) } - - // ****************************************************************************************** - // helpers - // ****************************************************************************************** - def token0 = rule { oneOrMore(tchar) } - - def listSep = rule { ',' ~ OWS } - - def digit = rule { DIGIT ~ push(digitInt(lastChar)) } - - def digit2 = rule { DIGIT ~ DIGIT ~ push(digitInt(charAt(-2)) * 10 + digitInt(lastChar)) } - - def digit4 = rule { - DIGIT ~ DIGIT ~ DIGIT ~ DIGIT ~ push(digitInt(charAt(-4)) * 1000 + digitInt(charAt(-3)) * 100 + digitInt(charAt(-2)) * 10 + digitInt(lastChar)) - } - - def ws(c: Char) = rule { c ~ OWS } - def ws(s: String) = rule { s ~ OWS } - - // parses a potentially long series of digits and extracts its Long value capping at Int.MaxValue in case of overflows - def longNumberCappedAtIntMaxValue = rule { - capture((1 to 11).times(DIGIT)) ~> (s ⇒ math.min(s.toLong, Int.MaxValue)) ~ zeroOrMore(DIGIT) ~ OWS - } - - // parses a potentially long series of digits and extracts its Long value capping at 999,999,999,999,999,999 in case of overflows - def longNumberCapped = rule( - (capture((1 to 18).times(DIGIT)) ~ !DIGIT ~> (_.toLong) - | oneOrMore(DIGIT) ~ push(999999999999999999L)) ~ OWS) - - private def digitInt(c: Char): Int = c - '0' - - private def createDateTime(year: Int, month: Int, day: Int, hour: Int, min: Int, sec: Int, wkday: Int) = { - val dt = DateTime(year, month, day, hour, min, sec) - if (dt.weekday != wkday) - throw ParsingException(s"Illegal weekday in date $dt: is '${DateTime.weekday(wkday)}' but " + - s"should be '${DateTime.weekday(dt.weekday)}'") - dt - } - - def httpMethodDef = rule { - token ~> { s ⇒ - HttpMethods.getForKey(s) match { - case Some(m) ⇒ m - case None ⇒ HttpMethod.custom(s) - } - } - } - - def newUriParser(input: ParserInput): UriParser - def uriReference: Rule1[Uri] = rule { runSubParser(newUriParser(_).`URI-reference-pushed`) } -} - diff --git a/akka-http-core/src/main/scala/akka/http/impl/model/parser/ContentDispositionHeader.scala b/akka-http-core/src/main/scala/akka/http/impl/model/parser/ContentDispositionHeader.scala deleted file mode 100644 index 6c11098f87..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/model/parser/ContentDispositionHeader.scala +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.model.parser - -import akka.parboiled2.Parser -import akka.http.scaladsl.model.headers._ - -private[parser] trait ContentDispositionHeader { this: Parser with CommonRules with CommonActions ⇒ - - // http://tools.ietf.org/html/rfc6266#section-4.1 - def `content-disposition` = rule { - `disposition-type` ~ zeroOrMore(ws(';') ~ `disposition-parm`) ~ EOI ~> (_.toMap) ~> (`Content-Disposition`(_, _)) - } - - def `disposition-type` = rule( - ignoreCase("inline") ~ OWS ~ push(ContentDispositionTypes.inline) - | ignoreCase("attachment") ~ OWS ~ push(ContentDispositionTypes.attachment) - | ignoreCase("form-data") ~ OWS ~ push(ContentDispositionTypes.`form-data`) - | `disp-ext-type` ~> (ContentDispositionTypes.Ext(_))) - - def `disp-ext-type` = rule { token } - - def `disposition-parm` = rule { (`filename-parm` | `disp-ext-parm`) ~> (_ → _) } - - def `filename-parm` = rule( - ignoreCase("filename") ~ OWS ~ ws('=') ~ push("filename") ~ word - | ignoreCase("filename*") ~ OWS ~ ws('=') ~ push("filename") ~ `ext-value`) - - def `disp-ext-parm` = rule( - token ~ ws('=') ~ word - | `ext-token` ~ ws('=') ~ `ext-value`) - - def `ext-token` = rule { // token which ends with '*' - token ~> (s ⇒ test(s endsWith "*") ~ push(s)) - } - - def `ext-value` = rule { word } // support full `ext-value` notation from http://tools.ietf.org/html/rfc5987#section-3.2.1 -} \ No newline at end of file diff --git a/akka-http-core/src/main/scala/akka/http/impl/model/parser/ContentTypeHeader.scala b/akka-http-core/src/main/scala/akka/http/impl/model/parser/ContentTypeHeader.scala deleted file mode 100644 index 4df602a98c..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/model/parser/ContentTypeHeader.scala +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.model.parser - -import scala.annotation.tailrec -import akka.parboiled2.Parser -import akka.http.scaladsl.model._ - -private[parser] trait ContentTypeHeader { this: Parser with CommonRules with CommonActions ⇒ - - // http://tools.ietf.org/html/rfc7231#section-3.1.1.5 - def `content-type` = rule { - `media-type` ~ EOI ~> ((main, sub, params) ⇒ headers.`Content-Type`(contentType(main, sub, params))) - } - - @tailrec private def contentType( - main: String, - sub: String, - params: Seq[(String, String)], - charset: Option[HttpCharset] = None, - builder: StringMapBuilder = null): ContentType = - params match { - case Nil ⇒ - val parameters = if (builder eq null) Map.empty[String, String] else builder.result() - getMediaType(main, sub, charset.isDefined, parameters) match { - case x: MediaType.Binary ⇒ ContentType.Binary(x) - case x: MediaType.WithFixedCharset ⇒ ContentType.WithFixedCharset(x) - case x: MediaType.WithOpenCharset ⇒ - // if we have an open charset media-type but no charset parameter we default to UTF-8 - val cs = if (charset.isDefined) charset.get else HttpCharsets.`UTF-8` - ContentType.WithCharset(x, cs) - } - - case Seq(("charset", value), tail @ _*) ⇒ - contentType(main, sub, tail, Some(getCharset(value)), builder) - - case Seq(kvp, tail @ _*) ⇒ - val b = if (builder eq null) Map.newBuilder[String, String] else builder - b += kvp - contentType(main, sub, tail, charset, b) - } -} \ No newline at end of file diff --git a/akka-http-core/src/main/scala/akka/http/impl/model/parser/HeaderParser.scala b/akka-http-core/src/main/scala/akka/http/impl/model/parser/HeaderParser.scala deleted file mode 100644 index 04c6e3a157..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/model/parser/HeaderParser.scala +++ /dev/null @@ -1,195 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.model.parser - -import akka.http.scaladsl.settings.ParserSettings -import akka.http.scaladsl.settings.ParserSettings.CookieParsingMode -import akka.http.scaladsl.settings.ParserSettings.IllegalResponseHeaderValueProcessingMode -import akka.http.scaladsl.model.headers.HttpCookiePair -import akka.stream.impl.ConstantFun -import scala.util.control.NonFatal -import akka.http.impl.util.SingletonException -import akka.parboiled2._ -import akka.shapeless._ -import akka.http.scaladsl.model._ - -/** - * INTERNAL API. - */ -private[http] class HeaderParser( - val input: ParserInput, - settings: HeaderParser.Settings = HeaderParser.DefaultSettings) - extends Parser with DynamicRuleHandler[HeaderParser, HttpHeader :: HNil] - with CommonRules - with AcceptCharsetHeader - with AcceptEncodingHeader - with AcceptHeader - with AcceptLanguageHeader - with CacheControlHeader - with ContentDispositionHeader - with ContentTypeHeader - with CommonActions - with IpAddressParsing - with LinkHeader - with SimpleHeaders - with StringBuilding - with WebSocketHeaders { - import CharacterClasses._ - - override def customMediaTypes = settings.customMediaTypes - - // http://www.rfc-editor.org/errata_search.php?rfc=7230 errata id 4189 - def `header-field-value`: Rule1[String] = rule { - FWS ~ clearSB() ~ `field-value` ~ FWS ~ EOI ~ push(sb.toString) - } - def `field-value` = { - var fwsStart = cursor - rule { - zeroOrMore(`field-value-chunk`).separatedBy { // zeroOrMore because we need to also accept empty values - run { fwsStart = cursor } ~ FWS ~ &(`field-value-char`) ~ run { if (cursor > fwsStart) sb.append(' ') } - } - } - } - def `field-value-chunk` = rule { oneOrMore(`field-value-char` ~ appendSB()) } - def `field-value-char` = rule { VCHAR | `obs-text` } - def FWS = rule { zeroOrMore(WSP) ~ zeroOrMore(`obs-fold`) } - def `obs-fold` = rule { CRLF ~ oneOrMore(WSP) } - - ///////////////// DynamicRuleHandler ////////////// - - type Result = Either[ErrorInfo, HttpHeader] - def parser: HeaderParser = this - def success(result: HttpHeader :: HNil): Result = Right(result.head) - def parseError(error: ParseError): Result = { - val formatter = new ErrorFormatter(showLine = false) - Left(ErrorInfo(formatter.format(error, input), formatter.formatErrorLine(error, input))) - } - def failure(error: Throwable): Result = error match { - case IllegalUriException(info) ⇒ Left(info) - case NonFatal(e) ⇒ Left(ErrorInfo.fromCompoundString(e.getMessage)) - } - def ruleNotFound(ruleName: String): Result = throw HeaderParser.RuleNotFoundException - - def newUriParser(input: ParserInput): UriParser = new UriParser(input, uriParsingMode = settings.uriParsingMode) - - def `cookie-value`: Rule1[String] = - settings.cookieParsingMode match { - case CookieParsingMode.RFC6265 ⇒ rule { `cookie-value-rfc-6265` } - case CookieParsingMode.Raw ⇒ rule { `cookie-value-raw` } - } - - def createCookiePair(name: String, value: String): HttpCookiePair = settings.cookieParsingMode match { - case CookieParsingMode.RFC6265 ⇒ HttpCookiePair(name, value) - case CookieParsingMode.Raw ⇒ HttpCookiePair.raw(name, value) - } -} - -/** - * INTERNAL API. - */ -private[http] object HeaderParser { - object RuleNotFoundException extends SingletonException - object EmptyCookieException extends SingletonException("Cookie header contained no parsable cookie values.") - - def parseFull(headerName: String, value: String, settings: Settings = DefaultSettings): HeaderParser#Result = { - import akka.parboiled2.EOI - val v = value + EOI // this makes sure the parser isn't broken even if there's no trailing garbage in this value - val parser = new HeaderParser(v, settings) - dispatch(parser, headerName) match { - case r @ Right(_) if parser.cursor == v.length ⇒ r - case r @ Right(_) ⇒ - Left(ErrorInfo( - "Header parsing error", - s"Rule for $headerName accepted trailing garbage. Is the parser missing a trailing EOI?")) - case Left(e) ⇒ Left(e.copy(summary = e.summary.filterNot(_ == EOI), detail = e.detail.filterNot(_ == EOI))) - } - } - - val (dispatch, ruleNames) = DynamicRuleDispatch[HeaderParser, HttpHeader :: HNil]( - "accept", - "accept-charset", - "accept-encoding", - "accept-language", - "accept-ranges", - "access-control-allow-credentials", - "access-control-allow-headers", - "access-control-allow-methods", - "access-control-allow-origin", - "access-control-expose-headers", - "access-control-max-age", - "access-control-request-headers", - "access-control-request-method", - "accept", - "age", - "allow", - "authorization", - "cache-control", - "connection", - "content-disposition", - "content-encoding", - "content-length", - "content-range", - "content-type", - "cookie", - "date", - "etag", - "expect", - "expires", - "host", - "if-match", - "if-modified-since", - "if-none-match", - "if-range", - "if-unmodified-since", - "last-modified", - "link", - "location", - "origin", - "proxy-authenticate", - "proxy-authorization", - "range", - "referer", - "server", - "sec-websocket-accept", - "sec-websocket-extensions", - "sec-websocket-key", - "sec-websocket-protocol", - "sec-websocket-version", - "set-cookie", - "strict-transport-security", - "transfer-encoding", - "upgrade", - "user-agent", - "www-authenticate", - "x-forwarded-for", - "x-real-ip") - - abstract class Settings { - def uriParsingMode: Uri.ParsingMode - def cookieParsingMode: ParserSettings.CookieParsingMode - def customMediaTypes: MediaTypes.FindCustom - def illegalResponseHeaderValueProcessingMode: IllegalResponseHeaderValueProcessingMode - } - def Settings( - uriParsingMode: Uri.ParsingMode = Uri.ParsingMode.Relaxed, - cookieParsingMode: ParserSettings.CookieParsingMode = ParserSettings.CookieParsingMode.RFC6265, - customMediaTypes: MediaTypes.FindCustom = ConstantFun.scalaAnyTwoToNone, - mode: IllegalResponseHeaderValueProcessingMode = ParserSettings.IllegalResponseHeaderValueProcessingMode.Error): Settings = { - - val _uriParsingMode = uriParsingMode - val _cookieParsingMode = cookieParsingMode - val _customMediaTypes = customMediaTypes - val _illegalResponseHeaderValueProcessingMode = mode - - new Settings { - def uriParsingMode: Uri.ParsingMode = _uriParsingMode - def cookieParsingMode: CookieParsingMode = _cookieParsingMode - def customMediaTypes: MediaTypes.FindCustom = _customMediaTypes - def illegalResponseHeaderValueProcessingMode: IllegalResponseHeaderValueProcessingMode = - _illegalResponseHeaderValueProcessingMode - } - } - val DefaultSettings: Settings = Settings() -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/model/parser/IpAddressParsing.scala b/akka-http-core/src/main/scala/akka/http/impl/model/parser/IpAddressParsing.scala deleted file mode 100644 index 64d3efcf8b..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/model/parser/IpAddressParsing.scala +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.model.parser - -import akka.parboiled2._ - -private[parser] trait IpAddressParsing { this: Parser ⇒ - import CharacterClasses._ - - def `ip-v4-address` = rule { - `ip-number` ~ '.' ~ `ip-number` ~ '.' ~ `ip-number` ~ '.' ~ `ip-number` ~> (Array[Byte](_, _, _, _)) - } - - def `ip-number` = rule { - capture( - '2' ~ (DIGIT04 ~ DIGIT | '5' ~ DIGIT05) - | '1' ~ DIGIT ~ DIGIT - | DIGIT19 ~ DIGIT - | DIGIT) ~> (java.lang.Integer.parseInt(_).toByte) - } - - def `ip-v6-address`: Rule1[Array[Byte]] = { - import CharUtils.{ hexValue ⇒ hv } - var a: Array[Byte] = null - def zero(ix: Int) = rule { run(a(ix) = 0.toByte) } - def zero2(ix: Int) = rule { run { a(ix) = 0.toByte; a(ix + 1) = 0.toByte; } } - def h4(ix: Int) = rule { HEXDIG ~ run(a(ix) = hv(lastChar).toByte) } - def h8(ix: Int) = rule { HEXDIG ~ HEXDIG ~ run(a(ix) = (hv(charAt(-2)) * 16 + hv(lastChar)).toByte) } - def h16(ix: Int) = rule { h8(ix) ~ h8(ix + 1) | h4(ix) ~ h8(ix + 1) | zero(ix) ~ h8(ix + 1) | zero(ix) ~ h4(ix + 1) } - def h16c(ix: Int) = rule { h16(ix) ~ ':' ~ !':' } - def ch16o(ix: Int) = rule { optional(':' ~ !':') ~ (h16(ix) | zero2(ix)) } - def ls32 = rule { h16(12) ~ ':' ~ h16(14) | `ip-v4-address` ~> (System.arraycopy(_, 0, a, 12, 4)) } - def cc(ix: Int) = rule { ':' ~ ':' ~ zero2(ix) } - def tail2 = rule { h16c(2) ~ tail4 } - def tail4 = rule { h16c(4) ~ tail6 } - def tail6 = rule { h16c(6) ~ tail8 } - def tail8 = rule { h16c(8) ~ tail10 } - def tail10 = rule { h16c(10) ~ ls32 } - rule { - !(':' ~ HEXDIG) ~ push { a = new Array[Byte](16); a } ~ ( - h16c(0) ~ tail2 - | cc(0) ~ tail2 - | ch16o(0) ~ ( - cc(2) ~ tail4 - | ch16o(2) ~ ( - cc(4) ~ tail6 - | ch16o(4) ~ ( - cc(6) ~ tail8 - | ch16o(6) ~ ( - cc(8) ~ tail10 - | ch16o(8) ~ ( - cc(10) ~ ls32 - | ch16o(10) ~ ( - cc(12) ~ h16(14) - | ch16o(12) ~ cc(14)))))))) - } - } - - def `ip-v6-reference`: Rule1[String] = rule { capture('[' ~ oneOrMore(HEXDIG | anyOf(":.")) ~ ']') } -} - diff --git a/akka-http-core/src/main/scala/akka/http/impl/model/parser/LinkHeader.scala b/akka-http-core/src/main/scala/akka/http/impl/model/parser/LinkHeader.scala deleted file mode 100644 index 15874bbe7d..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/model/parser/LinkHeader.scala +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.model.parser - -import scala.annotation.tailrec -import akka.parboiled2.Parser -import akka.http.scaladsl.model.{ ParsingException, IllegalUriException } -import akka.http.scaladsl.model.headers._ - -private[parser] trait LinkHeader { this: Parser with CommonRules with CommonActions ⇒ - import CharacterClasses._ - - // http://tools.ietf.org/html/rfc5988#section-5 - def `link` = rule { - zeroOrMore(`link-value`).separatedBy(listSep) ~ EOI ~> (Link(_)) - } - - def `link-value` = rule { - ws('<') ~ UriReference('>') ~ ws('>') ~ oneOrMore(ws(';') ~ `link-param`) ~> (sanitize(_)) ~> (LinkValue(_, _: _*)) - } - - def `link-param` = rule( - ws("rel") ~ ws('=') ~ `relation-types` ~> LinkParams.rel - | ws("anchor") ~ ws('=') ~ ws('"') ~ UriReference('"') ~ ws('"') ~> LinkParams.anchor - | ws("rev") ~ ws('=') ~ `relation-types` ~> LinkParams.rev - | ws("hreflang") ~ ws('=') ~ language ~> LinkParams.hreflang - | ws("media") ~ ws('=') ~ word ~> LinkParams.media - | ws("title") ~ ws('=') ~ word ~> LinkParams.title - | ws("title*") ~ ws('=') ~ word ~> LinkParams.`title*` // support full `ext-value` notation from http://tools.ietf.org/html/rfc5987#section-3.2.1 - | ws("type") ~ ws('=') ~ (ws('"') ~ `link-media-type` ~ ws('"') | `link-media-type`) ~> LinkParams.`type`) - // TODO: support `link-extension` - - def `relation-types` = rule( - ws('"') ~ oneOrMore(`relation-type`).separatedBy(oneOrMore(SP)) ~> (_.mkString(" ")) ~ ws('"') - | `relation-type` ~ OWS) - - def `relation-type` = rule { `reg-rel-type` | `ext-rel-type` } - - def `reg-rel-type` = rule { - capture(LOWER_ALPHA ~ zeroOrMore(`reg-rel-type-octet`)) ~ !VCHAR - } - - def `ext-rel-type` = rule { - URI - } - - ////////////////////////////// helpers /////////////////////////////////// - - def UriReference(terminationChar: Char) = rule { - capture(oneOrMore(!terminationChar ~ VCHAR)) ~> (newUriParser(_).parseUriReference()) - } - - def URI = rule { - capture(oneOrMore(!'"' ~ !';' ~ !',' ~ VCHAR)) ~> { s ⇒ - try new UriParser(s).parseUriReference() - catch { - case IllegalUriException(info) ⇒ throw ParsingException(info.withSummaryPrepended("Illegal `Link` header relation-type")) - } - s - } - } - - def `link-media-type` = rule { `media-type` ~> ((mt, st, pm) ⇒ getMediaType(mt, st, pm contains "charset", pm.toMap)) } - - // filter out subsequent `rel`, `media`, `title`, `type` and `type*` params - @tailrec private def sanitize(params: Seq[LinkParam], result: Seq[LinkParam] = Nil, seenRel: Boolean = false, - seenMedia: Boolean = false, seenTitle: Boolean = false, seenTitleS: Boolean = false, - seenType: Boolean = false): Seq[LinkParam] = - params match { - case Seq((x: LinkParams.rel), tail @ _*) ⇒ sanitize(tail, if (seenRel) result else result :+ x, seenRel = true, seenMedia, seenTitle, seenTitleS, seenType) - case Seq((x: LinkParams.media), tail @ _*) ⇒ sanitize(tail, if (seenMedia) result else result :+ x, seenRel, seenMedia = true, seenTitle, seenTitleS, seenType) - case Seq((x: LinkParams.title), tail @ _*) ⇒ sanitize(tail, if (seenTitle) result else result :+ x, seenRel, seenMedia, seenTitle = true, seenTitleS, seenType) - case Seq((x: LinkParams.`title*`), tail @ _*) ⇒ sanitize(tail, if (seenTitleS) result else result :+ x, seenRel, seenMedia, seenTitle, seenTitleS = true, seenType) - case Seq((x: LinkParams.`type`), tail @ _*) ⇒ sanitize(tail, if (seenType) result else result :+ x, seenRel, seenMedia, seenTitle, seenTitleS, seenType = true) - case Seq(head, tail @ _*) ⇒ sanitize(tail, result :+ head, seenRel, seenMedia, seenTitle, seenTitleS, seenType) - case Nil ⇒ result - } -} \ No newline at end of file diff --git a/akka-http-core/src/main/scala/akka/http/impl/model/parser/SimpleHeaders.scala b/akka-http-core/src/main/scala/akka/http/impl/model/parser/SimpleHeaders.scala deleted file mode 100644 index d5f82c7e40..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/model/parser/SimpleHeaders.scala +++ /dev/null @@ -1,233 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.model.parser - -import akka.parboiled2.Parser -import akka.http.scaladsl.model.RemoteAddress -import akka.http.scaladsl.model.headers._ - -/** - * Parser rules for all headers that can be parsed with one single rule. - * All header rules that require more than one single rule are modelled in their own trait. - */ -private[parser] trait SimpleHeaders { this: Parser with CommonRules with CommonActions with IpAddressParsing ⇒ - - // http://tools.ietf.org/html/rfc7233#section-2.3 - def `accept-ranges` = rule { - ("none" ~ push(Nil) | zeroOrMore(ws(',')) ~ oneOrMore(`range-unit`).separatedBy(listSep)) ~ EOI ~> (`Accept-Ranges`(_)) - } - - // http://www.w3.org/TR/cors/#access-control-allow-credentials-response-header - // in addition to the spec we also allow for a `false` value - def `access-control-allow-credentials` = rule( - ("true" ~ push(`Access-Control-Allow-Credentials`(true)) - | "false" ~ push(`Access-Control-Allow-Credentials`(false))) ~ EOI) - - // http://www.w3.org/TR/cors/#access-control-allow-headers-response-header - def `access-control-allow-headers` = rule { - zeroOrMore(token).separatedBy(listSep) ~ EOI ~> (`Access-Control-Allow-Headers`(_)) - } - - // http://www.w3.org/TR/cors/#access-control-allow-methods-response-header - def `access-control-allow-methods` = rule { - zeroOrMore(httpMethodDef).separatedBy(listSep) ~ EOI ~> (`Access-Control-Allow-Methods`(_)) - } - - // http://www.w3.org/TR/cors/#access-control-allow-origin-response-header - def `access-control-allow-origin` = rule( - ws('*') ~ EOI ~ push(`Access-Control-Allow-Origin`.`*`) - | `origin-list-or-null` ~ EOI ~> (origins ⇒ `Access-Control-Allow-Origin`.forRange(HttpOriginRange(origins: _*)))) - - // http://www.w3.org/TR/cors/#access-control-expose-headers-response-header - def `access-control-expose-headers` = rule { - zeroOrMore(token).separatedBy(listSep) ~ EOI ~> (`Access-Control-Expose-Headers`(_)) - } - - // http://www.w3.org/TR/cors/#access-control-max-age-response-header - def `access-control-max-age` = rule { - `delta-seconds` ~ EOI ~> (`Access-Control-Max-Age`(_)) - } - - // http://www.w3.org/TR/cors/#access-control-request-headers-request-header - def `access-control-request-headers` = rule { - zeroOrMore(token).separatedBy(listSep) ~ EOI ~> (`Access-Control-Request-Headers`(_)) - } - - // http://www.w3.org/TR/cors/#access-control-request-method-request-header - def `access-control-request-method` = rule { - httpMethodDef ~ EOI ~> (`Access-Control-Request-Method`(_)) - } - - // http://tools.ietf.org/html/rfc7234#section-5.1 - def age = rule { `delta-seconds` ~ EOI ~> (Age(_)) } - - // http://tools.ietf.org/html/rfc7231#section-7.4.1 - def allow = rule { - zeroOrMore(httpMethodDef).separatedBy(listSep) ~ EOI ~> (Allow(_)) - } - - // http://tools.ietf.org/html/rfc7235#section-4.2 - def authorization = rule { credentials ~ EOI ~> (Authorization(_)) } - - // http://tools.ietf.org/html/rfc7230#section-6.1 - def connection = rule { - oneOrMore(token).separatedBy(listSep) ~ EOI ~> (Connection(_)) - } - - // http://tools.ietf.org/html/rfc7231#section-3.1.2.2 - // http://tools.ietf.org/html/rfc7231#appendix-D - def `content-encoding` = rule { - oneOrMore(token ~> (x ⇒ HttpEncodings.getForKeyCaseInsensitive(x) getOrElse HttpEncoding.custom(x))) - .separatedBy(listSep) ~ EOI ~> (`Content-Encoding`(_)) - } - - // http://tools.ietf.org/html/rfc7230#section-3.3.2 - def `content-length` = rule { - longNumberCapped ~> (`Content-Length`(_)) ~ EOI - } - - // http://tools.ietf.org/html/rfc7233#section-4.2 - def `content-range` = rule { - (`byte-content-range` | `other-content-range`) ~ EOI ~> (`Content-Range`(_, _)) - } - - // https://tools.ietf.org/html/rfc6265#section-4.2 - def `cookie` = rule { - oneOrMore(`optional-cookie-pair`).separatedBy(';' ~ OWS) ~ EOI ~> { pairs ⇒ - val validPairs = pairs.collect { case Some(p) ⇒ p } - `Cookie` { - if (validPairs.nonEmpty) validPairs - // Parsing infrastructure requires to return an HttpHeader value here but it is not possible - // to create a Cookie header without elements, so we throw here. This will 1) log a warning - // provide the complete content of the header as a RawHeader - else throw HeaderParser.EmptyCookieException - } - } - } - - // http://tools.ietf.org/html/rfc7231#section-7.1.1.2 - def `date` = rule { - `HTTP-date` ~ EOI ~> (Date(_)) - } - - // http://tools.ietf.org/html/rfc7232#section-2.3 - def etag = rule { `entity-tag` ~ EOI ~> (ETag(_)) } - - // http://tools.ietf.org/html/rfc7231#section-5.1.1 - def `expect` = rule { - ignoreCase("100-continue") ~ OWS ~ EOI ~ push(Expect.`100-continue`) - } - - // http://tools.ietf.org/html/rfc7234#section-5.3 - def `expires` = rule { `expires-date` ~ EOI ~> (Expires(_)) } - - // http://tools.ietf.org/html/rfc7230#section-5.4 - // We don't accept scoped IPv6 addresses as they should not appear in the Host header, - // see also https://issues.apache.org/bugzilla/show_bug.cgi?id=35122 (WONTFIX in Apache 2 issue) and - // https://bugzilla.mozilla.org/show_bug.cgi?id=464162 (FIXED in mozilla) - // Also: an empty hostnames with a non-empty port value (as in `Host: :8080`) are *allowed*, - // see http://trac.tools.ietf.org/wg/httpbis/trac/ticket/92 - def host = rule { - runSubParser(newUriParser(_).`hostAndPort-pushed`) ~ EOI ~> (Host(_, _)) - } - - // http://tools.ietf.org/html/rfc7232#section-3.1 - def `if-match` = rule( - ws('*') ~ EOI ~ push(`If-Match`.`*`) - | oneOrMore(`entity-tag`).separatedBy(listSep) ~ EOI ~> (tags ⇒ `If-Match`(EntityTagRange(tags: _*)))) - - // http://tools.ietf.org/html/rfc7232#section-3.3 - def `if-modified-since` = rule { `HTTP-date` ~ EOI ~> (`If-Modified-Since`(_)) } - - // http://tools.ietf.org/html/rfc7232#section-3.2 - def `if-none-match` = rule { - ws('*') ~ EOI ~ push(`If-None-Match`.`*`) | - oneOrMore(`entity-tag`).separatedBy(listSep) ~ EOI ~> (tags ⇒ `If-None-Match`(EntityTagRange(tags: _*))) - } - - // http://tools.ietf.org/html/rfc7232#section-3.5 - // http://tools.ietf.org/html/rfc7233#section-3.2 - def `if-range` = rule { (`entity-tag` ~> (Left(_)) | `HTTP-date` ~> (Right(_))) ~ EOI ~> (`If-Range`(_)) } - - // http://tools.ietf.org/html/rfc7232#section-3.4 - def `if-unmodified-since` = rule { `HTTP-date` ~ EOI ~> (`If-Unmodified-Since`(_)) } - - // http://tools.ietf.org/html/rfc7232#section-2.2 - def `last-modified` = rule { `HTTP-date` ~ EOI ~> (`Last-Modified`(_)) } - - // http://tools.ietf.org/html/rfc7231#section-7.1.2 - def location = rule { - uriReference ~ EOI ~> (Location(_)) - } - - // http://tools.ietf.org/html/rfc6454#section-7 - def `origin` = rule { `origin-list-or-null` ~ EOI ~> (Origin(_)) } - - // http://tools.ietf.org/html/rfc7235#section-4.3 - def `proxy-authenticate` = rule { - oneOrMore(challenge).separatedBy(listSep) ~ EOI ~> (`Proxy-Authenticate`(_)) - } - - // http://tools.ietf.org/html/rfc7235#section-4.4 - def `proxy-authorization` = rule { credentials ~ EOI ~> (`Proxy-Authorization`(_)) } - - // http://tools.ietf.org/html/rfc7233#section-3.1 - def `range` = rule { `byte-ranges-specifier` /*| `other-ranges-specifier` */ ~ EOI ~> (Range(_, _)) } - - // http://tools.ietf.org/html/rfc7231#section-5.5.2 - def referer = rule { - // we are bit more relaxed than the spec here by also parsing a potential fragment - // but catch it in the `Referer` instance validation (with a `require` in the constructor) - uriReference ~ EOI ~> (Referer(_)) - } - - // http://tools.ietf.org/html/rfc7231#section-7.4.2 - def server = rule { products ~ EOI ~> (Server(_)) } - - def `strict-transport-security` = rule { - ignoreCase("max-age=") ~ `delta-seconds` ~ optional(ws(";") ~ ignoreCase("includesubdomains") ~ push(true)) ~ zeroOrMore(ws(";") ~ token0) ~ EOI ~> (`Strict-Transport-Security`(_, _)) - } - - // http://tools.ietf.org/html/rfc7230#section-3.3.1 - def `transfer-encoding` = rule { - oneOrMore(`transfer-coding`).separatedBy(listSep) ~ EOI ~> (`Transfer-Encoding`(_)) - } - - // https://tools.ietf.org/html/rfc6265 - def `set-cookie` = rule { - `cookie-pair` ~> (_.toCookie) ~ zeroOrMore(ws(';') ~ `cookie-av`) ~ EOI ~> (`Set-Cookie`(_)) - } - - // http://tools.ietf.org/html/rfc7230#section-6.7 - def upgrade = rule { - oneOrMore(protocol).separatedBy(listSep) ~ EOI ~> (Upgrade(_)) - } - - def protocol = rule { - token ~ optional(ws("/") ~ token) ~> (UpgradeProtocol(_, _)) - } - - // http://tools.ietf.org/html/rfc7231#section-5.5.3 - def `user-agent` = rule { products ~ EOI ~> (`User-Agent`(_)) } - - // http://tools.ietf.org/html/rfc7235#section-4.1 - def `www-authenticate` = rule { - oneOrMore(challenge).separatedBy(listSep) ~ EOI ~> (`WWW-Authenticate`(_)) - } - - // de-facto standard as per http://en.wikipedia.org/wiki/X-Forwarded-For - // It's not clear in which format IpV6 addresses are to be expected, the ones we've seen in the wild - // were not quoted and that's also what the "Transition" section in the draft says: - // http://tools.ietf.org/html/draft-ietf-appsawg-http-forwarded-10 - def `x-forwarded-for` = { - def addr = rule { (`ip-v4-address` | `ip-v6-address`) ~> (RemoteAddress(_)) | "unknown" ~ push(RemoteAddress.Unknown) } - rule { oneOrMore(addr).separatedBy(listSep) ~ EOI ~> (`X-Forwarded-For`(_)) } - } - - def `x-real-ip` = rule { - (`ip-v4-address` | `ip-v6-address`) ~ EOI ~> (b ⇒ `X-Real-Ip`(RemoteAddress(b))) - } - -} \ No newline at end of file diff --git a/akka-http-core/src/main/scala/akka/http/impl/model/parser/StringBuilding.scala b/akka-http-core/src/main/scala/akka/http/impl/model/parser/StringBuilding.scala deleted file mode 100644 index 9ae10b44bd..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/model/parser/StringBuilding.scala +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2009-2016 Mathias Doenitz, Alexander Myltsev - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package akka.http.impl.model.parser - -import akka.parboiled2._ - -/** - * For certain high-performance use-cases it is better to construct Strings - * that the parser is to produce/extract from the input in a char-by-char fashion. - * - * Mixing this trait into your parser gives you a simple facility to support this. - */ -private[parser] trait StringBuilding { this: Parser ⇒ - protected val sb = new java.lang.StringBuilder - - def clearSB(): Rule0 = rule { run(sb.setLength(0)) } - - def appendSB(): Rule0 = rule { run(sb.append(lastChar)) } - - def appendSB(offset: Int): Rule0 = rule { run(sb.append(charAt(offset))) } - - def appendSB(c: Char): Rule0 = rule { run(sb.append(c)) } - - def appendSB(s: String): Rule0 = rule { run(sb.append(s)) } - - def prependSB(): Rule0 = rule { run(doPrepend(lastChar)) } - - def prependSB(offset: Int): Rule0 = rule { run(doPrepend(charAt(offset))) } - - def prependSB(c: Char): Rule0 = rule { run(doPrepend(c)) } - - def prependSB(s: String): Rule0 = rule { run(doPrepend(s)) } - - def setSB(s: String): Rule0 = rule { run(doSet(s)) } - - private def doPrepend(c: Char): Unit = { - val saved = sb.toString - sb.setLength(0) - sb.append(c) - sb.append(saved) - } - - private def doPrepend(s: String): Unit = { - val saved = sb.toString - sb.setLength(0) - sb.append(s) - sb.append(saved) - } - - private def doSet(s: String): Unit = { - sb.setLength(0) - sb.append(s) - } -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/model/parser/UriParser.scala b/akka-http-core/src/main/scala/akka/http/impl/model/parser/UriParser.scala deleted file mode 100644 index 64a32d8cd5..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/model/parser/UriParser.scala +++ /dev/null @@ -1,265 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.model.parser - -import java.nio.charset.Charset -import akka.parboiled2._ -import akka.http.impl.util.enhanceString_ -import akka.http.scaladsl.model.Uri -import akka.http.scaladsl.model.headers.HttpOrigin -import Parser.DeliveryScheme.Either -import Uri._ - -// format: OFF -/** - * INTERNAL API - * - * http://tools.ietf.org/html/rfc3986 - */ -private[http] final class UriParser(val input: ParserInput, - val uriParsingCharset: Charset, - val uriParsingMode: Uri.ParsingMode, - val maxValueStackSize: Int) extends Parser(maxValueStackSize) - with IpAddressParsing with StringBuilding { - import CharacterClasses._ - - def this(input: ParserInput, - uriParsingCharset: Charset = UTF8, - uriParsingMode: Uri.ParsingMode = Uri.ParsingMode.Relaxed) = - this(input, uriParsingCharset, uriParsingMode, 1024) - - def parseAbsoluteUri(): Uri = - rule(`absolute-URI` ~ EOI).run() match { - case Right(_) => create(_scheme, _userinfo, _host, _port, collapseDotSegments(_path), _rawQueryString, _fragment) - case Left(error) => fail(error, "absolute URI") - } - - def parseUriReference(): Uri = - rule(`URI-reference` ~ EOI).run() match { - case Right(_) => createUriReference() - case Left(error) => fail(error, "URI reference") - } - - def parseAndResolveUriReference(base: Uri): Uri = - rule(`URI-reference` ~ EOI).run() match { - case Right(_) => resolve(_scheme, _userinfo, _host, _port, _path, _rawQueryString, _fragment, base) - case Left(error) => fail(error, "URI reference") - } - - def parseOrigin(): HttpOrigin = - rule(origin ~ EOI).run() match { - case Right(_) => HttpOrigin(_scheme, akka.http.scaladsl.model.headers.Host(_host.address, _port)) - case Left(error) => fail(error, "origin") - } - - def parseHost(): Host = - rule(relaxedHost ~ EOI).run() match { - case Right(_) => _host - case Left(error) => fail(error, "URI host") - } - - def parseQuery(): Query = - rule(query ~ EOI).run() match { - case Right(query) => query - case Left(error) => fail(error, "query") - } - - def fail(error: ParseError, target: String): Nothing = { - val formatter = new ErrorFormatter(showLine = false) - Uri.fail(s"Illegal $target: " + formatter.format(error, input), formatter.formatErrorLine(error, input)) - } - - private[this] val `path-segment-char` = uriParsingMode match { - case Uri.ParsingMode.Strict ⇒ `pchar-base` - case _ ⇒ `relaxed-path-segment-char` - } - private[this] val `query-char` = uriParsingMode match { - case Uri.ParsingMode.Strict ⇒ `strict-query-char` - case Uri.ParsingMode.Relaxed ⇒ `relaxed-query-char` - } - private[this] val `fragment-char` = uriParsingMode match { - case Uri.ParsingMode.Strict ⇒ `query-fragment-char` - case _ ⇒ `relaxed-fragment-char` - } - - var _scheme = "" - var _userinfo = "" - var _host: Host = Host.Empty - var _port: Int = 0 - var _path: Path = Path.Empty - var _rawQueryString: Option[String] = None - var _fragment: Option[String] = None - - // http://tools.ietf.org/html/rfc3986#appendix-A - - def URI = rule { scheme ~ ':' ~ `hier-part` ~ optional('?' ~ rawQueryString) ~ optional('#' ~ fragment) } - - def origin = rule { scheme ~ ':' ~ '/' ~ '/' ~ hostAndPort } - - def `hier-part` = rule( - '/' ~ '/' ~ authority ~ `path-abempty` - | `path-absolute` - | `path-rootless` - | `path-empty`) - - def `URI-reference` = rule { URI | `relative-ref` } - - def `URI-reference-pushed`: Rule1[Uri] = rule { `URI-reference` ~ push(createUriReference()) } - - def `absolute-URI` = rule { scheme ~ ':' ~ `hier-part` ~ optional('?' ~ rawQueryString) } - - def `relative-ref` = rule { `relative-part` ~ optional('?' ~ rawQueryString) ~ optional('#' ~ fragment) } - - def `relative-part` = rule( - '/' ~ '/' ~ authority ~ `path-abempty` - | `path-absolute` - | `path-noscheme` - | `path-empty`) - - def scheme = rule( - 'h' ~ 't' ~ 't' ~ 'p' ~ (&(':') ~ run(_scheme = "http") | 's' ~ &(':') ~ run(_scheme = "https")) - | clearSB() ~ ALPHA ~ appendLowered() ~ zeroOrMore(`scheme-char` ~ appendLowered()) ~ &(':') ~ run(_scheme = sb.toString)) - - def authority = rule { optional(userinfo) ~ hostAndPort } - - def userinfo = rule { - clearSB() ~ zeroOrMore(`userinfo-char` ~ appendSB()| `pct-encoded`) ~ '@' ~ run(_userinfo = sb.toString) - } - - def hostAndPort = rule { host ~ optional(':' ~ port) } - - def `hostAndPort-pushed` = rule { hostAndPort ~ push(_host) ~ push(_port) } - - def host = rule { `IP-literal` | ipv4Host | `reg-name` } - - /** A relaxed host rule to use in `parseHost` that also recognizes IPv6 address without the brackets. */ - def relaxedHost = rule { `IP-literal` | ipv6Host | ipv4Host | `reg-name` } - - def port = rule { - DIGIT ~ run(_port = lastChar - '0') ~ optional( - DIGIT ~ run(_port = 10 * _port + lastChar - '0') ~ optional( - DIGIT ~ run(_port = 10 * _port + lastChar - '0') ~ optional( - DIGIT ~ run(_port = 10 * _port + lastChar - '0') ~ optional( - DIGIT ~ run(_port = 10 * _port + lastChar - '0'))))) - } - - def `IP-literal` = rule { '[' ~ ipv6Host ~ ']' } // IPvFuture not currently recognized - - def ipv4Host = rule { capture(`ip-v4-address`) ~ &(colonSlashEOI) ~> ((b, a) => _host = IPv4Host(b, a)) } - def ipv6Host = rule { capture(`ip-v6-address`) ~> ((b, a) => _host = IPv6Host(b, a)) } - - def `reg-name` = rule( - clearSBForDecoding() ~ oneOrMore(`lower-reg-name-char` ~ appendSB() | UPPER_ALPHA ~ appendLowered() | `pct-encoded`) ~ - run(_host = NamedHost(getDecodedStringAndLowerIfEncoded(UTF8))) - | run(_host = Host.Empty)) - - def `path-abempty` = rule { clearSB() ~ slashSegments ~ savePath() } - def `path-absolute` = rule { clearSB() ~ '/' ~ appendSB('/') ~ optional(`segment-nz` ~ slashSegments) ~ savePath() } - def `path-noscheme` = rule { clearSB() ~ `segment-nz-nc` ~ slashSegments ~ savePath() } - def `path-rootless` = rule { clearSB() ~ `segment-nz` ~ slashSegments ~ savePath() } - def `path-empty` = rule { MATCH } - - def slashSegments = rule { zeroOrMore('/' ~ appendSB('/') ~ segment) } - - def segment = rule { zeroOrMore(pchar) } - def `segment-nz` = rule { oneOrMore(pchar) } - def `segment-nz-nc` = rule { oneOrMore(!':' ~ pchar) } - - def pchar = rule { `path-segment-char` ~ appendSB() | `pct-encoded` } - - def rawQueryString = rule { - clearSB() ~ oneOrMore(`raw-query-char` ~ appendSB()) ~ run(_rawQueryString = Some(sb.toString)) | run(_rawQueryString = Some("")) - } - - // http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1 - def query: Rule1[Query] = { - def part = rule( - clearSBForDecoding() ~ oneOrMore('+' ~ appendSB(' ') | `query-char` ~ appendSB() | `pct-encoded`) ~ push(getDecodedString()) - | push("")) - - def keyValuePair: Rule2[String, String] = rule { - part ~ ('=' ~ part | push(Query.EmptyValue)) - } - - // has a max value-stack depth of 3 - def keyValuePairsWithLimitedStackUse: Rule1[Query] = rule { - keyValuePair ~> { (key, value) => Query.Cons(key, value, Query.Empty) } ~ { - zeroOrMore('&' ~ keyValuePair ~> { (prefix: Query, key, value) => Query.Cons(key, value, prefix) }) ~> - (_.reverse) - } - } - - // non-tail recursion, which we accept because it allows us to directly build the query - // without having to reverse it at the end. - // Adds 2 values to the value stack for the first pair, then parses the remaining pairs. - def keyValuePairsWithReversalAvoidance: Rule1[Query] = rule { - keyValuePair ~ ('&' ~ keyValuePairs | push(Query.Empty)) ~> { (key, value, tail) => - Query.Cons(key, value, tail) - } - } - - // Uses a reversal-free parsing approach as long as there is enough space on the value stack, - // switching to a limited-stack approach when necessary. - def keyValuePairs: Rule1[Query] = - if (valueStack.size + 5 <= maxValueStackSize) keyValuePairsWithReversalAvoidance - else keyValuePairsWithLimitedStackUse - - rule { keyValuePairs } - } - - def fragment = rule( - clearSBForDecoding() ~ oneOrMore(`fragment-char` ~ appendSB() | `pct-encoded`) ~ run(_fragment = Some(getDecodedString())) - | run(_fragment = Some(""))) - - def `pct-encoded` = rule { - '%' ~ HEXDIG ~ HEXDIG ~ run { - if (firstPercentIx == -1) firstPercentIx = sb.length() - sb.append('%').append(charAt(-2)).append(lastChar) - } - } - - //////////////////////////// ADDITIONAL HTTP-SPECIFIC RULES ////////////////////////// - - // http://tools.ietf.org/html/rfc7230#section-2.7 - def `absolute-path` = rule { - clearSB() ~ oneOrMore('/' ~ appendSB('/') ~ segment) ~ savePath() - } - - // http://tools.ietf.org/html/rfc7230#section-5.3 - def `request-target` = rule( - `absolute-path` ~ optional('?' ~ rawQueryString) // origin-form - | `absolute-URI` // absolute-form - | authority) // authority-form or asterisk-form - - def parseHttpRequestTarget(): Uri = - rule(`request-target` ~ EOI).run() match { - case Right(_) => - val path = if (_scheme.isEmpty) _path else collapseDotSegments(_path) - create(_scheme, _userinfo, _host, _port, path, _rawQueryString, _fragment) - case Left(error) => fail(error, "request-target") - } - - ///////////// helpers ///////////// - - private def appendLowered(): Rule0 = rule { run(sb.append(CharUtils.toLowerCase(lastChar))) } - - private def savePath() = rule { run(_path = Path(sb.toString, uriParsingCharset)) } - - private[this] var firstPercentIx = -1 - - private def clearSBForDecoding(): Rule0 = rule { run { sb.setLength(0); firstPercentIx = -1 } } - - private def getDecodedString(charset: Charset = uriParsingCharset) = - if (firstPercentIx >= 0) decode(sb.toString, charset, firstPercentIx)() else sb.toString - - private def getDecodedStringAndLowerIfEncoded(charset: Charset) = - if (firstPercentIx >= 0) decode(sb.toString, charset, firstPercentIx)().toRootLowerCase else sb.toString - - private def createUriReference(): Uri = { - val path = if (_scheme.isEmpty) _path else collapseDotSegments(_path) - create(_scheme, _userinfo, _host, _port, path, _rawQueryString, _fragment) - } -} - diff --git a/akka-http-core/src/main/scala/akka/http/impl/model/parser/WebSocketHeaders.scala b/akka-http-core/src/main/scala/akka/http/impl/model/parser/WebSocketHeaders.scala deleted file mode 100644 index 4216d8f16b..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/model/parser/WebSocketHeaders.scala +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.model.parser - -import akka.http.scaladsl.model.headers._ -import akka.parboiled2._ - -// see grammar at http://tools.ietf.org/html/rfc6455#section-4.3 -private[parser] trait WebSocketHeaders { this: Parser with CommonRules with CommonActions ⇒ - import CharacterClasses._ - import Base64Parsing.rfc2045Alphabet - - def `sec-websocket-accept` = rule { - `base64-value-non-empty` ~ EOI ~> (`Sec-WebSocket-Accept`(_)) - } - - def `sec-websocket-extensions` = rule { - oneOrMore(extension).separatedBy(listSep) ~ EOI ~> (`Sec-WebSocket-Extensions`(_)) - } - - def `sec-websocket-key` = rule { - `base64-value-non-empty` ~ EOI ~> (`Sec-WebSocket-Key`(_)) - } - - def `sec-websocket-protocol` = rule { - oneOrMore(token).separatedBy(listSep) ~ EOI ~> (`Sec-WebSocket-Protocol`(_)) - } - - def `sec-websocket-version` = rule { - oneOrMore(version).separatedBy(listSep) ~ EOI ~> (`Sec-WebSocket-Version`(_)) - } - - private def `base64-value-non-empty` = rule { - capture(oneOrMore(`base64-data`) ~ optional(`base64-padding`) | `base64-padding`) - } - private def `base64-data` = rule { 4.times(`base64-character`) } - private def `base64-padding` = rule { - 2.times(`base64-character`) ~ "==" | - 3.times(`base64-character`) ~ "=" - } - private def `base64-character` = rfc2045Alphabet - - private def extension = rule { - `extension-token` ~ zeroOrMore(ws(";") ~ `extension-param`) ~> - ((name, params) ⇒ WebSocketExtension(name, Map(params: _*))) - } - private def `extension-token`: Rule1[String] = token - private def `extension-param`: Rule1[(String, String)] = - rule { - token ~ optional(ws("=") ~ word) ~> ((name: String, value: Option[String]) ⇒ (name, value.getOrElse(""))) - } - - private def version = rule { - capture( - NZDIGIT ~ optional(DIGIT ~ optional(DIGIT)) | - DIGIT) ~> (_.toInt) - } - private def NZDIGIT = DIGIT19 -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/settings/ClientConnectionSettingsImpl.scala b/akka-http-core/src/main/scala/akka/http/impl/settings/ClientConnectionSettingsImpl.scala deleted file mode 100644 index fbe942717e..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/settings/ClientConnectionSettingsImpl.scala +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright (C) 2009-2014 Lightbend Inc. - */ - -package akka.http.impl.settings - -import java.util.Random - -import akka.http.impl.engine.ws.Randoms -import akka.http.impl.util._ -import akka.http.scaladsl.model.headers.`User-Agent` -import akka.http.scaladsl.settings.ParserSettings -import akka.io.Inet.SocketOption -import com.typesafe.config.Config - -import scala.collection.immutable -import scala.concurrent.duration.{ Duration, FiniteDuration } - -/** INTERNAL API */ -private[akka] final case class ClientConnectionSettingsImpl( - userAgentHeader: Option[`User-Agent`], - connectingTimeout: FiniteDuration, - idleTimeout: Duration, - requestHeaderSizeHint: Int, - websocketRandomFactory: () ⇒ Random, - socketOptions: immutable.Seq[SocketOption], - parserSettings: ParserSettings) - extends akka.http.scaladsl.settings.ClientConnectionSettings { - - require(connectingTimeout >= Duration.Zero, "connectingTimeout must be >= 0") - require(requestHeaderSizeHint > 0, "request-size-hint must be > 0") - - override def productPrefix = "ClientConnectionSettings" -} - -object ClientConnectionSettingsImpl extends SettingsCompanion[ClientConnectionSettingsImpl]("akka.http.client") { - def fromSubConfig(root: Config, inner: Config) = { - val c = inner.withFallback(root.getConfig(prefix)) - new ClientConnectionSettingsImpl( - userAgentHeader = c.getString("user-agent-header").toOption.map(`User-Agent`(_)), - connectingTimeout = c getFiniteDuration "connecting-timeout", - idleTimeout = c getPotentiallyInfiniteDuration "idle-timeout", - requestHeaderSizeHint = c getIntBytes "request-header-size-hint", - websocketRandomFactory = Randoms.SecureRandomInstances, // can currently only be overridden from code - socketOptions = SocketOptionSettings.fromSubConfig(root, c.getConfig("socket-options")), - parserSettings = ParserSettingsImpl.fromSubConfig(root, c.getConfig("parsing"))) - } - -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/settings/ConnectionPoolSettingsImpl.scala b/akka-http-core/src/main/scala/akka/http/impl/settings/ConnectionPoolSettingsImpl.scala deleted file mode 100644 index fadfb63bf0..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/settings/ConnectionPoolSettingsImpl.scala +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright (C) 2016 Lightbend Inc. - */ - -package akka.http.impl.settings - -import akka.http.impl.util.SettingsCompanion -import akka.http.impl.util._ -import akka.http.scaladsl.settings.{ ConnectionPoolSettings, ClientConnectionSettings } -import com.typesafe.config.Config -import scala.concurrent.duration.Duration - -/** INTERNAL API */ -private[akka] final case class ConnectionPoolSettingsImpl( - val maxConnections: Int, - val minConnections: Int, - val maxRetries: Int, - val maxOpenRequests: Int, - val pipeliningLimit: Int, - val idleTimeout: Duration, - val connectionSettings: ClientConnectionSettings) - extends ConnectionPoolSettings { - - require(maxConnections > 0, "max-connections must be > 0") - require(minConnections >= 0, "min-connections must be >= 0") - require(minConnections <= maxConnections, "min-connections must be <= max-connections") - require(maxRetries >= 0, "max-retries must be >= 0") - require(maxOpenRequests > 0 && (maxOpenRequests & (maxOpenRequests - 1)) == 0, "max-open-requests must be a power of 2 > 0") - require(pipeliningLimit > 0, "pipelining-limit must be > 0") - require(idleTimeout >= Duration.Zero, "idle-timeout must be >= 0") - - override def productPrefix = "ConnectionPoolSettings" -} - -object ConnectionPoolSettingsImpl extends SettingsCompanion[ConnectionPoolSettingsImpl]("akka.http.host-connection-pool") { - def fromSubConfig(root: Config, c: Config) = { - ConnectionPoolSettingsImpl( - c getInt "max-connections", - c getInt "min-connections", - c getInt "max-retries", - c getInt "max-open-requests", - c getInt "pipelining-limit", - c getPotentiallyInfiniteDuration "idle-timeout", - ClientConnectionSettingsImpl.fromSubConfig(root, c.getConfig("client"))) - } -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/settings/ConnectionPoolSetup.scala b/akka-http-core/src/main/scala/akka/http/impl/settings/ConnectionPoolSetup.scala deleted file mode 100644 index a7ad0c8c19..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/settings/ConnectionPoolSetup.scala +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (C) 2016 Lightbend Inc. - */ - -package akka.http.impl.settings - -import akka.event.LoggingAdapter -import akka.http.scaladsl.ConnectionContext -import akka.http.scaladsl.settings.ConnectionPoolSettings - -/** INTERNAL API */ -private[akka] final case class ConnectionPoolSetup( - settings: ConnectionPoolSettings, - connectionContext: ConnectionContext = ConnectionContext.noEncryption(), - log: LoggingAdapter) \ No newline at end of file diff --git a/akka-http-core/src/main/scala/akka/http/impl/settings/HostConnectionPoolSetup.scala b/akka-http-core/src/main/scala/akka/http/impl/settings/HostConnectionPoolSetup.scala deleted file mode 100644 index 12df96548c..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/settings/HostConnectionPoolSetup.scala +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Copyright (C) 2016 Lightbend Inc. - */ - -package akka.http.impl.settings - -/** INTERNAL API */ -final case class HostConnectionPoolSetup(host: String, port: Int, setup: ConnectionPoolSetup) - diff --git a/akka-http-core/src/main/scala/akka/http/impl/settings/ParserSettingsImpl.scala b/akka-http-core/src/main/scala/akka/http/impl/settings/ParserSettingsImpl.scala deleted file mode 100644 index 09362ba630..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/settings/ParserSettingsImpl.scala +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Copyright (C) 2009-2014 Lightbend Inc. - */ - -package akka.http.impl.settings - -import akka.http.scaladsl.settings.ParserSettings -import akka.http.scaladsl.settings.ParserSettings.{ IllegalResponseHeaderValueProcessingMode, ErrorLoggingVerbosity, CookieParsingMode } -import akka.stream.impl.ConstantFun -import com.typesafe.config.Config -import scala.collection.JavaConverters._ -import akka.http.scaladsl.model._ -import akka.http.impl.util._ - -/** INTERNAL API */ -private[akka] final case class ParserSettingsImpl( - maxUriLength: Int, - maxMethodLength: Int, - maxResponseReasonLength: Int, - maxHeaderNameLength: Int, - maxHeaderValueLength: Int, - maxHeaderCount: Int, - maxContentLength: Long, - maxChunkExtLength: Int, - maxChunkSize: Int, - uriParsingMode: Uri.ParsingMode, - cookieParsingMode: CookieParsingMode, - illegalHeaderWarnings: Boolean, - errorLoggingVerbosity: ErrorLoggingVerbosity, - illegalResponseHeaderValueProcessingMode: IllegalResponseHeaderValueProcessingMode, - headerValueCacheLimits: Map[String, Int], - includeTlsSessionInfoHeader: Boolean, - customMethods: String ⇒ Option[HttpMethod], - customStatusCodes: Int ⇒ Option[StatusCode], - customMediaTypes: MediaTypes.FindCustom) - extends akka.http.scaladsl.settings.ParserSettings { - - require(maxUriLength > 0, "max-uri-length must be > 0") - require(maxMethodLength > 0, "max-method-length must be > 0") - require(maxResponseReasonLength > 0, "max-response-reason-length must be > 0") - require(maxHeaderNameLength > 0, "max-header-name-length must be > 0") - require(maxHeaderValueLength > 0, "max-header-value-length must be > 0") - require(maxHeaderCount > 0, "max-header-count must be > 0") - require(maxContentLength > 0, "max-content-length must be > 0") - require(maxChunkExtLength > 0, "max-chunk-ext-length must be > 0") - require(maxChunkSize > 0, "max-chunk-size must be > 0") - - override val defaultHeaderValueCacheLimit: Int = headerValueCacheLimits("default") - - override def headerValueCacheLimit(headerName: String): Int = - headerValueCacheLimits.getOrElse(headerName, defaultHeaderValueCacheLimit) - - override def productPrefix = "ParserSettings" -} - -object ParserSettingsImpl extends SettingsCompanion[ParserSettingsImpl]("akka.http.parsing") { - - private[this] val noCustomMethods: String ⇒ Option[HttpMethod] = ConstantFun.scalaAnyToNone - private[this] val noCustomStatusCodes: Int ⇒ Option[StatusCode] = ConstantFun.scalaAnyToNone - private[this] val noCustomMediaTypes: (String, String) ⇒ Option[MediaType] = ConstantFun.scalaAnyTwoToNone - - def fromSubConfig(root: Config, inner: Config) = { - val c = inner.withFallback(root.getConfig(prefix)) - val cacheConfig = c getConfig "header-cache" - - new ParserSettingsImpl( - c getIntBytes "max-uri-length", - c getIntBytes "max-method-length", - c getIntBytes "max-response-reason-length", - c getIntBytes "max-header-name-length", - c getIntBytes "max-header-value-length", - c getIntBytes "max-header-count", - c getPossiblyInfiniteBytes "max-content-length", - c getIntBytes "max-chunk-ext-length", - c getIntBytes "max-chunk-size", - Uri.ParsingMode(c getString "uri-parsing-mode"), - CookieParsingMode(c getString "cookie-parsing-mode"), - c getBoolean "illegal-header-warnings", - ErrorLoggingVerbosity(c getString "error-logging-verbosity"), - IllegalResponseHeaderValueProcessingMode(c getString "illegal-response-header-value-processing-mode"), - cacheConfig.entrySet.asScala.map(kvp ⇒ kvp.getKey → cacheConfig.getInt(kvp.getKey))(collection.breakOut), - c getBoolean "tls-session-info-header", - noCustomMethods, - noCustomStatusCodes, - noCustomMediaTypes) - } - -} - diff --git a/akka-http-core/src/main/scala/akka/http/impl/settings/RoutingSettingsImpl.scala b/akka-http-core/src/main/scala/akka/http/impl/settings/RoutingSettingsImpl.scala deleted file mode 100644 index 8abd0ea50a..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/settings/RoutingSettingsImpl.scala +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2009-2014 Lightbend Inc. - */ - -package akka.http.impl.settings - -import akka.http.impl.util._ -import com.typesafe.config.Config - -/** INTERNAL API */ -final case class RoutingSettingsImpl( - verboseErrorMessages: Boolean, - fileGetConditional: Boolean, - renderVanityFooter: Boolean, - rangeCountLimit: Int, - rangeCoalescingThreshold: Long, - decodeMaxBytesPerChunk: Int, - fileIODispatcher: String) extends akka.http.scaladsl.settings.RoutingSettings { - - override def productPrefix = "RoutingSettings" -} - -object RoutingSettingsImpl extends SettingsCompanion[RoutingSettingsImpl]("akka.http.routing") { - def fromSubConfig(root: Config, c: Config) = new RoutingSettingsImpl( - c getBoolean "verbose-error-messages", - c getBoolean "file-get-conditional", - c getBoolean "render-vanity-footer", - c getInt "range-count-limit", - c getBytes "range-coalescing-threshold", - c getIntBytes "decode-max-bytes-per-chunk", - c getString "file-io-dispatcher") - -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/settings/ServerSettingsImpl.scala b/akka-http-core/src/main/scala/akka/http/impl/settings/ServerSettingsImpl.scala deleted file mode 100644 index 32ee36b9ae..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/settings/ServerSettingsImpl.scala +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright (C) 2016 Lightbend Inc. - */ - -package akka.http.impl.settings - -import java.util.Random - -import akka.http.impl.engine.ws.Randoms -import akka.http.scaladsl.settings.{ ServerSettings, ParserSettings } -import com.typesafe.config.Config - -import scala.language.implicitConversions -import scala.collection.immutable -import scala.concurrent.duration._ - -import akka.http.javadsl.{ settings ⇒ js } -import akka.ConfigurationException -import akka.io.Inet.SocketOption - -import akka.http.impl.util._ - -import akka.http.scaladsl.model.HttpHeader -import akka.http.scaladsl.model.headers.{ Host, Server } - -/** INTERNAL API */ -private[akka] final case class ServerSettingsImpl( - serverHeader: Option[Server], - timeouts: ServerSettings.Timeouts, - maxConnections: Int, - pipeliningLimit: Int, - remoteAddressHeader: Boolean, - rawRequestUriHeader: Boolean, - transparentHeadRequests: Boolean, - verboseErrorMessages: Boolean, - responseHeaderSizeHint: Int, - backlog: Int, - socketOptions: immutable.Seq[SocketOption], - defaultHostHeader: Host, - websocketRandomFactory: () ⇒ Random, - parserSettings: ParserSettings) extends ServerSettings { - - require(0 < maxConnections, "max-connections must be > 0") - require(0 < pipeliningLimit && pipeliningLimit <= 1024, "pipelining-limit must be > 0 and <= 1024") - require(0 < responseHeaderSizeHint, "response-size-hint must be > 0") - require(0 < backlog, "backlog must be > 0") - - override def productPrefix = "ServerSettings" -} - -object ServerSettingsImpl extends SettingsCompanion[ServerSettingsImpl]("akka.http.server") { - implicit def timeoutsShortcut(s: js.ServerSettings): js.ServerSettings.Timeouts = s.getTimeouts - - /** INTERNAL API */ - final case class Timeouts( - idleTimeout: Duration, - requestTimeout: Duration, - bindTimeout: FiniteDuration) extends ServerSettings.Timeouts { - require(idleTimeout > Duration.Zero, "idleTimeout must be infinite or > 0") - require(requestTimeout > Duration.Zero, "requestTimeout must be infinite or > 0") - require(bindTimeout > Duration.Zero, "bindTimeout must be > 0") - } - - def fromSubConfig(root: Config, c: Config) = new ServerSettingsImpl( - c.getString("server-header").toOption.map(Server(_)), - new Timeouts( - c getPotentiallyInfiniteDuration "idle-timeout", - c getPotentiallyInfiniteDuration "request-timeout", - c getFiniteDuration "bind-timeout"), - c getInt "max-connections", - c getInt "pipelining-limit", - c getBoolean "remote-address-header", - c getBoolean "raw-request-uri-header", - c getBoolean "transparent-head-requests", - c getBoolean "verbose-error-messages", - c getIntBytes "response-header-size-hint", - c getInt "backlog", - SocketOptionSettings.fromSubConfig(root, c.getConfig("socket-options")), - defaultHostHeader = - HttpHeader.parse("Host", c getString "default-host-header", ParserSettings(root)) match { - case HttpHeader.ParsingResult.Ok(x: Host, Nil) ⇒ x - case result ⇒ - val info = result.errors.head.withSummary("Configured `default-host-header` is illegal") - throw new ConfigurationException(info.formatPretty) - }, - Randoms.SecureRandomInstances, // can currently only be overridden from code - ParserSettingsImpl.fromSubConfig(root, c.getConfig("parsing"))) - - // def apply(optionalSettings: Option[ServerSettings])(implicit actorRefFactory: ActorRefFactory): ServerSettings = - // optionalSettings getOrElse apply(actorSystem) - -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/util/ByteStringParserInput.scala b/akka-http-core/src/main/scala/akka/http/impl/util/ByteStringParserInput.scala deleted file mode 100644 index bd7b88ddfe..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/util/ByteStringParserInput.scala +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (C) 2015-2016 Lightbend Inc. - */ -package akka.http.impl.util - -import java.nio.charset.StandardCharsets - -import akka.parboiled2.ParserInput.DefaultParserInput -import akka.util.ByteString - -/** - * ParserInput reading directly off a ByteString. (Based on the ByteArrayBasedParserInput) - * This avoids a separate decoding step but assumes that each byte represents exactly one character, - * which is encoded by ISO-8859-1! - * You can therefore use this ParserInput type only if you know that all input will be `ISO-8859-1`-encoded, - * or only contains 7-bit ASCII characters (which is a subset of ISO-8859-1)! - * - * Note that this ParserInput type will NOT work with general `UTF-8`-encoded input as this can contain - * character representations spanning multiple bytes. However, if you know that your input will only ever contain - * 7-bit ASCII characters (0x00-0x7F) then UTF-8 is fine, since the first 127 UTF-8 characters are - * encoded with only one byte that is identical to 7-bit ASCII and ISO-8859-1. - */ -final class ByteStringParserInput(bytes: ByteString) extends DefaultParserInput { - override def charAt(ix: Int): Char = (bytes(ix) & 0xFF).toChar - override def length: Int = bytes.size - override def sliceString(start: Int, end: Int): String = bytes.slice(start, end).decodeString(StandardCharsets.ISO_8859_1) - override def sliceCharArray(start: Int, end: Int): Array[Char] = - StandardCharsets.ISO_8859_1.decode(bytes.slice(start, end).asByteBuffer).array() -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/util/EnhancedByteArray.scala b/akka-http-core/src/main/scala/akka/http/impl/util/EnhancedByteArray.scala deleted file mode 100644 index f7f2b26420..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/util/EnhancedByteArray.scala +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.util - -import scala.annotation.tailrec - -/** - * INTERNAL API - */ -private[http] class EnhancedByteArray(val underlying: Array[Byte]) extends AnyVal { - - /** - * Tests two byte arrays for value equality in a way that defends against timing attacks. - * Simple equality testing will stop at the end of a matching prefix thereby leaking information - * about the length of the matching prefix which can be exploited for per-byte progressive brute-forcing. - * - * @note This function leaks information about the length of each byte array as well as - * whether the two byte arrays have the same length. - * @see [[http://codahale.com/a-lesson-in-timing-attacks/]] - * @see [[http://rdist.root.org/2009/05/28/timing-attack-in-google-keyczar-library/]] - * @see [[http://emerose.com/timing-attacks-explained]] - */ - def secure_==(other: Array[Byte]): Boolean = { - @tailrec def xor(ix: Int = 0, result: Int = 0): Int = - if (ix < underlying.length) xor(ix + 1, result | (underlying(ix) ^ other(ix))) else result - - other.length == underlying.length && xor() == 0 - } -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/util/EnhancedByteStringTraversableOnce.scala b/akka-http-core/src/main/scala/akka/http/impl/util/EnhancedByteStringTraversableOnce.scala deleted file mode 100644 index 52add0eef6..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/util/EnhancedByteStringTraversableOnce.scala +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.util - -import akka.util.ByteString - -/** - * INTERNAL API - */ -private[http] class EnhancedByteStringTraversableOnce(val byteStrings: TraversableOnce[ByteString]) extends AnyVal { - def join: ByteString = byteStrings.foldLeft(ByteString.empty)(_ ++ _) -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/util/EnhancedConfig.scala b/akka-http-core/src/main/scala/akka/http/impl/util/EnhancedConfig.scala deleted file mode 100644 index 4adc9dbef9..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/util/EnhancedConfig.scala +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.util - -import com.typesafe.config.Config -import scala.concurrent.duration.{ FiniteDuration, Duration } -import akka.ConfigurationException - -/** - * INTERNAL API - */ -private[http] class EnhancedConfig(val underlying: Config) extends AnyVal { - - def getPotentiallyInfiniteDuration(path: String): Duration = underlying.getString(path) match { - case "infinite" ⇒ Duration.Inf - case x ⇒ Duration(x) - } - - def getFiniteDuration(path: String): FiniteDuration = Duration(underlying.getString(path)) match { - case x: FiniteDuration ⇒ x - case _ ⇒ throw new ConfigurationException(s"Config setting '$path' must be a finite duration") - } - - def getPossiblyInfiniteInt(path: String): Int = underlying.getString(path) match { - case "infinite" ⇒ Int.MaxValue - case x ⇒ underlying.getInt(path) - } - - def getIntBytes(path: String): Int = { - val value: Long = underlying getBytes path - if (value <= Int.MaxValue) value.toInt - else throw new ConfigurationException(s"Config setting '$path' must not be larger than ${Int.MaxValue}") - } - - def getPossiblyInfiniteIntBytes(path: String): Int = underlying.getString(path) match { - case "infinite" ⇒ Int.MaxValue - case x ⇒ getIntBytes(path) - } - - def getPossiblyInfiniteBytes(path: String): Long = underlying.getString(path) match { - case "infinite" ⇒ Long.MaxValue - case x ⇒ underlying.getBytes(path) - } -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/util/EnhancedRegex.scala b/akka-http-core/src/main/scala/akka/http/impl/util/EnhancedRegex.scala deleted file mode 100644 index d6853738d5..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/util/EnhancedRegex.scala +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.util - -import scala.util.matching.Regex - -/** - * INTERNAL API - */ -private[http] class EnhancedRegex(val regex: Regex) extends AnyVal { - def groupCount = regex.pattern.matcher("").groupCount() -} \ No newline at end of file diff --git a/akka-http-core/src/main/scala/akka/http/impl/util/EnhancedString.scala b/akka-http-core/src/main/scala/akka/http/impl/util/EnhancedString.scala deleted file mode 100644 index e6b511254d..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/util/EnhancedString.scala +++ /dev/null @@ -1,121 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.util - -import java.util.Locale -import scala.annotation.tailrec -import scala.collection.immutable - -/** - * INTERNAL API - */ -private[http] class EnhancedString(val underlying: String) extends AnyVal { - - /** - * Splits the underlying string into the segments that are delimited by the given character. - * The delimiter itself is never a part of any segment. If the string does not contain the - * delimiter the result is a List containing only the underlying string. - * Note that this implementation differs from the original String.split(...) method in that - * leading and trailing delimiters are NOT ignored, i.e. they trigger the inclusion of an - * empty leading or trailing empty string (respectively). - */ - def fastSplit(delimiter: Char): immutable.LinearSeq[String] = { - @tailrec def split(end: Int = underlying.length, elements: List[String] = Nil): List[String] = { - val ix = underlying.lastIndexOf(delimiter, end - 1) - if (ix < 0) - underlying.substring(0, end) :: elements - else - split(ix, underlying.substring(ix + 1, end) :: elements) - } - split() - } - - /** - * Lazily splits the underlying string into the segments that are delimited by the given character. - * Only the segments that are actually accessed are computed. - * The delimiter itself is never a part of any segment. If the string does not contain the - * delimiter the result is a single-element stream containing only the underlying string. - * Note that this implementation differs from the original String.split(...) method in that - * leading and trailing delimiters are NOT ignored, i.e. they trigger the inclusion of an - * empty leading or trailing empty string (respectively). - */ - def lazySplit(delimiter: Char): Stream[String] = { - def split(start: Int = 0): Stream[String] = { - val ix = underlying.indexOf(delimiter, start) - if (ix < 0) - Stream.cons(underlying.substring(start), Stream.Empty) - else - Stream.cons(underlying.substring(start, ix), split(ix + 1)) - } - split() - } - - /** - * Returns Some(String) if the underlying string is non-empty, None otherwise - */ - def toOption: Option[String] = - if ((underlying eq null) || underlying.isEmpty) None else Some(underlying) - - /** - * If the underlying string is null the method returns the empty string, otherwise the underlying string. - */ - def nullAsEmpty: String = - if (underlying eq null) "" else underlying - - /** - * Returns the ASCII encoded bytes of this string. Truncates characters to 8-bit byte value. - */ - def asciiBytes: Array[Byte] = { - val array = new Array[Byte](underlying.length) - getAsciiBytes(array, 0) - array - } - - /** - * Copies the ASCII encoded bytes of this string into the given byte array starting at the `offset` index. - * Truncates characters to 8-bit byte value. - * If the array does not have enough space for the whole string only the portion that fits is copied. - */ - def getAsciiBytes(array: Array[Byte], offset: Int): Unit = { - @tailrec def rec(ix: Int): Unit = - if (ix < array.length) { - array(ix) = underlying.charAt(ix - offset).asInstanceOf[Byte] - rec(ix + 1) - } - rec(offset) - } - - /** - * Tests two string for value equality in a way that defends against timing attacks. - * Simple equality testing will stop at the end of a matching prefix thereby leaking information - * about the length of the matching prefix which can be exploited for per-byte progressive brute-forcing. - * - * @note This function leaks information about the length of each string as well as - * whether the two string have the same length. - * @see [[http://codahale.com/a-lesson-in-timing-attacks/]] - * @see [[http://rdist.root.org/2009/05/28/timing-attack-in-google-keyczar-library/]] - * @see [[http://emerose.com/timing-attacks-explained]] - */ - def secure_==(other: String): Boolean = asciiBytes secure_== other.asciiBytes - - /** - * Determines whether the underlying String starts with the given character. - */ - def startsWith(c: Char): Boolean = underlying.nonEmpty && underlying.charAt(0) == c - - /** - * Determines whether the underlying String ends with the given character. - */ - def endsWith(c: Char): Boolean = underlying.nonEmpty && underlying.charAt(underlying.length - 1) == c - - /** Strips margin and fixes the newline sequence to the given one preventing dependencies on the build platform */ - def stripMarginWithNewline(newline: String) = underlying.stripMargin.replace("\r\n", "\n").replace("\n", newline) - - /** - * Provides a default toLowerCase that doesn't suffer from the dreaded turkish-i problem. - * See http://bugs.java.com/view_bug.do?bug_id=6208680 - */ - def toRootLowerCase: String = underlying.toLowerCase(Locale.ROOT) -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/util/JavaAccessors.scala b/akka-http-core/src/main/scala/akka/http/impl/util/JavaAccessors.scala deleted file mode 100644 index 28dd842c3a..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/util/JavaAccessors.scala +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.util - -import java.io.File -import java.nio.file.Path - -import JavaMapping.Implicits._ -import akka.http.javadsl.model._ -import akka.http.scaladsl.model - -/** - * INTERNAL API - * - * Accessors for constructors with default arguments to be used from the Java implementation - */ -object JavaAccessors { - /** INTERNAL API */ - def HttpRequest(): HttpRequest = model.HttpRequest() - - /** INTERNAL API */ - def HttpRequest(uri: String): HttpRequest = model.HttpRequest(uri = uri) - - /** INTERNAL API */ - def HttpResponse(): HttpResponse = model.HttpResponse() - - /** INTERNAL API */ - def HttpEntity(contentType: ContentType, file: File): UniversalEntity = - model.HttpEntity(contentType.asScala, file) - - /** INTERNAL API */ - def HttpEntity(contentType: ContentType, file: Path): UniversalEntity = - model.HttpEntity.fromPath(contentType.asScala, file) -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/util/JavaMapping.scala b/akka-http-core/src/main/scala/akka/http/impl/util/JavaMapping.scala deleted file mode 100644 index bf22a80c3b..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/util/JavaMapping.scala +++ /dev/null @@ -1,258 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.util - -import java.net.InetAddress -import java.util.Optional -import java.{ util ⇒ ju, lang ⇒ jl } -import akka.japi.Pair -import akka.stream.{ Graph, FlowShape, javadsl, scaladsl } - -import scala.collection.immutable -import scala.compat.java8.OptionConverters -import scala.reflect.ClassTag -import akka.NotUsed -import akka.http.impl.model.{ JavaQuery, JavaUri } -import akka.http.javadsl.{ model ⇒ jm, HttpConnectionContext, ConnectionContext, HttpsConnectionContext } -import akka.http.scaladsl.{ model ⇒ sm } -import akka.http.javadsl.{ settings ⇒ js } - -import akka.http.impl.util.JavaMapping.Implicits._ - -import scala.util.Try - -/** INTERNAL API */ -private[http] trait J2SMapping[J] { - type S - def toScala(javaObject: J): S -} - -/** INTERNAL API */ -private[http] object J2SMapping { - implicit def fromJavaMapping[J](implicit mapping: JavaMapping[J, _]): J2SMapping[J] { type S = mapping.S } = mapping - - implicit def fromJavaSeqMapping[J](implicit mapping: J2SMapping[J]): J2SMapping[Seq[J]] { type S = immutable.Seq[mapping.S] } = - new J2SMapping[Seq[J]] { - type S = immutable.Seq[mapping.S] - def toScala(javaObject: Seq[J]): S = javaObject.map(mapping.toScala(_)).toList - } -} - -/** INTERNAL API */ -private[http] trait S2JMapping[S] { - type J - def toJava(scalaObject: S): J -} - -/** INTERNAL API */ -private[http] object S2JMapping { - implicit def fromScalaMapping[S](implicit mapping: JavaMapping[_, S]): S2JMapping[S] { type J = mapping.J } = mapping -} - -/** INTERNAL API */ -private[http] trait JavaMapping[_J, _S] extends J2SMapping[_J] with S2JMapping[_S] { - type J = _J - type S = _S -} - -/** INTERNAL API */ -private[http] object JavaMapping { - trait AsScala[S] { - def asScala: S - } - trait AsJava[J] { - def asJava: J - } - - def toJava[J, S](s: S)(implicit mapping: JavaMapping[J, S]): J = mapping.toJava(s) - def toScala[J, S](j: J)(implicit mapping: JavaMapping[J, S]): S = mapping.toScala(j) - - object Implicits { - import scala.language.implicitConversions - - implicit def convertToScala[J](j: J)(implicit mapping: J2SMapping[J]): mapping.S = mapping.toScala(j) - implicit def convertSeqToScala[J](j: Seq[J])(implicit mapping: J2SMapping[J]): immutable.Seq[mapping.S] = - j.map(mapping.toScala(_)).toList - - implicit def AddAsScala[J](javaObject: J)(implicit mapping: J2SMapping[J]): AsScala[mapping.S] = new AsScala[mapping.S] { - def asScala = convertToScala(javaObject) - } - implicit def AddAsJava[S](scalaObject: S)(implicit mapping: S2JMapping[S]): AsJava[mapping.J] = new AsJava[mapping.J] { - def asJava = mapping.toJava(scalaObject) - } - } - - /** This trivial mapping isn't enabled by default to prevent it from conflicting with the `Inherited` ones */ - def identity[T]: JavaMapping[T, T] = _identityMapping.asInstanceOf[JavaMapping[T, T]] - private final val _identityMapping = new JavaMapping[Any, Any] { - def toJava(scalaObject: Any): Any = scalaObject - def toScala(javaObject: Any): Any = javaObject - } - - implicit def iterableMapping[_J, _S](implicit mapping: JavaMapping[_J, _S]): JavaMapping[jl.Iterable[_J], immutable.Seq[_S]] = - new JavaMapping[jl.Iterable[_J], immutable.Seq[_S]] { - import collection.JavaConverters._ - - def toJava(scalaObject: immutable.Seq[_S]): jl.Iterable[_J] = scalaObject.map(mapping.toJava).asJavaCollection - def toScala(javaObject: jl.Iterable[_J]): immutable.Seq[_S] = - Implicits.convertSeqToScala(iterableAsScalaIterableConverter(javaObject).asScala.toSeq) - } - implicit def map[K, V]: JavaMapping[ju.Map[K, V], immutable.Map[K, V]] = - new JavaMapping[ju.Map[K, V], immutable.Map[K, V]] { - import scala.collection.JavaConverters._ - def toScala(javaObject: ju.Map[K, V]): immutable.Map[K, V] = javaObject.asScala.toMap - def toJava(scalaObject: immutable.Map[K, V]): ju.Map[K, V] = scalaObject.asJava - } - implicit def option[_J, _S](implicit mapping: JavaMapping[_J, _S]): JavaMapping[Optional[_J], Option[_S]] = - new JavaMapping[Optional[_J], Option[_S]] { - def toScala(javaObject: Optional[_J]): Option[_S] = OptionConverters.toScala(javaObject).map(mapping.toScala) - def toJava(scalaObject: Option[_S]): Optional[_J] = OptionConverters.toJava(scalaObject.map(mapping.toJava)) - } - - implicit def flowMapping[JIn, SIn, JOut, SOut, M](implicit inMapping: JavaMapping[JIn, SIn], outMapping: JavaMapping[JOut, SOut]): JavaMapping[javadsl.Flow[JIn, JOut, M], scaladsl.Flow[SIn, SOut, M]] = - new JavaMapping[javadsl.Flow[JIn, JOut, M], scaladsl.Flow[SIn, SOut, M]] { - def toScala(javaObject: javadsl.Flow[JIn, JOut, M]): S = - scaladsl.Flow[SIn].map(inMapping.toJava).viaMat(javaObject)(scaladsl.Keep.right).map(outMapping.toScala) - def toJava(scalaObject: scaladsl.Flow[SIn, SOut, M]): J = - javadsl.Flow.fromGraph { - scaladsl.Flow[JIn].map(inMapping.toScala).viaMat(scalaObject)(scaladsl.Keep.right).map(outMapping.toJava) - } - } - - implicit def graphFlowMapping[JIn, SIn, JOut, SOut, M](implicit inMapping: JavaMapping[JIn, SIn], outMapping: JavaMapping[JOut, SOut]): JavaMapping[Graph[FlowShape[JIn, JOut], M], Graph[FlowShape[SIn, SOut], M]] = - new JavaMapping[Graph[FlowShape[JIn, JOut], M], Graph[FlowShape[SIn, SOut], M]] { - def toScala(javaObject: Graph[FlowShape[JIn, JOut], M]): S = - scaladsl.Flow[SIn].map(inMapping.toJava).viaMat(javaObject)(scaladsl.Keep.right).map(outMapping.toScala) - def toJava(scalaObject: Graph[FlowShape[SIn, SOut], M]): J = - javadsl.Flow.fromGraph { - scaladsl.Flow[JIn].map(inMapping.toScala).viaMat(scalaObject)(scaladsl.Keep.right).map(outMapping.toJava) - } - } - - def scalaToJavaAdapterFlow[J, S](implicit mapping: JavaMapping[J, S]): scaladsl.Flow[S, J, NotUsed] = - scaladsl.Flow[S].map(mapping.toJava) - def javaToScalaAdapterFlow[J, S](implicit mapping: JavaMapping[J, S]): scaladsl.Flow[J, S, NotUsed] = - scaladsl.Flow[J].map(mapping.toScala) - def adapterBidiFlow[JIn, SIn, SOut, JOut](implicit inMapping: JavaMapping[JIn, SIn], outMapping: JavaMapping[JOut, SOut]): scaladsl.BidiFlow[JIn, SIn, SOut, JOut, NotUsed] = - scaladsl.BidiFlow.fromFlowsMat(javaToScalaAdapterFlow(inMapping), scalaToJavaAdapterFlow(outMapping))(scaladsl.Keep.none) - - implicit def pairMapping[J1, J2, S1, S2](implicit _1Mapping: JavaMapping[J1, S1], _2Mapping: JavaMapping[J2, S2]): JavaMapping[Pair[J1, J2], (S1, S2)] = - new JavaMapping[Pair[J1, J2], (S1, S2)] { - def toJava(scalaObject: (S1, S2)): J = Pair(_1Mapping.toJava(scalaObject._1), _2Mapping.toJava(scalaObject._2)) - def toScala(javaObject: Pair[J1, J2]): (S1, S2) = (_1Mapping.toScala(javaObject.first), _2Mapping.toScala(javaObject.second)) - } - implicit def tryMapping[_J, _S](implicit mapping: JavaMapping[_J, _S]): JavaMapping[Try[_J], Try[_S]] = - new JavaMapping[Try[_J], Try[_S]] { - def toScala(javaObject: Try[_J]): S = javaObject.map(mapping.toScala) - def toJava(scalaObject: Try[_S]): J = scalaObject.map(mapping.toJava) - } - - implicit object StringIdentity extends Identity[String] - - implicit object LongMapping extends JavaMapping[jl.Long, Long] { - def toScala(javaObject: jl.Long): Long = javaObject - def toJava(scalaObject: Long): jl.Long = scalaObject - } - implicit object InetAddressIdentity extends Identity[InetAddress] - - class Identity[T] extends JavaMapping[T, T] { - def toScala(javaObject: T): T = javaObject - def toJava(scalaObject: T): T = scalaObject - } - class Inherited[J <: AnyRef, S <: J](implicit classTag: ClassTag[S]) extends JavaMapping[J, S] { - def toJava(scalaObject: S): J = scalaObject - def toScala(javaObject: J): S = cast[S](javaObject) - } - - implicit object ConnectionContext extends Inherited[ConnectionContext, akka.http.scaladsl.ConnectionContext] - implicit object HttpConnectionContext extends Inherited[HttpConnectionContext, akka.http.scaladsl.HttpConnectionContext] - implicit object HttpsConnectionContext extends Inherited[HttpsConnectionContext, akka.http.scaladsl.HttpsConnectionContext] - - implicit object ClientConnectionSettings extends Inherited[js.ClientConnectionSettings, akka.http.scaladsl.settings.ClientConnectionSettings] - implicit object ConnectionPoolSettings extends Inherited[js.ConnectionPoolSettings, akka.http.scaladsl.settings.ConnectionPoolSettings] - implicit object ParserSettings extends Inherited[js.ParserSettings, akka.http.scaladsl.settings.ParserSettings] - implicit object CookieParsingMode extends Inherited[js.ParserSettings.CookieParsingMode, akka.http.scaladsl.settings.ParserSettings.CookieParsingMode] - implicit object ErrorLoggingVerbosity extends Inherited[js.ParserSettings.ErrorLoggingVerbosity, akka.http.scaladsl.settings.ParserSettings.ErrorLoggingVerbosity] - implicit object ServerSettings extends Inherited[js.ServerSettings, akka.http.scaladsl.settings.ServerSettings] - implicit object ServerSettingsT extends Inherited[js.ServerSettings.Timeouts, akka.http.scaladsl.settings.ServerSettings.Timeouts] - - implicit object DateTime extends Inherited[jm.DateTime, akka.http.scaladsl.model.DateTime] - - implicit object ContentType extends Inherited[jm.ContentType, sm.ContentType] - implicit object ContentTypeBinary extends Inherited[jm.ContentType.Binary, sm.ContentType.Binary] - implicit object ContentTypeNonBinary extends Inherited[jm.ContentType.NonBinary, sm.ContentType.NonBinary] - implicit object ContentTypeWithFixedCharset extends Inherited[jm.ContentType.WithFixedCharset, sm.ContentType.WithFixedCharset] - implicit object ContentTypeWithCharset extends Inherited[jm.ContentType.WithCharset, sm.ContentType.WithCharset] - implicit object ContentTypeRange extends Inherited[jm.ContentTypeRange, sm.ContentTypeRange] - implicit object Host extends Inherited[jm.Host, sm.Uri.Host] - implicit object HttpCharset extends Inherited[jm.HttpCharset, sm.HttpCharset] - implicit object HttpCharsetRange extends Inherited[jm.HttpCharsetRange, sm.HttpCharsetRange] - implicit object HttpEntity extends Inherited[jm.HttpEntity, sm.HttpEntity] - implicit object RequestEntity extends Inherited[jm.RequestEntity, sm.RequestEntity] - implicit object ResponseEntity extends Inherited[jm.ResponseEntity, sm.ResponseEntity] - implicit object HttpHeader extends Inherited[jm.HttpHeader, sm.HttpHeader] - implicit object HttpMethod extends Inherited[jm.HttpMethod, sm.HttpMethod] - implicit object HttpProtocol extends Inherited[jm.HttpProtocol, sm.HttpProtocol] - implicit object HttpRequest extends Inherited[jm.HttpRequest, sm.HttpRequest] - implicit object HttpResponse extends Inherited[jm.HttpResponse, sm.HttpResponse] - implicit object MediaRange extends Inherited[jm.MediaRange, sm.MediaRange] - implicit object MediaType extends Inherited[jm.MediaType, sm.MediaType] - implicit object MediaTypeBinary extends Inherited[jm.MediaType.Binary, sm.MediaType.Binary] - implicit object MediaTypeNonBinary extends Inherited[jm.MediaType.NonBinary, sm.MediaType.NonBinary] - implicit object MediaTypeFixedCharset extends Inherited[jm.MediaType.WithFixedCharset, sm.MediaType.WithFixedCharset] - implicit object MediaTypeOpenCharset extends Inherited[jm.MediaType.WithOpenCharset, sm.MediaType.WithOpenCharset] - implicit object StatusCode extends Inherited[jm.StatusCode, sm.StatusCode] - - implicit object ContentRange extends Inherited[jm.ContentRange, sm.ContentRange] - implicit object RemoteAddress extends Inherited[jm.RemoteAddress, sm.RemoteAddress] - implicit object TransferEncoding extends Inherited[jm.TransferEncoding, sm.TransferEncoding] - - implicit object HostHeader extends Inherited[jm.headers.Host, sm.headers.Host] - implicit object Server extends Inherited[jm.headers.Server, sm.headers.Server] - implicit object ByteRange extends Inherited[jm.headers.ByteRange, sm.headers.ByteRange] - implicit object CacheDirective extends Inherited[jm.headers.CacheDirective, sm.headers.CacheDirective] - implicit object UserAgent extends Inherited[jm.headers.UserAgent, sm.headers.`User-Agent`] - implicit object ContentDispositionType extends Inherited[jm.headers.ContentDispositionType, sm.headers.ContentDispositionType] - implicit object EntityTag extends Inherited[jm.headers.EntityTag, sm.headers.EntityTag] - implicit object EntityTagRange extends Inherited[jm.headers.EntityTagRange, sm.headers.EntityTagRange] - implicit object HttpChallenge extends Inherited[jm.headers.HttpChallenge, sm.headers.HttpChallenge] - implicit object HttpCookie extends Inherited[jm.headers.HttpCookie, sm.headers.HttpCookie] - implicit object HttpCookiePair extends Inherited[jm.headers.HttpCookiePair, sm.headers.HttpCookiePair] - implicit object HttpCredentials extends Inherited[jm.headers.HttpCredentials, sm.headers.HttpCredentials] - implicit object HttpEncoding extends Inherited[jm.headers.HttpEncoding, sm.headers.HttpEncoding] - implicit object HttpEncodingRange extends Inherited[jm.headers.HttpEncodingRange, sm.headers.HttpEncodingRange] - implicit object HttpOrigin extends Inherited[jm.headers.HttpOrigin, sm.headers.HttpOrigin] - implicit object HttpOriginRange extends Inherited[jm.headers.HttpOriginRange, sm.headers.HttpOriginRange] - implicit object Language extends Inherited[jm.headers.Language, sm.headers.Language] - implicit object LanguageRange extends Inherited[jm.headers.LanguageRange, sm.headers.LanguageRange] - implicit object LinkParam extends Inherited[jm.headers.LinkParam, sm.headers.LinkParam] - implicit object LinkValue extends Inherited[jm.headers.LinkValue, sm.headers.LinkValue] - implicit object ProductVersion extends Inherited[jm.headers.ProductVersion, sm.headers.ProductVersion] - implicit object RangeUnit extends Inherited[jm.headers.RangeUnit, sm.headers.RangeUnit] - - implicit object WsMessage extends JavaMapping[jm.ws.Message, sm.ws.Message] { - def toScala(javaObject: J): WsMessage.S = javaObject.asScala - def toJava(scalaObject: S): WsMessage.J = jm.ws.Message.adapt(scalaObject) - } - - implicit object Uri extends JavaMapping[jm.Uri, sm.Uri] { - def toScala(javaObject: J): Uri.S = cast[JavaUri](javaObject).uri - def toJava(scalaObject: S): Uri.J = JavaUri(scalaObject) - } - implicit object UriParsingMode extends Inherited[jm.Uri.ParsingMode, akka.http.scaladsl.model.Uri.ParsingMode] - - implicit object Query extends JavaMapping[jm.Query, sm.Uri.Query] { - def toScala(javaObject: J): Query.S = cast[JavaQuery](javaObject).query - def toJava(scalaObject: S): Query.J = JavaQuery(scalaObject) - } - - private def cast[T](obj: AnyRef)(implicit classTag: ClassTag[T]): T = - try classTag.runtimeClass.cast(obj).asInstanceOf[T] - catch { - case exp: ClassCastException ⇒ - throw new IllegalArgumentException(s"Illegal custom subclass of $classTag. " + - s"Please use only the provided factories in akka.http.javadsl.model.Http") - } -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/util/ObjectRegistry.scala b/akka-http-core/src/main/scala/akka/http/impl/util/ObjectRegistry.scala deleted file mode 100644 index 03dd3fda97..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/util/ObjectRegistry.scala +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.util - -/** - * INTERNAL API - * - * A unsynchronized registry to keep track of singleton instances similar to what - * java.lang.Enum provides. `registry` should therefore only be used inside of singleton constructors. - */ -private[http] trait ObjectRegistry[K, V <: AnyRef] { - private[this] var _registry = Map.empty[K, V] - - protected final def register(key: K, obj: V): obj.type = { - require(!_registry.contains(key), s"ObjectRegistry for ${getClass.getSimpleName} already contains value for $key") - _registry = _registry.updated(key, obj) - obj - } - def getForKey(key: K): Option[V] = _registry.get(key) - - def getForKeyCaseInsensitive(key: String)(implicit conv: String <:< K): Option[V] = - getForKey(conv(key.toRootLowerCase)) -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/util/One2OneBidiFlow.scala b/akka-http-core/src/main/scala/akka/http/impl/util/One2OneBidiFlow.scala deleted file mode 100644 index 8c22fc1a60..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/util/One2OneBidiFlow.scala +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Copyright (C) 2015-2016 Lightbend Inc. - */ -package akka.http.impl.util - -import akka.NotUsed -import akka.stream.scaladsl.BidiFlow - -import scala.util.control.NoStackTrace -import akka.stream._ -import akka.stream.stage.{ OutHandler, InHandler, GraphStageLogic, GraphStage } - -/** - * INTERNAL API - */ -private[http] object One2OneBidiFlow { - - case class UnexpectedOutputException(element: Any) extends RuntimeException(element.toString) with NoStackTrace - case object OutputTruncationException - extends RuntimeException("Inner stream finished before inputs completed. Outputs might have been truncated.") - with NoStackTrace - - /** - * Creates a generic ``BidiFlow`` which verifies that another flow produces exactly one output element per - * input element, at the right time. Specifically it - * - * 1. triggers an ``UnexpectedOutputException`` if the inner flow produces an output element before having - * consumed the respective input element. - * 2. triggers an `OutputTruncationException` if the inner flow completes before having produced an output element - * for every input element. - * 3. triggers an `OutputTruncationException` if the inner flow cancels its inputs before the upstream completes its - * stream of inputs. - * 4. Backpressures the input side if the maximum number of pending output elements has been reached, - * which is given via the ``maxPending`` parameter. You can use -1 to disable this feature. - */ - def apply[I, O](maxPending: Int): BidiFlow[I, I, O, O, NotUsed] = - BidiFlow.fromGraph(new One2OneBidi[I, O](maxPending)) - - /* - * +--------------------+ - * ~> | in toWrapped | ~> - * | | - * <~ | out fromWrapped | <~ - * +--------------------+ - */ - class One2OneBidi[I, O](maxPending: Int) extends GraphStage[BidiShape[I, I, O, O]] { - val in = Inlet[I]("One2OneBidi.in") - val out = Outlet[O]("One2OneBidi.out") - val toWrapped = Outlet[I]("One2OneBidi.toWrapped") - val fromWrapped = Inlet[O]("One2OneBidi.fromWrapped") - - override def initialAttributes = Attributes.name("One2OneBidi") - val shape = BidiShape(in, toWrapped, fromWrapped, out) - - override def toString = "One2OneBidi" - - override def createLogic(effectiveAttributes: Attributes): GraphStageLogic = new GraphStageLogic(shape) { - private var insideWrappedFlow = 0 - private var pullSuppressed = false - - setHandler(in, new InHandler { - override def onPush(): Unit = { - insideWrappedFlow += 1 - push(toWrapped, grab(in)) - } - override def onUpstreamFinish(): Unit = complete(toWrapped) - }) - - setHandler(toWrapped, new OutHandler { - override def onPull(): Unit = - if (insideWrappedFlow < maxPending || maxPending == -1) pull(in) - else pullSuppressed = true - override def onDownstreamFinish(): Unit = cancel(in) - }) - - setHandler(fromWrapped, new InHandler { - override def onPush(): Unit = { - val element = grab(fromWrapped) - if (insideWrappedFlow > 0) { - insideWrappedFlow -= 1 - push(out, element) - if (pullSuppressed) { - pullSuppressed = false - if (!isClosed(in)) pull(in) - } - } else throw new UnexpectedOutputException(element) - } - override def onUpstreamFinish(): Unit = { - if (insideWrappedFlow > 0) throw OutputTruncationException - else completeStage() - } - }) - - setHandler(out, new OutHandler { - override def onPull(): Unit = pull(fromWrapped) - override def onDownstreamFinish(): Unit = cancel(fromWrapped) - }) - } - } -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/util/Rendering.scala b/akka-http-core/src/main/scala/akka/http/impl/util/Rendering.scala deleted file mode 100644 index 944010ccf3..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/util/Rendering.scala +++ /dev/null @@ -1,359 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.util - -import java.nio.CharBuffer -import java.nio.charset.Charset -import java.text.{ DecimalFormatSymbols, DecimalFormat } -import java.util.Locale -import scala.annotation.tailrec -import scala.collection.{ immutable, LinearSeq } -import akka.parboiled2.{ CharPredicate, CharUtils } -import akka.http.impl.model.parser.CharacterClasses -import akka.util.{ ByteStringBuilder, ByteString } - -/** - * INTERNAL API - * - * An entity that can render itself - */ -private[http] trait Renderable { - private[http] def render[R <: Rendering](r: R): r.type -} - -/** - * INTERNAL API - * - * An entity that can render itself and implements toString in terms of its rendering - */ -private[http] trait ToStringRenderable extends Renderable { - override def toString = render(new StringRendering).get -} - -/** - * INTERNAL API - * - * An entity that has a rendered value (like an HttpHeader) - */ -private[http] trait ValueRenderable extends ToStringRenderable { - def value: String = toString -} - -/** - * INTERNAL API - * - * An entity whose rendering result is cached in an unsynchronized and non-volatile lazy. - */ -private[http] trait LazyValueBytesRenderable extends Renderable { - // unsynchronized and non-volatile lazy init, worst case: we init once per core - // which, since instances of derived classes are usually long-lived, is still better - // that a synchronization overhead or even @volatile reads - private[this] var _valueBytes: Array[Byte] = _ - private def valueBytes = - if (_valueBytes != null) _valueBytes else { _valueBytes = value.asciiBytes; _valueBytes } - - def value: String - def render[R <: Rendering](r: R): r.type = r ~~ valueBytes - override def toString = value -} - -/** - * INTERNAL API - * - * An entity whose rendering result is determined eagerly at instantiation (and then is cached). - * Useful for common predefined singleton values. - */ -private[http] trait SingletonValueRenderable extends Product with Renderable { - private[this] val valueBytes = value.asciiBytes - def value = productPrefix - def render[R <: Rendering](r: R): r.type = r ~~ valueBytes -} - -/** - * INTERNAL API - * - * A typeclass for rendering values. - */ -private[http] trait Renderer[-T] { - def render[R <: Rendering](r: R, value: T): r.type -} - -private[http] object Renderer { - implicit object CharRenderer extends Renderer[Char] { - def render[R <: Rendering](r: R, value: Char): r.type = r ~~ value - } - implicit object IntRenderer extends Renderer[Int] { - def render[R <: Rendering](r: R, value: Int): r.type = r ~~ value - } - implicit object StringRenderer extends Renderer[String] { - def render[R <: Rendering](r: R, value: String): r.type = r ~~ value - } - implicit object ByteStringRenderer extends Renderer[ByteString] { - def render[R <: Rendering](r: R, value: ByteString): r.type = r ~~ value - } - implicit object CharsRenderer extends Renderer[Array[Char]] { - def render[R <: Rendering](r: R, value: Array[Char]): r.type = r ~~ value - } - object RenderableRenderer extends Renderer[Renderable] { - def render[R <: Rendering](r: R, value: Renderable): r.type = value.render(r) - } - implicit def renderableRenderer[T <: Renderable]: Renderer[T] = RenderableRenderer - - def optionRenderer[D, T](defaultValue: D)(implicit sRenderer: Renderer[D], tRenderer: Renderer[T]): Renderer[Option[T]] = - new Renderer[Option[T]] { - def render[R <: Rendering](r: R, value: Option[T]): r.type = - if (value.isEmpty) sRenderer.render(r, defaultValue) else tRenderer.render(r, value.get) - } - - def defaultSeqRenderer[T: Renderer] = genericSeqRenderer[Renderable, T](Rendering.`, `, Rendering.Empty) - def seqRenderer[T: Renderer](separator: String = ", ", empty: String = "") = genericSeqRenderer[String, T](separator, empty) - def genericSeqRenderer[S, T](separator: S, empty: S)(implicit sRenderer: Renderer[S], tRenderer: Renderer[T]): Renderer[immutable.Seq[T]] = - new Renderer[immutable.Seq[T]] { - def render[R <: Rendering](r: R, value: immutable.Seq[T]): r.type = { - @tailrec def recI(values: IndexedSeq[T], ix: Int = 0): r.type = - if (ix < values.size) { - if (ix > 0) sRenderer.render(r, separator) - tRenderer.render(r, values(ix)) - recI(values, ix + 1) - } else r - - @tailrec def recL(remaining: LinearSeq[T]): r.type = - if (remaining.nonEmpty) { - if (remaining ne value) sRenderer.render(r, separator) - tRenderer.render(r, remaining.head) - recL(remaining.tail) - } else r - - value match { - case Nil ⇒ r ~~ empty - case x: IndexedSeq[T] ⇒ recI(x) - case x: LinearSeq[T] ⇒ recL(x) - } - } - } -} - -/** - * INTERNAL API - * - * The interface for a rendering sink. Implemented for several serialization targets. - */ -private[http] trait Rendering { - def ~~(ch: Char): this.type - def ~~(bytes: Array[Byte]): this.type - def ~~(bytes: ByteString): this.type - - def ~~(f: Float): this.type = this ~~ Rendering.floatFormat.format(f) - def ~~(d: Double): this.type = this ~~ d.toString - - def ~~(i: Int): this.type = this ~~ i.toLong - - def ~~(l: Long): this.type = if (l != 0) this ~~ CharUtils.signedDecimalChars(l) else this ~~ '0' - - /** - * Renders the given Int in (lower case) hex notation. - */ - def ~~%(i: Int): this.type = this ~~% i.toLong - - /** - * Renders the given Long in (lower case) hex notation. - */ - def ~~%(lng: Long): this.type = - if (lng != 0) { - @tailrec def putChar(shift: Int): this.type = { - this ~~ CharUtils.lowerHexDigit(lng >>> shift) - if (shift > 0) putChar(shift - 4) else this - } - putChar((63 - java.lang.Long.numberOfLeadingZeros(lng)) & 0xFC) - } else this ~~ '0' - - def ~~(string: String): this.type = { - @tailrec def rec(ix: Int = 0): this.type = - if (ix < string.length) { this ~~ string.charAt(ix); rec(ix + 1) } else this - rec() - } - - def ~~(chars: Array[Char]): this.type = { - @tailrec def rec(ix: Int = 0): this.type = - if (ix < chars.length) { this ~~ chars(ix); rec(ix + 1) } else this - rec() - } - - def ~~[T](value: T)(implicit ev: Renderer[T]): this.type = ev.render(this, value) - - /** - * Renders the given string either directly (if it only contains token chars) - * or in double quotes (if it is empty or contains at least one non-token char). - */ - def ~~#(s: String): this.type = - if (s.nonEmpty && CharacterClasses.tchar.matchesAll(s)) this ~~ s else ~~#!(s) - - /** - * Renders the given string in double quotes. - */ - def ~~#!(s: String): this.type = ~~('"').putEscaped(s) ~~ '"' - - def putEscaped(s: String, escape: CharPredicate = Rendering.`\"`, escChar: Char = '\\'): this.type = { - @tailrec def rec(ix: Int = 0): this.type = - if (ix < s.length) { - val c = s.charAt(ix) - if (escape(c)) this ~~ escChar - this ~~ c - rec(ix + 1) - } else this - rec() - } -} - -private[http] object Rendering { - val floatFormat = new DecimalFormat("0.0##", DecimalFormatSymbols.getInstance(Locale.ROOT)) - val `\"` = CharPredicate('\\', '"') - - case object `, ` extends SingletonValueRenderable // default separator - case object Empty extends Renderable { - def render[R <: Rendering](r: R): r.type = r - } - - case object CrLf extends Renderable { - def render[R <: Rendering](r: R): r.type = r ~~ '\r' ~~ '\n' - } -} - -/** - * INTERNAL API - */ -private[http] class StringRendering extends Rendering { - private[this] val sb = new java.lang.StringBuilder - def ~~(ch: Char): this.type = { sb.append(ch); this } - def ~~(bytes: Array[Byte]): this.type = { - @tailrec def rec(ix: Int = 0): this.type = - if (ix < bytes.length) { this ~~ bytes(ix).asInstanceOf[Char]; rec(ix + 1) } else this - rec() - } - def ~~(bytes: ByteString): this.type = this ~~ bytes.toArray[Byte] - def get: String = sb.toString -} - -/** - * INTERNAL API - */ -private[http] class ByteArrayRendering(sizeHint: Int) extends Rendering { - private[this] var array = new Array[Byte](sizeHint) - private[this] var size = 0 - - def get: Array[Byte] = - if (size == array.length) array - else java.util.Arrays.copyOfRange(array, 0, size) - - def ~~(char: Char): this.type = { - val oldSize = growBy(1) - array(oldSize) = char.toByte - this - } - - def ~~(bytes: Array[Byte]): this.type = { - if (bytes.length > 0) { - val oldSize = growBy(bytes.length) - System.arraycopy(bytes, 0, array, oldSize, bytes.length) - } - this - } - - def ~~(bytes: ByteString): this.type = { - if (bytes.length > 0) { - val oldSize = growBy(bytes.length) - bytes.copyToArray(array, oldSize, bytes.length) - } - this - } - - private def growBy(delta: Int): Int = { - val oldSize = size - val neededSize = oldSize.toLong + delta - if (array.length < neededSize) { - require(neededSize < Int.MaxValue, "Cannot create byte array greater than 2GB in size") - val newLen = math.min(math.max(array.length.toLong << 1, neededSize), Int.MaxValue).toInt - val newArray = new Array[Byte](newLen) - System.arraycopy(array, 0, newArray, 0, array.length) - array = newArray - } - size = neededSize.toInt - oldSize - } -} - -/** - * INTERNAL API - */ -private[http] class ByteStringRendering(sizeHint: Int) extends Rendering { - private[this] val builder = new ByteStringBuilder - builder.sizeHint(sizeHint) - - def get: ByteString = builder.result - - def ~~(char: Char): this.type = { - builder += char.toByte - this - } - - def ~~(bytes: Array[Byte]): this.type = { - if (bytes.length > 0) builder.putByteArrayUnsafe(bytes) - this - } - - def ~~(bytes: ByteString): this.type = { - if (bytes.length > 0) builder ++= bytes - this - } -} - -/** - * INTERNAL API - */ -private[http] class CustomCharsetByteStringRendering(nioCharset: Charset, sizeHint: Int) extends Rendering { - private[this] val charBuffer = CharBuffer.allocate(64) - private[this] val builder = new ByteStringBuilder - builder.sizeHint(sizeHint) - - def get: ByteString = { - flushCharBuffer() - builder.result() - } - - def ~~(char: Char): this.type = { - if (!charBuffer.hasRemaining) flushCharBuffer() - charBuffer.put(char) - this - } - - def ~~(bytes: Array[Byte]): this.type = { - if (bytes.length > 0) { - flushCharBuffer() - builder.putByteArrayUnsafe(bytes) - } - this - } - - def ~~(bytes: ByteString): this.type = { - if (bytes.length > 0) { - flushCharBuffer() - builder ++= bytes - } - this - } - - private def flushCharBuffer(): Unit = { - charBuffer.flip() - if (charBuffer.hasRemaining) { - val byteBuffer = nioCharset.encode(charBuffer) - // TODO: optimize by adding another `putByteArrayUnsafe` overload taking an byte array slice - // and thus enabling `builder.putByteArrayUnsafe(byteBuffer.array(), 0, byteBuffer.remaining())` - val bytes = new Array[Byte](byteBuffer.remaining()) - byteBuffer.get(bytes) - builder.putByteArrayUnsafe(bytes) - } - charBuffer.clear() - } -} \ No newline at end of file diff --git a/akka-http-core/src/main/scala/akka/http/impl/util/SettingsCompanion.scala b/akka-http-core/src/main/scala/akka/http/impl/util/SettingsCompanion.scala deleted file mode 100644 index df8b5f2b01..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/util/SettingsCompanion.scala +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.util - -import java.net.{ InetSocketAddress, InetAddress } -import com.typesafe.config.{ ConfigFactory, Config } -import com.typesafe.config.ConfigFactory._ -import scala.util.control.NonFatal -import scala.collection.immutable.ListMap -import scala.collection.JavaConverters._ -import akka.actor.{ ActorRefFactory, ActorSystem } - -/** - * INTERNAL API - */ -private[http] abstract class SettingsCompanion[T](protected val prefix: String) { - private final val MaxCached = 8 - private[this] var cache = ListMap.empty[ActorSystem, T] - - implicit def default(implicit refFactory: ActorRefFactory): T = - apply(actorSystem) - - def apply(system: ActorSystem): T = - // we use and update the cache without any synchronization, - // there are two possible "problems" resulting from this: - // - cache misses of things another thread has already put into the cache, - // in these cases we do double work, but simply accept it - // - cache hits of things another thread has already dropped from the cache, - // in these cases we avoid double work, which is nice - cache.getOrElse(system, { - val settings = apply(system.settings.config) - val c = - if (cache.size < MaxCached) cache - else cache.tail // drop the first (and oldest) cache entry - cache = c.updated(system, settings) - settings - }) - - def apply(configOverrides: String): T = - apply(parseString(configOverrides) - .withFallback(SettingsCompanion.configAdditions) - .withFallback(defaultReference(getClass.getClassLoader))) - - def apply(config: Config): T = - fromSubConfig(config, config getConfig prefix) - - def fromSubConfig(root: Config, c: Config): T -} - -private[http] object SettingsCompanion { - lazy val configAdditions: Config = { - val localHostName = - try new InetSocketAddress(InetAddress.getLocalHost, 80).getHostString - catch { case NonFatal(_) ⇒ "" } - ConfigFactory.parseMap(Map("akka.http.hostname" → localHostName).asJava) - } -} \ No newline at end of file diff --git a/akka-http-core/src/main/scala/akka/http/impl/util/SingletonException.scala b/akka-http-core/src/main/scala/akka/http/impl/util/SingletonException.scala deleted file mode 100644 index a01677026b..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/util/SingletonException.scala +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.util - -import scala.util.control.NoStackTrace - -/** - * INTERNAL API - * - * Convenience base class for exception objects. - */ -private[http] abstract class SingletonException(msg: String) extends RuntimeException(msg) with NoStackTrace { - def this() = this(null) -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/util/SocketOptionSettings.scala b/akka-http-core/src/main/scala/akka/http/impl/util/SocketOptionSettings.scala deleted file mode 100644 index 3314522a91..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/util/SocketOptionSettings.scala +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.util - -import akka.io.{ Tcp, Inet } - -import scala.collection.immutable - -import akka.io.Inet.SocketOption -import com.typesafe.config.Config - -private[http] object SocketOptionSettings { - def fromSubConfig(root: Config, c: Config): immutable.Seq[SocketOption] = { - def so[T](setting: String)(f: (Config, String) ⇒ T)(cons: T ⇒ SocketOption): List[SocketOption] = - c.getString(setting) match { - case "undefined" ⇒ Nil - case x ⇒ cons(f(c, setting)) :: Nil - } - - so("so-receive-buffer-size")(_ getIntBytes _)(Inet.SO.ReceiveBufferSize) ::: - so("so-send-buffer-size")(_ getIntBytes _)(Inet.SO.SendBufferSize) ::: - so("so-reuse-address")(_ getBoolean _)(Inet.SO.ReuseAddress) ::: - so("so-traffic-class")(_ getInt _)(Inet.SO.TrafficClass) ::: - so("tcp-keep-alive")(_ getBoolean _)(Tcp.SO.KeepAlive) ::: - so("tcp-oob-inline")(_ getBoolean _)(Tcp.SO.OOBInline) ::: - so("tcp-no-delay")(_ getBoolean _)(Tcp.SO.TcpNoDelay) - } -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/util/StreamUtils.scala b/akka-http-core/src/main/scala/akka/http/impl/util/StreamUtils.scala deleted file mode 100644 index 1881ebab24..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/util/StreamUtils.scala +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.util - -import java.util.concurrent.atomic.{ AtomicBoolean, AtomicReference } -import akka.NotUsed -import akka.stream._ -import akka.stream.impl.fusing.GraphStages.SimpleLinearGraphStage -import akka.stream.impl.{ PublisherSink, SinkModule, SourceModule } -import akka.stream.scaladsl._ -import akka.stream.stage._ -import akka.util.ByteString -import org.reactivestreams.{ Processor, Publisher, Subscriber, Subscription } - -import scala.concurrent.{ ExecutionContext, Future, Promise } - -/** - * INTERNAL API - */ -private[http] object StreamUtils { - import Attributes.none - - /** - * Creates a transformer that will call `f` for each incoming ByteString and output its result. After the complete - * input has been read it will call `finish` once to determine the final ByteString to post to the output. - * Empty ByteStrings are discarded. - */ - def byteStringTransformer(f: ByteString ⇒ ByteString, finish: () ⇒ ByteString): GraphStage[FlowShape[ByteString, ByteString]] = new SimpleLinearGraphStage[ByteString] { - override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = new GraphStageLogic(shape) with InHandler with OutHandler { - override def onPush(): Unit = { - val data = f(grab(in)) - if (data.nonEmpty) push(out, data) - else pull(in) - } - - override def onPull(): Unit = pull(in) - - override def onUpstreamFinish(): Unit = { - val data = finish() - if (data.nonEmpty) emit(out, data) - completeStage() - } - - setHandlers(in, out, this) - } - } - - def failedPublisher[T](ex: Throwable): Publisher[T] = - impl.ErrorPublisher(ex, "failed").asInstanceOf[Publisher[T]] - - def captureTermination[T, Mat](source: Source[T, Mat]): (Source[T, Mat], Future[Unit]) = { - val promise = Promise[Unit]() - val transformer = new SimpleLinearGraphStage[T] { - override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = new GraphStageLogic(shape) with InHandler with OutHandler { - override def onPush(): Unit = push(out, grab(in)) - - override def onPull(): Unit = pull(in) - - override def onUpstreamFailure(ex: Throwable): Unit = { - promise.tryFailure(ex) - failStage(ex) - } - - override def postStop(): Unit = { - promise.trySuccess(()) - } - - setHandlers(in, out, this) - } - } - source.via(transformer) → promise.future - } - - def sliceBytesTransformer(start: Long, length: Long): Flow[ByteString, ByteString, NotUsed] = { - val transformer = new SimpleLinearGraphStage[ByteString] { - override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = new GraphStageLogic(shape) with InHandler with OutHandler { - override def onPull() = pull(in) - - var toSkip = start - var remaining = length - - override def onPush(): Unit = { - val element = grab(in) - if (toSkip >= element.length) - pull(in) - else { - val data = element.drop(toSkip.toInt).take(math.min(remaining, Int.MaxValue).toInt) - remaining -= data.size - push(out, data) - if (remaining <= 0) completeStage() - } - toSkip -= element.length - } - - setHandlers(in, out, this) - } - } - Flow[ByteString].via(transformer).named("sliceBytes") - } - - def limitByteChunksStage(maxBytesPerChunk: Int): GraphStage[FlowShape[ByteString, ByteString]] = - new SimpleLinearGraphStage[ByteString] { - override def initialAttributes = Attributes.name("limitByteChunksStage") - - override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = new GraphStageLogic(shape) { - - var remaining = ByteString.empty - - def splitAndPush(elem: ByteString): Unit = { - val toPush = remaining.take(maxBytesPerChunk) - val toKeep = remaining.drop(maxBytesPerChunk) - push(out, toPush) - remaining = toKeep - } - setHandlers(in, out, WaitingForData) - - case object WaitingForData extends InHandler with OutHandler { - override def onPush(): Unit = { - val elem = grab(in) - if (elem.size <= maxBytesPerChunk) push(out, elem) - else { - splitAndPush(elem) - setHandlers(in, out, DeliveringData) - } - } - override def onPull(): Unit = pull(in) - } - - case object DeliveringData extends InHandler() with OutHandler { - var finishing = false - override def onPush(): Unit = throw new IllegalStateException("Not expecting data") - override def onPull(): Unit = { - splitAndPush(remaining) - if (remaining.isEmpty) { - if (finishing) completeStage() else setHandlers(in, out, WaitingForData) - } - } - override def onUpstreamFinish(): Unit = if (remaining.isEmpty) completeStage() else finishing = true - } - - override def toString = "limitByteChunksStage" - } - } - - /** - * Returns a source that can only be used once for testing purposes. - */ - def oneTimeSource[T, Mat](other: Source[T, Mat], errorMsg: String = "One time source can only be instantiated once"): Source[T, Mat] = { - val onlyOnceFlag = new AtomicBoolean(false) - other.mapMaterializedValue { elem ⇒ - if (onlyOnceFlag.get() || !onlyOnceFlag.compareAndSet(false, true)) - throw new IllegalStateException(errorMsg) - elem - } - } - - def oneTimePublisherSink[In](cell: OneTimeWriteCell[Publisher[In]], name: String): Sink[In, Publisher[In]] = - new Sink[In, Publisher[In]](new OneTimePublisherSink(none, SinkShape(Inlet(name)), cell)) - def oneTimeSubscriberSource[Out](cell: OneTimeWriteCell[Subscriber[Out]], name: String): Source[Out, Subscriber[Out]] = - new Source[Out, Subscriber[Out]](new OneTimeSubscriberSource(none, SourceShape(Outlet(name)), cell)) - - /** A copy of PublisherSink that allows access to the publisher through the cell but can only materialized once */ - private class OneTimePublisherSink[In](attributes: Attributes, shape: SinkShape[In], cell: OneTimeWriteCell[Publisher[In]]) - extends PublisherSink[In](attributes, shape) { - override def create(context: MaterializationContext): (AnyRef, Publisher[In]) = { - val results = super.create(context) - cell.set(results._2) - results - } - override protected def newInstance(shape: SinkShape[In]): SinkModule[In, Publisher[In]] = - new OneTimePublisherSink[In](attributes, shape, cell) - - override def withAttributes(attr: Attributes): OneTimePublisherSink[In] = - new OneTimePublisherSink[In](attr, amendShape(attr), cell) - } - /** A copy of SubscriberSource that allows access to the subscriber through the cell but can only materialized once */ - private class OneTimeSubscriberSource[Out](val attributes: Attributes, shape: SourceShape[Out], cell: OneTimeWriteCell[Subscriber[Out]]) - extends SourceModule[Out, Subscriber[Out]](shape) { - - override def create(context: MaterializationContext): (Publisher[Out], Subscriber[Out]) = { - val processor = new Processor[Out, Out] { - @volatile private var subscriber: Subscriber[_ >: Out] = null - - override def subscribe(s: Subscriber[_ >: Out]): Unit = subscriber = s - - override def onError(t: Throwable): Unit = subscriber.onError(t) - override def onSubscribe(s: Subscription): Unit = subscriber.onSubscribe(s) - override def onComplete(): Unit = subscriber.onComplete() - override def onNext(t: Out): Unit = subscriber.onNext(t) - } - cell.setValue(processor) - - (processor, processor) - } - - override protected def newInstance(shape: SourceShape[Out]): SourceModule[Out, Subscriber[Out]] = - new OneTimeSubscriberSource[Out](attributes, shape, cell) - override def withAttributes(attr: Attributes): OneTimeSubscriberSource[Out] = - new OneTimeSubscriberSource[Out](attr, amendShape(attr), cell) - } - - trait ReadableCell[+T] { - def value: T - } - /** A one time settable cell */ - class OneTimeWriteCell[T <: AnyRef] extends AtomicReference[T] with ReadableCell[T] { - def value: T = { - val value = get() - require(value != null, "Value wasn't set yet") - value - } - - def setValue(value: T): Unit = - if (!compareAndSet(null.asInstanceOf[T], value)) - throw new IllegalStateException("Value can be only set once.") - } - - /** - * Similar to Source.maybe but doesn't rely on materialization. Can only be used once. - */ - trait OneTimeValve { - def source[T]: Source[T, NotUsed] - def open(): Unit - } - object OneTimeValve { - def apply(): OneTimeValve = new OneTimeValve { - val promise = Promise[Unit]() - val _source = Source.fromFuture(promise.future).drop(1) // we are only interested in the completion event - - def source[T]: Source[T, NotUsed] = _source.asInstanceOf[Source[T, NotUsed]] // safe, because source won't generate any elements - def open(): Unit = promise.success(()) - } - } -} - -/** - * INTERNAL API - */ -private[http] class EnhancedByteStringSource[Mat](val byteStringStream: Source[ByteString, Mat]) extends AnyVal { - def join(implicit materializer: Materializer): Future[ByteString] = - byteStringStream.runFold(ByteString.empty)(_ ++ _) - def utf8String(implicit materializer: Materializer, ec: ExecutionContext): Future[String] = - join.map(_.utf8String) -} diff --git a/akka-http-core/src/main/scala/akka/http/impl/util/Timestamp.scala b/akka-http-core/src/main/scala/akka/http/impl/util/Timestamp.scala deleted file mode 100644 index 83799c674f..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/util/Timestamp.scala +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.util - -import scala.concurrent.duration._ - -/** - * INTERNAL API - * - * Helper for dealing with points in time rather than durations. - * We mark it private[http] because we don't want to support it as public API. - */ -private[http] class Timestamp private (val timestampNanos: Long) extends AnyVal { - - def +(period: Duration): Timestamp = - if (isNever) this - else if (!period.isFinite()) Timestamp.never - else new Timestamp(timestampNanos + period.toNanos) - - def -(other: Timestamp): Duration = - if (isNever) Duration.Inf - else if (other.isNever) Duration.MinusInf - else (timestampNanos - other.timestampNanos).nanos - - def isPast: Boolean = System.nanoTime() >= timestampNanos - def isPast(now: Timestamp): Boolean = now.timestampNanos >= timestampNanos - def isFuture: Boolean = !isPast - - def isFinite: Boolean = timestampNanos < Long.MaxValue - def isNever: Boolean = timestampNanos == Long.MaxValue -} - -private[http] object Timestamp { - def now: Timestamp = new Timestamp(System.nanoTime()) - def never: Timestamp = new Timestamp(Long.MaxValue) - - implicit object Ordering extends Ordering[Timestamp] { - def compare(x: Timestamp, y: Timestamp): Int = math.signum(x.timestampNanos - y.timestampNanos).toInt - } -} \ No newline at end of file diff --git a/akka-http-core/src/main/scala/akka/http/impl/util/package.scala b/akka-http-core/src/main/scala/akka/http/impl/util/package.scala deleted file mode 100644 index c5138f3aba..0000000000 --- a/akka-http-core/src/main/scala/akka/http/impl/util/package.scala +++ /dev/null @@ -1,163 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl - -import akka.NotUsed -import akka.stream.{ Attributes, Outlet, Inlet, FlowShape } - -import language.implicitConversions -import java.nio.charset.Charset -import com.typesafe.config.Config -import akka.stream.scaladsl.{ Flow, Source } -import akka.stream.stage._ -import scala.concurrent.duration.Duration -import scala.concurrent.{ Await, Future } -import scala.reflect.ClassTag -import scala.util.{ Failure, Success } -import scala.util.matching.Regex -import akka.util.ByteString -import akka.actor._ - -package object util { - private[http] val UTF8 = Charset.forName("UTF8") - private[http] val ASCII = Charset.forName("ASCII") - private[http] val ISO88591 = Charset.forName("ISO-8859-1") - - private[http] val EmptyByteArray = Array.empty[Byte] - - private[http] def actorSystem(implicit refFactory: ActorRefFactory): ExtendedActorSystem = - refFactory match { - case x: ActorContext ⇒ actorSystem(x.system) - case x: ExtendedActorSystem ⇒ x - case _ ⇒ throw new IllegalStateException - } - - private[http] implicit def enhanceByteArray(array: Array[Byte]): EnhancedByteArray = new EnhancedByteArray(array) - private[http] implicit def enhanceConfig(config: Config): EnhancedConfig = new EnhancedConfig(config) - private[http] implicit def enhanceString_(s: String): EnhancedString = new EnhancedString(s) - private[http] implicit def enhanceRegex(regex: Regex): EnhancedRegex = new EnhancedRegex(regex) - private[http] implicit def enhanceByteStrings(byteStrings: TraversableOnce[ByteString]): EnhancedByteStringTraversableOnce = - new EnhancedByteStringTraversableOnce(byteStrings) - private[http] implicit def enhanceByteStringsMat[Mat](byteStrings: Source[ByteString, Mat]): EnhancedByteStringSource[Mat] = - new EnhancedByteStringSource(byteStrings) - - private[this] var eventStreamLogger: ActorRef = _ - private[http] def installEventStreamLoggerFor(channel: Class[_])(implicit system: ActorSystem): Unit = { - synchronized { - if (eventStreamLogger == null) - eventStreamLogger = system.actorOf(Props[util.EventStreamLogger]().withDeploy(Deploy.local), name = "event-stream-logger") - } - system.eventStream.subscribe(eventStreamLogger, channel) - } - private[http] def installEventStreamLoggerFor[T](implicit ct: ClassTag[T], system: ActorSystem): Unit = - installEventStreamLoggerFor(ct.runtimeClass) - - private[http] implicit class AddFutureAwaitResult[T](future: Future[T]) { - /** "Safe" Await.result that doesn't throw away half of the stacktrace */ - def awaitResult(atMost: Duration): T = { - Await.ready(future, atMost) - future.value.get match { - case Success(t) ⇒ t - case Failure(ex) ⇒ throw new RuntimeException("Trying to await result of failed Future, see the cause for the original problem.", ex) - } - } - } - - private[http] def humanReadableByteCount(bytes: Long, si: Boolean): String = { - val unit = if (si) 1000 else 1024 - if (bytes >= unit) { - val exp = (math.log(bytes) / math.log(unit)).toInt - val pre = if (si) "kMGTPE".charAt(exp - 1).toString else "KMGTPE".charAt(exp - 1).toString + 'i' - "%.1f %sB" format (bytes / math.pow(unit, exp), pre) - } else bytes.toString + " B" - } -} - -package util { - - import akka.http.scaladsl.model.{ ContentType, HttpEntity } - import akka.stream.impl.fusing.GraphStages.SimpleLinearGraphStage - import akka.stream.{ Attributes, Outlet, Inlet, FlowShape } - import scala.concurrent.duration.FiniteDuration - - /** - * Maps error with the provided function if it is defined for an error or, otherwise, passes it on unchanged. - */ - private[http] final case class MapError[T](f: PartialFunction[Throwable, Throwable]) extends SimpleLinearGraphStage[T] { - override def createLogic(attr: Attributes) = - new GraphStageLogic(shape) with InHandler with OutHandler { - override def onPush(): Unit = push(out, grab(in)) - - override def onUpstreamFailure(ex: Throwable): Unit = - if (f.isDefinedAt(ex)) super.onUpstreamFailure(f(ex)) - else super.onUpstreamFailure(ex) - - override def onPull(): Unit = pull(in) - - setHandlers(in, out, this) - } - } - - private[http] class ToStrict(timeout: FiniteDuration, contentType: ContentType) - extends GraphStage[FlowShape[ByteString, HttpEntity.Strict]] { - - val in = Inlet[ByteString]("in") - val out = Outlet[HttpEntity.Strict]("out") - - override def initialAttributes = Attributes.name("ToStrict") - - override val shape = FlowShape(in, out) - - override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = new TimerGraphStageLogic(shape) { - val bytes = ByteString.newBuilder - private var emptyStream = false - - override def preStart(): Unit = scheduleOnce("ToStrictTimeoutTimer", timeout) - - setHandler(out, new OutHandler { - override def onPull(): Unit = { - if (emptyStream) { - push(out, HttpEntity.Strict(contentType, ByteString.empty)) - completeStage() - } else pull(in) - } - }) - - setHandler(in, new InHandler { - override def onPush(): Unit = { - bytes ++= grab(in) - pull(in) - } - override def onUpstreamFinish(): Unit = { - if (isAvailable(out)) { - push(out, HttpEntity.Strict(contentType, bytes.result())) - completeStage() - } else emptyStream = true - } - }) - - override def onTimer(key: Any): Unit = - failStage(new java.util.concurrent.TimeoutException( - s"HttpEntity.toStrict timed out after $timeout while still waiting for outstanding data")) - } - - override def toString = "ToStrict" - } - - private[http] class EventStreamLogger extends Actor with ActorLogging { - def receive = { case x ⇒ log.warning(x.toString) } - } - - private[http] trait LogMessages extends ActorLogging { this: Actor ⇒ - def logMessages(mark: String = "")(r: Receive): Receive = - new Receive { - def isDefinedAt(x: Any): Boolean = r.isDefinedAt(x) - def apply(x: Any): Unit = { - log.debug(s"[$mark] received: $x") - r(x) - } - } - } -} diff --git a/akka-http-core/src/main/scala/akka/http/javadsl/ConnectHttp.scala b/akka-http-core/src/main/scala/akka/http/javadsl/ConnectHttp.scala deleted file mode 100644 index 246aa01849..0000000000 --- a/akka-http-core/src/main/scala/akka/http/javadsl/ConnectHttp.scala +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2016 Lightbend Inc. - */ -package akka.http.javadsl - -import java.util.Locale -import java.util.Optional - -import akka.http.javadsl.model.Uri - -abstract class ConnectHttp { - def host: String - def port: Int - - def isHttps: Boolean - def connectionContext: Optional[HttpsConnectionContext] - - final def effectiveHttpsConnectionContext(fallbackContext: HttpsConnectionContext): HttpsConnectionContext = - connectionContext.orElse(fallbackContext) - - final def effectiveConnectionContext(fallbackContext: ConnectionContext): ConnectionContext = - if (connectionContext.isPresent) connectionContext.get() - else fallbackContext - - override def toString = s"ConnectHttp($host,$port,$isHttps,$connectionContext)" -} - -object ConnectHttp { - - // TODO may be optimised a bit to avoid parsing the Uri entirely for the known port cases - - /** Extracts host data from given Uri. */ - def toHost(uriHost: Uri): ConnectHttp = { - val s = uriHost.scheme.toLowerCase(Locale.ROOT) - if (s == "https") new ConnectHttpsImpl(uriHost.host.address, effectivePort(s, uriHost.port)) - else new ConnectHttpImpl(uriHost.host.address, effectivePort(s, uriHost.port)) - } - - def toHost(host: String): ConnectHttp = { - if (isHttpOrHttps(host)) toHost(Uri.create(host)) - else toHost(Uri.create(s"http://$host")) - } - - def toHost(host: String, port: Int): ConnectHttp = { - require(port > 0, "port must be > 0") - val start = if (isHttpOrHttps(host)) host else s"http://$host" - toHost(Uri.create(start).port(port)) - } - - /** - * Extracts host data from given Uri. - * Forces an HTTPS connection to the given host, using the default HTTPS context and default port. - */ - @throws(classOf[IllegalArgumentException]) - def toHostHttps(uriHost: Uri): ConnectWithHttps = { - val s = uriHost.scheme.toLowerCase(Locale.ROOT) - require(s == "" || s == "https", "toHostHttps used with non https scheme! Was: " + uriHost) - val httpsHost = uriHost.scheme("https") // for effective port calculation - new ConnectHttpsImpl(httpsHost.host.address, effectivePort(uriHost)) - } - - /** Forces an HTTPS connection to the given host, using the default HTTPS context and default port. */ - @throws(classOf[IllegalArgumentException]) - def toHostHttps(host: String): ConnectWithHttps = - toHostHttps(Uri.create(host)) - - /** Forces an HTTPS connection to the given host, using the default HTTPS context and given port. */ - @throws(classOf[IllegalArgumentException]) - def toHostHttps(host: String, port: Int): ConnectWithHttps = { - require(port > 0, "port must be > 0") - val start = if (isHttpOrHttps(host)) host else s"https://$host" - toHostHttps(Uri.create(s"$start").port(port)) - } - - private def isHttpOrHttps(s: String) = s.startsWith("http://") || s.startsWith("https://") - - private def effectivePort(uri: Uri): Int = { - val s = uri.scheme.toLowerCase(Locale.ROOT) - effectivePort(s, uri.port) - } - - private def effectivePort(scheme: String, port: Int): Int = { - val s = scheme.toLowerCase(Locale.ROOT) - if (port > 0) port - else if (s == "https" || s == "wss") 443 - else if (s == "http" || s == "ws") 80 - else throw new IllegalArgumentException("Scheme is not http/https/ws/wss and no port given!") - } - -} - -abstract class ConnectWithHttps extends ConnectHttp { - def withCustomHttpsContext(context: HttpsConnectionContext): ConnectWithHttps - def withDefaultHttpsContext(): ConnectWithHttps -} - -/** INTERNAL API */ -final class ConnectHttpImpl(val host: String, val port: Int) extends ConnectHttp { - def isHttps: Boolean = false - - def connectionContext: Optional[HttpsConnectionContext] = Optional.empty() -} - -final class ConnectHttpsImpl(val host: String, val port: Int, val context: Optional[HttpsConnectionContext] = Optional.empty()) - extends ConnectWithHttps { - - override def isHttps: Boolean = true - - override def withCustomHttpsContext(context: HttpsConnectionContext): ConnectWithHttps = - new ConnectHttpsImpl(host, port, Optional.of(context)) - - override def withDefaultHttpsContext(): ConnectWithHttps = - new ConnectHttpsImpl(host, port, Optional.empty()) - - override def connectionContext: Optional[HttpsConnectionContext] = context - -} diff --git a/akka-http-core/src/main/scala/akka/http/javadsl/ConnectionContext.scala b/akka-http-core/src/main/scala/akka/http/javadsl/ConnectionContext.scala deleted file mode 100644 index ff76145bf4..0000000000 --- a/akka-http-core/src/main/scala/akka/http/javadsl/ConnectionContext.scala +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Copyright (C) 2016 Lightbend Inc. - */ -package akka.http.javadsl - -import java.util.{ Collection ⇒ JCollection, Optional } -import javax.net.ssl.{ SSLContext, SSLParameters } -import akka.http.scaladsl -import akka.japi.Util -import akka.stream.TLSClientAuth -import akka.http.impl.util.JavaMapping.Implicits._ -import com.typesafe.sslconfig.akka.AkkaSSLConfig - -import scala.compat.java8.OptionConverters -import scala.collection.JavaConverters._ - -object ConnectionContext { - //#https-context-creation - // ConnectionContext - /** Used to serve HTTPS traffic. */ - def https(sslContext: SSLContext): HttpsConnectionContext = - scaladsl.ConnectionContext.https(sslContext) - - /** Used to serve HTTPS traffic. */ - def https( - sslContext: SSLContext, - sslConfig: Optional[AkkaSSLConfig], - enabledCipherSuites: Optional[JCollection[String]], - enabledProtocols: Optional[JCollection[String]], - clientAuth: Optional[TLSClientAuth], - sslParameters: Optional[SSLParameters]) = - scaladsl.ConnectionContext.https( - sslContext, - OptionConverters.toScala(sslConfig), - OptionConverters.toScala(enabledCipherSuites).map(Util.immutableSeq(_)), - OptionConverters.toScala(enabledProtocols).map(Util.immutableSeq(_)), - OptionConverters.toScala(clientAuth), - OptionConverters.toScala(sslParameters)) - //#https-context-creation - - /** Used to serve HTTPS traffic. */ - // for binary-compatibility, since 2.4.7 - def https( - sslContext: SSLContext, - enabledCipherSuites: Optional[JCollection[String]], - enabledProtocols: Optional[JCollection[String]], - clientAuth: Optional[TLSClientAuth], - sslParameters: Optional[SSLParameters]) = - scaladsl.ConnectionContext.https( - sslContext, - OptionConverters.toScala(enabledCipherSuites).map(Util.immutableSeq(_)), - OptionConverters.toScala(enabledProtocols).map(Util.immutableSeq(_)), - OptionConverters.toScala(clientAuth), - OptionConverters.toScala(sslParameters)) - - /** Used to serve HTTP traffic. */ - def noEncryption(): HttpConnectionContext = - scaladsl.ConnectionContext.noEncryption() -} - -abstract class ConnectionContext { - def isSecure: Boolean - /** Java API */ - def getDefaultPort: Int - def sslConfig: Option[AkkaSSLConfig] -} - -abstract class HttpConnectionContext extends akka.http.javadsl.ConnectionContext { - override final def isSecure = false - override final def getDefaultPort = 80 - override def sslConfig: Option[AkkaSSLConfig] = None -} - -abstract class HttpsConnectionContext extends akka.http.javadsl.ConnectionContext { - override final def isSecure = true - override final def getDefaultPort = 443 - - /** Java API */ - def getEnabledCipherSuites: Optional[JCollection[String]] - /** Java API */ - def getEnabledProtocols: Optional[JCollection[String]] - /** Java API */ - def getClientAuth: Optional[TLSClientAuth] - - /** Java API */ - def getSslContext: SSLContext - /** Java API */ - def getSslParameters: Optional[SSLParameters] -} diff --git a/akka-http-core/src/main/scala/akka/http/javadsl/HostConnectionPool.scala b/akka-http-core/src/main/scala/akka/http/javadsl/HostConnectionPool.scala deleted file mode 100644 index fb95fb0a93..0000000000 --- a/akka-http-core/src/main/scala/akka/http/javadsl/HostConnectionPool.scala +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl - -import java.util.concurrent.CompletionStage - -import akka.Done -import akka.http.impl.settings.HostConnectionPoolSetup - -import scala.concurrent.ExecutionContextExecutor - -abstract class HostConnectionPool private[http] { - def setup: HostConnectionPoolSetup - - /** - * Asynchronously triggers the shutdown of the host connection pool. - * - * The produced [[CompletionStage]] is fulfilled when the shutdown has been completed. - */ - def shutdown(ec: ExecutionContextExecutor): CompletionStage[Done] - -} diff --git a/akka-http-core/src/main/scala/akka/http/javadsl/Http.scala b/akka-http-core/src/main/scala/akka/http/javadsl/Http.scala deleted file mode 100644 index 6bb2e1511c..0000000000 --- a/akka-http-core/src/main/scala/akka/http/javadsl/Http.scala +++ /dev/null @@ -1,747 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl - -import java.net.InetSocketAddress -import java.util.Optional -import akka.http.impl.util.JavaMapping -import akka.http.impl.util.JavaMapping.HttpsConnectionContext -import akka.http.javadsl.model.ws._ -import akka.http.javadsl.settings.{ ConnectionPoolSettings, ClientConnectionSettings, ServerSettings } -import akka.{ NotUsed, stream } -import akka.stream.TLSProtocol._ -import com.typesafe.sslconfig.akka.AkkaSSLConfig -import scala.concurrent.Future -import scala.util.Try -import akka.stream.scaladsl.Keep -import akka.japi.{ Pair, Function } -import akka.actor.{ ExtendedActorSystem, ActorSystem, ExtensionIdProvider, ExtensionId } -import akka.event.LoggingAdapter -import akka.stream.Materializer -import akka.stream.javadsl.{ BidiFlow, Flow, Source } -import akka.http.impl.util.JavaMapping.Implicits._ -import akka.http.scaladsl.{ model ⇒ sm } -import akka.http.javadsl.model._ -import akka.http._ -import scala.compat.java8.OptionConverters._ -import scala.compat.java8.FutureConverters._ -import java.util.concurrent.CompletionStage - -object Http extends ExtensionId[Http] with ExtensionIdProvider { - override def get(system: ActorSystem): Http = super.get(system) - def lookup() = Http - def createExtension(system: ExtendedActorSystem): Http = new Http(system) -} - -class Http(system: ExtendedActorSystem) extends akka.actor.Extension { - import akka.dispatch.ExecutionContexts.{ sameThreadExecutionContext ⇒ ec } - - import language.implicitConversions - private implicit def completionStageCovariant[T, U >: T](in: CompletionStage[T]): CompletionStage[U] = in.asInstanceOf[CompletionStage[U]] - private implicit def javaModelIsScalaModel[J <: AnyRef, S <: J](in: Future[J])(implicit ev: JavaMapping.Inherited[J, S]): Future[S] = in.asInstanceOf[Future[S]] - - private lazy val delegate = akka.http.scaladsl.Http(system) - - /** - * Constructs a server layer stage using the configured default [[akka.http.javadsl.settings.ServerSettings]]. The returned [[BidiFlow]] isn't - * reusable and can only be materialized once. - */ - def serverLayer(materializer: Materializer): BidiFlow[HttpResponse, SslTlsOutbound, SslTlsInbound, HttpRequest, NotUsed] = - adaptServerLayer(delegate.serverLayer()(materializer)) - - /** - * Constructs a server layer stage using the given [[akka.http.javadsl.settings.ServerSettings]]. The returned [[BidiFlow]] isn't reusable and - * can only be materialized once. - */ - def serverLayer( - settings: ServerSettings, - materializer: Materializer): BidiFlow[HttpResponse, SslTlsOutbound, SslTlsInbound, HttpRequest, NotUsed] = - adaptServerLayer(delegate.serverLayer(settings.asScala)(materializer)) - - /** - * Constructs a server layer stage using the given [[akka.http.javadsl.settings.ServerSettings]]. The returned [[BidiFlow]] isn't reusable and - * can only be materialized once. The `remoteAddress`, if provided, will be added as a header to each [[HttpRequest]] - * this layer produces if the `akka.http.server.remote-address-header` configuration option is enabled. - */ - def serverLayer( - settings: ServerSettings, - remoteAddress: Optional[InetSocketAddress], - materializer: Materializer): BidiFlow[HttpResponse, SslTlsOutbound, SslTlsInbound, HttpRequest, NotUsed] = - adaptServerLayer(delegate.serverLayer(settings.asScala, remoteAddress.asScala)(materializer)) - - /** - * Constructs a server layer stage using the given [[ServerSettings]]. The returned [[BidiFlow]] isn't reusable and - * can only be materialized once. The remoteAddress, if provided, will be added as a header to each [[HttpRequest]] - * this layer produces if the `akka.http.server.remote-address-header` configuration option is enabled. - */ - def serverLayer( - settings: ServerSettings, - remoteAddress: Optional[InetSocketAddress], - log: LoggingAdapter, - materializer: Materializer): BidiFlow[HttpResponse, SslTlsOutbound, SslTlsInbound, HttpRequest, NotUsed] = - adaptServerLayer(delegate.serverLayer(settings.asScala, remoteAddress.asScala, log)(materializer)) - - /** - * Creates a [[Source]] of [[IncomingConnection]] instances which represents a prospective HTTP server binding - * on the given `endpoint`. - * - * If the given port is 0 the resulting source can be materialized several times. Each materialization will - * then be assigned a new local port by the operating system, which can then be retrieved by the materialized - * [[ServerBinding]]. - * - * If the given port is non-zero subsequent materialization attempts of the produced source will immediately - * fail, unless the first materialization has already been unbound. Unbinding can be triggered via the materialized - * [[ServerBinding]]. - * - * The server will be bound using HTTPS if the [[ConnectHttp]] object is configured with an [[HttpsConnectionContext]], - * or the [[defaultServerHttpContext]] has been configured to be an [[HttpsConnectionContext]]. - */ - def bind(connect: ConnectHttp, materializer: Materializer): Source[IncomingConnection, CompletionStage[ServerBinding]] = { - val connectionContext = connect.effectiveConnectionContext(defaultServerHttpContext).asScala - new Source(delegate.bind(connect.host, connect.port, connectionContext)(materializer) - .map(new IncomingConnection(_)) - .mapMaterializedValue(_.map(new ServerBinding(_))(ec).toJava)) - } - - /** - * Creates a [[Source]] of [[IncomingConnection]] instances which represents a prospective HTTP server binding - * on the given `endpoint`. - * - * If the given port is 0 the resulting source can be materialized several times. Each materialization will - * then be assigned a new local port by the operating system, which can then be retrieved by the materialized - * [[ServerBinding]]. - * - * If the given port is non-zero subsequent materialization attempts of the produced source will immediately - * fail, unless the first materialization has already been unbound. Unbinding can be triggered via the materialized - * [[ServerBinding]]. - * - * The server will be bound using HTTPS if the [[ConnectHttp]] object is configured with an [[HttpsConnectionContext]], - * or the [[defaultServerHttpContext]] has been configured to be an [[HttpsConnectionContext]]. - */ - def bind( - connect: ConnectHttp, - settings: ServerSettings, - materializer: Materializer): Source[IncomingConnection, CompletionStage[ServerBinding]] = { - val connectionContext = connect.effectiveConnectionContext(defaultServerHttpContext).asScala - new Source(delegate.bind(connect.host, connect.port, settings = settings.asScala, connectionContext = connectionContext)(materializer) - .map(new IncomingConnection(_)) - .mapMaterializedValue(_.map(new ServerBinding(_))(ec).toJava)) - } - - /** - * Creates a [[Source]] of [[IncomingConnection]] instances which represents a prospective HTTP server binding - * on the given `endpoint`. - * - * If the given port is 0 the resulting source can be materialized several times. Each materialization will - * then be assigned a new local port by the operating system, which can then be retrieved by the materialized - * [[ServerBinding]]. - * - * If the given port is non-zero subsequent materialization attempts of the produced source will immediately - * fail, unless the first materialization has already been unbound. Unbinding can be triggered via the materialized - * [[ServerBinding]]. - * - * The server will be bound using HTTPS if the [[ConnectHttp]] object is configured with an [[HttpsConnectionContext]], - * or the [[defaultServerHttpContext]] has been configured to be an [[HttpsConnectionContext]]. - */ - def bind( - connect: ConnectHttp, - settings: ServerSettings, - log: LoggingAdapter, - materializer: Materializer): Source[IncomingConnection, CompletionStage[ServerBinding]] = { - val connectionContext = connect.effectiveConnectionContext(defaultServerHttpContext).asScala - new Source(delegate.bind(connect.host, connect.port, connectionContext, settings.asScala, log)(materializer) - .map(new IncomingConnection(_)) - .mapMaterializedValue(_.map(new ServerBinding(_))(ec).toJava)) - } - - /** - * Convenience method which starts a new HTTP server at the given endpoint and uses the given `handler` - * [[Flow]] for processing all incoming connections. - * - * The number of concurrently accepted connections can be configured by overriding - * the `akka.http.server.max-connections` setting. - * - * The server will be bound using HTTPS if the [[ConnectHttp]] object is configured with an [[HttpsConnectionContext]], - * or the [[defaultServerHttpContext]] has been configured to be an [[HttpsConnectionContext]]. - */ - def bindAndHandle( - handler: Flow[HttpRequest, HttpResponse, _], - connect: ConnectHttp, - materializer: Materializer): CompletionStage[ServerBinding] = { - val connectionContext = connect.effectiveConnectionContext(defaultServerHttpContext).asScala - delegate.bindAndHandle( - handler.asInstanceOf[Flow[sm.HttpRequest, sm.HttpResponse, _]].asScala, - connect.host, connect.port, connectionContext)(materializer) - .map(new ServerBinding(_))(ec).toJava - } - - /** - * Convenience method which starts a new HTTP server at the given endpoint and uses the given `handler` - * [[Flow]] for processing all incoming connections. - * - * The number of concurrently accepted connections can be configured by overriding - * the `akka.http.server.max-connections` setting. - * - * The server will be bound using HTTPS if the [[ConnectHttp]] object is configured with an [[HttpsConnectionContext]], - * or the [[defaultServerHttpContext]] has been configured to be an [[HttpsConnectionContext]]. - */ - def bindAndHandle( - handler: Flow[HttpRequest, HttpResponse, _], - connect: ConnectHttp, - settings: ServerSettings, - log: LoggingAdapter, - materializer: Materializer): CompletionStage[ServerBinding] = { - val connectionContext = connect.effectiveConnectionContext(defaultServerHttpContext).asScala - delegate.bindAndHandle( - handler.asInstanceOf[Flow[sm.HttpRequest, sm.HttpResponse, _]].asScala, - connect.host, connect.port, connectionContext, settings.asScala, log)(materializer) - .map(new ServerBinding(_))(ec).toJava - } - - /** - * Convenience method which starts a new HTTP server at the given endpoint and uses the given `handler` - * [[Flow]] for processing all incoming connections. - * - * The number of concurrently accepted connections can be configured by overriding - * the `akka.http.server.max-connections` setting. - * - * The server will be bound using HTTPS if the [[ConnectHttp]] object is configured with an [[HttpsConnectionContext]], - * or the [[defaultServerHttpContext]] has been configured to be an [[HttpsConnectionContext]]. - */ - def bindAndHandleSync( - handler: Function[HttpRequest, HttpResponse], - connect: ConnectHttp, - materializer: Materializer): CompletionStage[ServerBinding] = { - val connectionContext = connect.effectiveConnectionContext(defaultServerHttpContext).asScala - delegate.bindAndHandleSync(handler.apply(_).asScala, connect.host, connect.port, connectionContext)(materializer) - .map(new ServerBinding(_))(ec).toJava - } - - /** - * Convenience method which starts a new HTTP server at the given endpoint and uses the given `handler` - * [[Flow]] for processing all incoming connections. - * - * The number of concurrently accepted connections can be configured by overriding - * the `akka.http.server.max-connections` setting. - * - * The server will be bound using HTTPS if the [[ConnectHttp]] object is configured with an [[HttpsConnectionContext]], - * or the [[defaultServerHttpContext]] has been configured to be an [[HttpsConnectionContext]]. - */ - def bindAndHandleSync( - handler: Function[HttpRequest, HttpResponse], - connect: ConnectHttp, - settings: ServerSettings, - log: LoggingAdapter, - materializer: Materializer): CompletionStage[ServerBinding] = { - val connectionContext = connect.effectiveConnectionContext(defaultServerHttpContext).asScala - delegate.bindAndHandleSync( - handler.apply(_).asScala, - connect.host, connect.port, connectionContext, settings.asScala, log)(materializer) - .map(new ServerBinding(_))(ec).toJava - } - - /** - * Convenience method which starts a new HTTP server at the given endpoint and uses the given `handler` - * [[Flow]] for processing all incoming connections. - * - * The number of concurrently accepted connections can be configured by overriding - * the `akka.http.server.max-connections` setting. - * - * The server will be bound using HTTPS if the [[ConnectHttp]] object is configured with an [[HttpsConnectionContext]], - * or the [[defaultServerHttpContext]] has been configured to be an [[HttpsConnectionContext]]. - */ - def bindAndHandleAsync( - handler: Function[HttpRequest, CompletionStage[HttpResponse]], - connect: ConnectHttp, - materializer: Materializer): CompletionStage[ServerBinding] = { - val connectionContext = connect.effectiveConnectionContext(defaultServerHttpContext).asScala - delegate.bindAndHandleAsync(handler.apply(_).toScala, connect.host, connect.port, connectionContext)(materializer) - .map(new ServerBinding(_))(ec).toJava - } - - /** - * Convenience method which starts a new HTTP server at the given endpoint and uses the given `handler` - * [[Flow]] for processing all incoming connections. - * - * The number of concurrently accepted connections can be configured by overriding - * the `akka.http.server.max-connections` setting. - * - * The server will be bound using HTTPS if the [[ConnectHttp]] object is configured with an [[HttpsConnectionContext]], - * or the [[defaultServerHttpContext]] has been configured to be an [[HttpsConnectionContext]]. - */ - def bindAndHandleAsync( - handler: Function[HttpRequest, CompletionStage[HttpResponse]], - connect: ConnectHttp, - settings: ServerSettings, - parallelism: Int, log: LoggingAdapter, - materializer: Materializer): CompletionStage[ServerBinding] = { - val connectionContext = connect.effectiveConnectionContext(defaultServerHttpContext).asScala - delegate.bindAndHandleAsync( - handler.apply(_).toScala, - connect.host, connect.port, connectionContext, settings.asScala, parallelism, log)(materializer) - .map(new ServerBinding(_))(ec).toJava - } - - /** - * Constructs a client layer stage using the configured default [[akka.http.javadsl.settings.ClientConnectionSettings]]. - */ - def clientLayer(hostHeader: headers.Host): BidiFlow[HttpRequest, SslTlsOutbound, SslTlsInbound, HttpResponse, NotUsed] = - adaptClientLayer(delegate.clientLayer(JavaMapping.toScala(hostHeader))) - - /** - * Constructs a client layer stage using the given [[akka.http.javadsl.settings.ClientConnectionSettings]]. - */ - def clientLayer( - hostHeader: headers.Host, - settings: ClientConnectionSettings): BidiFlow[HttpRequest, SslTlsOutbound, SslTlsInbound, HttpResponse, NotUsed] = - adaptClientLayer(delegate.clientLayer(JavaMapping.toScala(hostHeader), settings.asScala)) - - /** - * Constructs a client layer stage using the given [[ClientConnectionSettings]]. - */ - def clientLayer( - hostHeader: headers.Host, - settings: ClientConnectionSettings, - log: LoggingAdapter): BidiFlow[HttpRequest, SslTlsOutbound, SslTlsInbound, HttpResponse, NotUsed] = - adaptClientLayer(delegate.clientLayer(JavaMapping.toScala(hostHeader), settings.asScala, log)) - - /** - * Creates a [[Flow]] representing a prospective HTTP client connection to the given endpoint. - * Every materialization of the produced flow will attempt to establish a new outgoing connection. - * - * If the hostname is given with an `https://` prefix, the default [[HttpsConnectionContext]] will be used. - */ - def outgoingConnection(host: String): Flow[HttpRequest, HttpResponse, CompletionStage[OutgoingConnection]] = - outgoingConnection(ConnectHttp.toHost(host)) - - /** - * Creates a [[Flow]] representing a prospective HTTP client connection to the given endpoint. - * Every materialization of the produced flow will attempt to establish a new outgoing connection. - * - * Use the [[ConnectHttp]] DSL to configure target host and whether HTTPS should be used. - */ - def outgoingConnection(to: ConnectHttp): Flow[HttpRequest, HttpResponse, CompletionStage[OutgoingConnection]] = - adaptOutgoingFlow { - if (to.isHttps) delegate.outgoingConnectionHttps(to.host, to.port, to.effectiveHttpsConnectionContext(defaultClientHttpsContext).asScala) - else delegate.outgoingConnection(to.host, to.port) - } - - /** - * Creates a [[Flow]] representing a prospective HTTP client connection to the given endpoint. - * Every materialization of the produced flow will attempt to establish a new outgoing connection. - */ - def outgoingConnection( - to: ConnectHttp, - localAddress: Optional[InetSocketAddress], - settings: ClientConnectionSettings, - log: LoggingAdapter): Flow[HttpRequest, HttpResponse, CompletionStage[OutgoingConnection]] = - adaptOutgoingFlow { - if (to.isHttps) - delegate.outgoingConnectionHttps(to.host, to.port, to.effectiveConnectionContext(defaultClientHttpsContext).asInstanceOf[HttpsConnectionContext].asScala, localAddress.asScala, settings.asScala, log) - else - delegate.outgoingConnection(to.host, to.port, localAddress.asScala, settings.asScala, log) - } - - /** - * Starts a new connection pool to the given host and configuration and returns a [[Flow]] which dispatches - * the requests from all its materializations across this pool. - * While the started host connection pool internally shuts itself down automatically after the configured idle - * timeout it will spin itself up again if more requests arrive from an existing or a new client flow - * materialization. The returned flow therefore remains usable for the full lifetime of the application. - * - * Since the underlying transport usually comprises more than a single connection the produced flow might generate - * responses in an order that doesn't directly match the consumed requests. - * For example, if two requests A and B enter the flow in that order the response for B might be produced before the - * response for A. - * In order to allow for easy response-to-request association the flow takes in a custom, opaque context - * object of type `T` from the application which is emitted together with the corresponding response. - */ - def newHostConnectionPool[T](host: String, materializer: Materializer): Flow[Pair[HttpRequest, T], Pair[Try[HttpResponse], T], HostConnectionPool] = - newHostConnectionPool[T](ConnectHttp.toHost(host), materializer) - - /** - * Starts a new connection pool to the given host and configuration and returns a [[Flow]] which dispatches - * the requests from all its materializations across this pool. - * While the started host connection pool internally shuts itself down automatically after the configured idle - * timeout it will spin itself up again if more requests arrive from an existing or a new client flow - * materialization. The returned flow therefore remains usable for the full lifetime of the application. - * - * Since the underlying transport usually comprises more than a single connection the produced flow might generate - * responses in an order that doesn't directly match the consumed requests. - * For example, if two requests A and B enter the flow in that order the response for B might be produced before the - * response for A. - * In order to allow for easy response-to-request association the flow takes in a custom, opaque context - * object of type `T` from the application which is emitted together with the corresponding response. - */ - def newHostConnectionPool[T](to: ConnectHttp, materializer: Materializer): Flow[Pair[HttpRequest, T], Pair[Try[HttpResponse], T], HostConnectionPool] = - adaptTupleFlow(delegate.newHostConnectionPool[T](to.host, to.port)(materializer).mapMaterializedValue(_.toJava)) - - /** - * Same as [[newHostConnectionPool]] but with HTTPS encryption. - * - * The given [[ConnectionContext]] will be used for encryption on the connection. - */ - def newHostConnectionPool[T]( - to: ConnectHttp, - settings: ConnectionPoolSettings, - log: LoggingAdapter, materializer: Materializer): Flow[Pair[HttpRequest, T], Pair[Try[HttpResponse], T], HostConnectionPool] = - adaptTupleFlow { - to.effectiveHttpsConnectionContext(defaultClientHttpsContext) match { - case https: HttpsConnectionContext ⇒ - delegate.newHostConnectionPoolHttps[T](to.host, to.port, https.asScala, settings.asScala, log)(materializer) - .mapMaterializedValue(_.toJava) - case _ ⇒ - delegate.newHostConnectionPool[T](to.host, to.port, settings.asScala, log)(materializer) - .mapMaterializedValue(_.toJava) - } - } - - /** - * Returns a [[Flow]] which dispatches incoming HTTP requests to the per-ActorSystem pool of outgoing - * HTTP connections to the given target host endpoint. For every ActorSystem, target host and pool - * configuration a separate connection pool is maintained. - * The HTTP layer transparently manages idle shutdown and restarting of connections pools as configured. - * The returned [[Flow]] instances therefore remain valid throughout the lifetime of the application. - * - * The internal caching logic guarantees that there will never be more than a single pool running for the - * given target host endpoint and configuration (in this ActorSystem). - * - * Since the underlying transport usually comprises more than a single connection the produced flow might generate - * responses in an order that doesn't directly match the consumed requests. - * For example, if two requests A and B enter the flow in that order the response for B might be produced before the - * response for A. - * In order to allow for easy response-to-request association the flow takes in a custom, opaque context - * object of type `T` from the application which is emitted together with the corresponding response. - */ - def cachedHostConnectionPool[T](host: String, materializer: Materializer): Flow[Pair[HttpRequest, T], Pair[Try[HttpResponse], T], HostConnectionPool] = - cachedHostConnectionPool(ConnectHttp.toHost(host), materializer) - - /** - * Returns a [[Flow]] which dispatches incoming HTTP requests to the per-ActorSystem pool of outgoing - * HTTP connections to the given target host endpoint. For every ActorSystem, target host and pool - * configuration a separate connection pool is maintained. - * The HTTP layer transparently manages idle shutdown and restarting of connections pools as configured. - * The returned [[Flow]] instances therefore remain valid throughout the lifetime of the application. - * - * The internal caching logic guarantees that there will never be more than a single pool running for the - * given target host endpoint and configuration (in this ActorSystem). - * - * Since the underlying transport usually comprises more than a single connection the produced flow might generate - * responses in an order that doesn't directly match the consumed requests. - * For example, if two requests A and B enter the flow in that order the response for B might be produced before the - * response for A. - * In order to allow for easy response-to-request association the flow takes in a custom, opaque context - * object of type `T` from the application which is emitted together with the corresponding response. - */ - def cachedHostConnectionPool[T](to: ConnectHttp, materializer: Materializer): Flow[Pair[HttpRequest, T], Pair[Try[HttpResponse], T], HostConnectionPool] = - adaptTupleFlow(delegate.cachedHostConnectionPool[T](to.host, to.port)(materializer).mapMaterializedValue(_.toJava)) - - /** - * Same as [[cachedHostConnectionPool]] but with HTTPS encryption. - * - * The given [[ConnectionContext]] will be used for encryption on the connection. - */ - def cachedHostConnectionPool[T]( - to: ConnectHttp, - settings: ConnectionPoolSettings, - log: LoggingAdapter, materializer: Materializer): Flow[Pair[HttpRequest, T], Pair[Try[HttpResponse], T], HostConnectionPool] = - adaptTupleFlow(delegate.cachedHostConnectionPoolHttps[T](to.host, to.port, to.effectiveHttpsConnectionContext(defaultClientHttpsContext).asScala, settings.asScala, log)(materializer) - .mapMaterializedValue(_.toJava)) - - /** - * Creates a new "super connection pool flow", which routes incoming requests to a (cached) host connection pool - * depending on their respective effective URIs. Note that incoming requests must have either an absolute URI or - * a valid `Host` header. - * - * Since the underlying transport usually comprises more than a single connection the produced flow might generate - * responses in an order that doesn't directly match the consumed requests. - * For example, if two requests `A` and `B` enter the flow in that order the response for `B` might be produced before the - * response for `A`. - * In order to allow for easy response-to-request association the flow takes in a custom, opaque context - * object of type `T` from the application which is emitted together with the corresponding response. - */ - def superPool[T](materializer: Materializer): Flow[Pair[HttpRequest, T], Pair[Try[HttpResponse], T], NotUsed] = - adaptTupleFlow(delegate.superPool[T]()(materializer)) - - /** - * Creates a new "super connection pool flow", which routes incoming requests to a (cached) host connection pool - * depending on their respective effective URIs. Note that incoming requests must have either an absolute URI or - * a valid `Host` header. - * - * The given [[HttpsConnectionContext]] is used to configure TLS for the connection. - * - * Since the underlying transport usually comprises more than a single connection the produced flow might generate - * responses in an order that doesn't directly match the consumed requests. - * For example, if two requests `A` and `B` enter the `flow` in that order the response for `B` might be produced before the - * response for `A`. - * - * In order to allow for easy response-to-request association the flow takes in a custom, opaque context - * object of type `T` from the application which is emitted together with the corresponding response. - */ - def superPool[T]( - settings: ConnectionPoolSettings, - connectionContext: HttpsConnectionContext, - log: LoggingAdapter, materializer: Materializer): Flow[Pair[HttpRequest, T], Pair[Try[HttpResponse], T], NotUsed] = - adaptTupleFlow(delegate.superPool[T](connectionContext.asScala, settings.asScala, log)(materializer)) - - /** - * Creates a new "super connection pool flow", which routes incoming requests to a (cached) host connection pool - * depending on their respective effective URIs. Note that incoming requests must have either an absolute URI or - * a valid `Host` header. - * - * The [[defaultClientHttpsContext]] is used to configure TLS for the connection. - * - * Since the underlying transport usually comprises more than a single connection the produced flow might generate - * responses in an order that doesn't directly match the consumed requests. - * For example, if two requests `A` and `B` enter the `flow` in that order the response for `B` might be produced before the - * response for `A`. - * - * In order to allow for easy response-to-request association the flow takes in a custom, opaque context - * object of type `T` from the application which is emitted together with the corresponding response. - */ - def superPool[T]( - settings: ConnectionPoolSettings, - log: LoggingAdapter, materializer: Materializer): Flow[Pair[HttpRequest, T], Pair[Try[HttpResponse], T], NotUsed] = - adaptTupleFlow(delegate.superPool[T](defaultClientHttpsContext.asScala, settings.asScala, log)(materializer)) - - /** - * Fires a single [[HttpRequest]] across the (cached) host connection pool for the request's - * effective URI to produce a response future. - * - * The [[defaultClientHttpsContext]] is used to configure TLS for the connection. - * - * Note that the request must have either an absolute URI or a valid `Host` header, otherwise - * the future will be completed with an error. - */ - def singleRequest(request: HttpRequest, materializer: Materializer): CompletionStage[HttpResponse] = - delegate.singleRequest(request.asScala)(materializer).toJava - - /** - * Fires a single [[HttpRequest]] across the (cached) host connection pool for the request's - * effective URI to produce a response future. - * - * The [[defaultClientHttpsContext]] is used to configure TLS for the connection. - * - * Note that the request must have either an absolute URI or a valid `Host` header, otherwise - * the future will be completed with an error. - */ - def singleRequest(request: HttpRequest, connectionContext: HttpsConnectionContext, materializer: Materializer): CompletionStage[HttpResponse] = - delegate.singleRequest(request.asScala, connectionContext.asScala)(materializer).toJava - - /** - * Fires a single [[HttpRequest]] across the (cached) host connection pool for the request's - * effective URI to produce a response future. - * - * The given [[HttpsConnectionContext]] will be used for encruption if the request is sent to an https endpoint. - * - * Note that the request must have either an absolute URI or a valid `Host` header, otherwise - * the future will be completed with an error. - */ - def singleRequest( - request: HttpRequest, - connectionContext: HttpsConnectionContext, - settings: ConnectionPoolSettings, - log: LoggingAdapter, materializer: Materializer): CompletionStage[HttpResponse] = - delegate.singleRequest(request.asScala, connectionContext.asScala, settings.asScala, log)(materializer).toJava - - /** - * Constructs a WebSocket [[BidiFlow]]. - * - * The layer is not reusable and must only be materialized once. - */ - def webSocketClientLayer(request: WebSocketRequest): BidiFlow[Message, SslTlsOutbound, SslTlsInbound, Message, CompletionStage[WebSocketUpgradeResponse]] = - adaptWsBidiFlow(delegate.webSocketClientLayer(request.asScala)) - - /** - * Constructs a WebSocket [[BidiFlow]] using the configured default [[ClientConnectionSettings]], - * configured using the `akka.http.client` config section. - * - * The layer is not reusable and must only be materialized once. - */ - def webSocketClientLayer( - request: WebSocketRequest, - settings: ClientConnectionSettings): BidiFlow[Message, SslTlsOutbound, SslTlsInbound, Message, CompletionStage[WebSocketUpgradeResponse]] = - adaptWsBidiFlow(delegate.webSocketClientLayer(request.asScala, settings.asScala)) - - /** - * Constructs a WebSocket [[BidiFlow]] using the configured default [[ClientConnectionSettings]], - * configured using the `akka.http.client` config section. - * - * The layer is not reusable and must only be materialized once. - */ - def webSocketClientLayer( - request: WebSocketRequest, - settings: ClientConnectionSettings, - log: LoggingAdapter): BidiFlow[Message, SslTlsOutbound, SslTlsInbound, Message, CompletionStage[WebSocketUpgradeResponse]] = - adaptWsBidiFlow(delegate.webSocketClientLayer(request.asScala, settings.asScala, log)) - - /** - * Constructs a flow that once materialized establishes a WebSocket connection to the given Uri. - * - * The layer is not reusable and must only be materialized once. - */ - def webSocketClientFlow(request: WebSocketRequest): Flow[Message, Message, CompletionStage[WebSocketUpgradeResponse]] = - adaptWsFlow { - delegate.webSocketClientFlow(request.asScala) - } - - /** - * Constructs a flow that once materialized establishes a WebSocket connection to the given Uri. - * - * The layer is not reusable and must only be materialized once. - */ - def webSocketClientFlow( - request: WebSocketRequest, - connectionContext: ConnectionContext, - localAddress: Optional[InetSocketAddress], - settings: ClientConnectionSettings, - log: LoggingAdapter): Flow[Message, Message, CompletionStage[WebSocketUpgradeResponse]] = - adaptWsFlow { - delegate.webSocketClientFlow(request.asScala, connectionContext.asScala, localAddress.asScala, settings.asScala, log) - } - - /** - * Runs a single WebSocket conversation given a Uri and a flow that represents the client side of the - * WebSocket conversation. - * - * The [[defaultClientHttpsContext]] is used to configure TLS for the connection. - */ - def singleWebSocketRequest[T]( - request: WebSocketRequest, - clientFlow: Flow[Message, Message, T], - materializer: Materializer): Pair[CompletionStage[WebSocketUpgradeResponse], T] = - adaptWsResultTuple { - delegate.singleWebSocketRequest( - request.asScala, - adaptWsFlow[T](clientFlow))(materializer) - } - - /** - * Runs a single WebSocket conversation given a Uri and a flow that represents the client side of the - * WebSocket conversation. - * - * The [[defaultClientHttpsContext]] is used to configure TLS for the connection. - */ - def singleWebSocketRequest[T]( - request: WebSocketRequest, - clientFlow: Flow[Message, Message, T], - connectionContext: ConnectionContext, - materializer: Materializer): Pair[CompletionStage[WebSocketUpgradeResponse], T] = - adaptWsResultTuple { - delegate.singleWebSocketRequest( - request.asScala, - adaptWsFlow[T](clientFlow), - connectionContext.asScala)(materializer) - } - - /** - * Runs a single WebSocket conversation given a Uri and a flow that represents the client side of the - * WebSocket conversation. - */ - def singleWebSocketRequest[T]( - request: WebSocketRequest, - clientFlow: Flow[Message, Message, T], - connectionContext: ConnectionContext, - localAddress: Optional[InetSocketAddress], - settings: ClientConnectionSettings, - log: LoggingAdapter, - materializer: Materializer): Pair[CompletionStage[WebSocketUpgradeResponse], T] = - adaptWsResultTuple { - delegate.singleWebSocketRequest( - request.asScala, - adaptWsFlow[T](clientFlow), - connectionContext.asScala, - localAddress.asScala, - settings.asScala, - log)(materializer) - } - - /** - * Triggers an orderly shutdown of all host connections pools currently maintained by the [[ActorSystem]]. - * The returned future is completed when all pools that were live at the time of this method call - * have completed their shutdown process. - * - * If existing pool client flows are re-used or new ones materialized concurrently with or after this - * method call the respective connection pools will be restarted and not contribute to the returned future. - */ - def shutdownAllConnectionPools(): CompletionStage[Unit] = delegate.shutdownAllConnectionPools().toJava - - /** - * Gets the current default server-side [[ConnectionContext]] – defaults to plain HTTP. - * Can be modified using [[setDefaultServerHttpContext]], and will then apply for servers bound after that call has completed. - */ - def defaultServerHttpContext: ConnectionContext = - delegate.defaultServerHttpContext - - /** - * Sets the default server-side [[ConnectionContext]]. - * If it is an instance of [[HttpsConnectionContext]] then the server will be bound using HTTPS. - */ - def setDefaultServerHttpContext(context: ConnectionContext): Unit = - delegate.setDefaultServerHttpContext(context.asScala) - - /** - * Gets the current default client-side [[ConnectionContext]]. - */ - def defaultClientHttpsContext: akka.http.javadsl.HttpsConnectionContext = delegate.defaultClientHttpsContext - - /** - * Sets the default client-side [[ConnectionContext]]. - */ - def setDefaultClientHttpsContext(context: HttpsConnectionContext): Unit = - delegate.setDefaultClientHttpsContext(context.asScala) - - def createServerHttpsContext(sslConfig: AkkaSSLConfig): HttpsConnectionContext = - delegate.createServerHttpsContext(sslConfig) - - def createClientHttpsContext(sslConfig: AkkaSSLConfig): HttpsConnectionContext = - delegate.createClientHttpsContext(sslConfig) - - def createDefaultClientHttpsContext(): HttpsConnectionContext = - delegate.createDefaultClientHttpsContext() - - private def adaptTupleFlow[T, Mat](scalaFlow: stream.scaladsl.Flow[(scaladsl.model.HttpRequest, T), (Try[scaladsl.model.HttpResponse], T), Mat]): Flow[Pair[HttpRequest, T], Pair[Try[HttpResponse], T], Mat] = { - implicit val _ = JavaMapping.identity[T] - JavaMapping.toJava(scalaFlow)(JavaMapping.flowMapping[Pair[HttpRequest, T], (scaladsl.model.HttpRequest, T), Pair[Try[HttpResponse], T], (Try[scaladsl.model.HttpResponse], T), Mat]) - } - - private def adaptOutgoingFlow[T, Mat](scalaFlow: stream.scaladsl.Flow[scaladsl.model.HttpRequest, scaladsl.model.HttpResponse, Future[scaladsl.Http.OutgoingConnection]]): Flow[HttpRequest, HttpResponse, CompletionStage[OutgoingConnection]] = - Flow.fromGraph { - akka.stream.scaladsl.Flow[HttpRequest].map(_.asScala) - .viaMat(scalaFlow)(Keep.right) - .mapMaterializedValue(_.map(new OutgoingConnection(_))(ec).toJava) - } - - private def adaptServerLayer(serverLayer: scaladsl.Http.ServerLayer): BidiFlow[HttpResponse, SslTlsOutbound, SslTlsInbound, HttpRequest, NotUsed] = - new BidiFlow( - JavaMapping.adapterBidiFlow[HttpResponse, sm.HttpResponse, sm.HttpRequest, HttpRequest] - .atop(serverLayer)) - - private def adaptClientLayer(clientLayer: scaladsl.Http.ClientLayer): BidiFlow[HttpRequest, SslTlsOutbound, SslTlsInbound, HttpResponse, NotUsed] = - new BidiFlow( - JavaMapping.adapterBidiFlow[HttpRequest, sm.HttpRequest, sm.HttpResponse, HttpResponse] - .atop(clientLayer)) - - private def adaptWsBidiFlow(wsLayer: scaladsl.Http.WebSocketClientLayer): BidiFlow[Message, SslTlsOutbound, SslTlsInbound, Message, CompletionStage[WebSocketUpgradeResponse]] = - new BidiFlow( - JavaMapping.adapterBidiFlow[Message, sm.ws.Message, sm.ws.Message, Message] - .atopMat(wsLayer)((_, s) ⇒ adaptWsUpgradeResponse(s))) - - private def adaptWsFlow(wsLayer: stream.scaladsl.Flow[sm.ws.Message, sm.ws.Message, Future[scaladsl.model.ws.WebSocketUpgradeResponse]]): Flow[Message, Message, CompletionStage[WebSocketUpgradeResponse]] = - Flow.fromGraph(JavaMapping.adapterBidiFlow[Message, sm.ws.Message, sm.ws.Message, Message].joinMat(wsLayer)(Keep.right).mapMaterializedValue(adaptWsUpgradeResponse _)) - - private def adaptWsFlow[Mat](javaFlow: Flow[Message, Message, Mat]): stream.scaladsl.Flow[scaladsl.model.ws.Message, scaladsl.model.ws.Message, Mat] = - stream.scaladsl.Flow[scaladsl.model.ws.Message] - .map(Message.adapt) - .viaMat(javaFlow.asScala)(Keep.right) - .map(_.asScala) - - private def adaptWsResultTuple[T](result: (Future[scaladsl.model.ws.WebSocketUpgradeResponse], T)): Pair[CompletionStage[WebSocketUpgradeResponse], T] = - result match { - case (fut, tMat) ⇒ Pair(adaptWsUpgradeResponse(fut), tMat) - } - private def adaptWsUpgradeResponse(responseFuture: Future[scaladsl.model.ws.WebSocketUpgradeResponse]): CompletionStage[WebSocketUpgradeResponse] = - responseFuture.map(WebSocketUpgradeResponse.adapt)(system.dispatcher).toJava -} diff --git a/akka-http-core/src/main/scala/akka/http/javadsl/IncomingConnection.scala b/akka-http-core/src/main/scala/akka/http/javadsl/IncomingConnection.scala deleted file mode 100644 index 31c0eefadd..0000000000 --- a/akka-http-core/src/main/scala/akka/http/javadsl/IncomingConnection.scala +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl - -import java.net.InetSocketAddress -import akka.NotUsed -import akka.japi.function.Function -import akka.stream.Materializer -import akka.stream.javadsl.Flow -import akka.http.javadsl.model._ -import akka.http.scaladsl.{ model ⇒ sm } -import java.util.concurrent.CompletionStage -import scala.concurrent.Future -import scala.compat.java8.FutureConverters._ - -/** - * Represents one accepted incoming HTTP connection. - */ -class IncomingConnection private[http] (delegate: akka.http.scaladsl.Http.IncomingConnection) { - /** - * The local address of this connection. - */ - def localAddress: InetSocketAddress = delegate.localAddress - - /** - * The address of the remote peer. - */ - def remoteAddress: InetSocketAddress = delegate.remoteAddress - - /** - * A flow representing the incoming requests and outgoing responses for this connection. - * - * Use `Flow.join` or one of the handleXXX methods to consume handle requests on this connection. - */ - def flow: Flow[HttpResponse, HttpRequest, NotUsed] = Flow.fromGraph(delegate.flow).asInstanceOf[Flow[HttpResponse, HttpRequest, NotUsed]] - - /** - * Handles the connection with the given flow, which is materialized exactly once - * and the respective materialization result returned. - */ - def handleWith[Mat](handler: Flow[HttpRequest, HttpResponse, Mat], materializer: Materializer): Mat = - delegate.handleWith(handler.asInstanceOf[Flow[sm.HttpRequest, sm.HttpResponse, Mat]].asScala)(materializer) - - /** - * Handles the connection with the given handler function. - */ - def handleWithSyncHandler(handler: Function[HttpRequest, HttpResponse], materializer: Materializer): Unit = - delegate.handleWithSyncHandler(handler.apply(_).asInstanceOf[sm.HttpResponse])(materializer) - - /** - * Handles the connection with the given handler function. - */ - def handleWithAsyncHandler(handler: Function[HttpRequest, CompletionStage[HttpResponse]], materializer: Materializer): Unit = - delegate.handleWithAsyncHandler(handler.apply(_).toScala.asInstanceOf[Future[sm.HttpResponse]])(materializer) - - /** - * Handles the connection with the given handler function. - */ - def handleWithAsyncHandler(handler: Function[HttpRequest, CompletionStage[HttpResponse]], parallelism: Int, materializer: Materializer): Unit = - delegate.handleWithAsyncHandler(handler.apply(_).toScala.asInstanceOf[Future[sm.HttpResponse]], parallelism)(materializer) -} diff --git a/akka-http-core/src/main/scala/akka/http/javadsl/OutgoingConnection.scala b/akka-http-core/src/main/scala/akka/http/javadsl/OutgoingConnection.scala deleted file mode 100644 index 7bc8cdfaf2..0000000000 --- a/akka-http-core/src/main/scala/akka/http/javadsl/OutgoingConnection.scala +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl - -import java.net.InetSocketAddress - -import akka.http.scaladsl - -class OutgoingConnection private[http] (delegate: scaladsl.Http.OutgoingConnection) { - /** - * The local address of this connection. - */ - def localAddress: InetSocketAddress = delegate.localAddress - - /** - * The address of the remote peer. - */ - def remoteAddress: InetSocketAddress = delegate.remoteAddress -} diff --git a/akka-http-core/src/main/scala/akka/http/javadsl/ServerBinding.scala b/akka-http-core/src/main/scala/akka/http/javadsl/ServerBinding.scala deleted file mode 100644 index c70876b089..0000000000 --- a/akka-http-core/src/main/scala/akka/http/javadsl/ServerBinding.scala +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl - -import java.net.InetSocketAddress -import java.util.concurrent.CompletionStage -import scala.compat.java8.FutureConverters._ - -/** - * Represents a prospective HTTP server binding. - */ -class ServerBinding private[http] (delegate: akka.http.scaladsl.Http.ServerBinding) { - /** - * The local address of the endpoint bound by the materialization of the `connections` [[Source]]. - */ - def localAddress: InetSocketAddress = delegate.localAddress - - /** - * Asynchronously triggers the unbinding of the port that was bound by the materialization of the `connections` - * [[Source]] - * - * The produced [[java.util.concurrent.CompletionStage]] is fulfilled when the unbinding has been completed. - */ - def unbind(): CompletionStage[Unit] = delegate.unbind().toJava -} diff --git a/akka-http-core/src/main/scala/akka/http/javadsl/model/ContentType.scala b/akka-http-core/src/main/scala/akka/http/javadsl/model/ContentType.scala deleted file mode 100644 index 33de494cdc..0000000000 --- a/akka-http-core/src/main/scala/akka/http/javadsl/model/ContentType.scala +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.javadsl.model - -import java.util.Optional - -/** - * Represents an Http content-type. A content-type consists of a media-type and an optional charset. - * - * See [[ContentTypes]] for convenience access to often used values. - */ -// Has to be defined in Scala even though it's JavaDSL because of: -// https://issues.scala-lang.org/browse/SI-9621 -object ContentType { - - trait Binary extends ContentType { - } - - trait NonBinary extends ContentType { - def charset: HttpCharset - } - - trait WithFixedCharset extends NonBinary { - } - - trait WithCharset extends NonBinary { - } - -} - -trait ContentType { - /** - * The media-type of this content-type. - */ - def mediaType: MediaType - - /** - * True if this ContentType is non-textual. - */ - def binary: Boolean - - /** - * Returns the charset if this ContentType is non-binary. - */ - def getCharsetOption: Optional[HttpCharset] -} diff --git a/akka-http-core/src/main/scala/akka/http/javadsl/model/MediaType.scala b/akka-http-core/src/main/scala/akka/http/javadsl/model/MediaType.scala deleted file mode 100644 index e42e297bd5..0000000000 --- a/akka-http-core/src/main/scala/akka/http/javadsl/model/MediaType.scala +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.javadsl.model - -/** - * Represents an Http media-type. A media-type consists of a main-type and a sub-type. - * - * See [[MediaTypes]] for convenience access to often used values. - */ -// Has to be defined in Scala even though it's JavaDSL because of: -// https://issues.scala-lang.org/browse/SI-9621 -object MediaType { - - trait Binary extends MediaType { - def toContentType: ContentType.Binary - } - - trait NonBinary extends MediaType { - } - - trait WithFixedCharset extends NonBinary { - def toContentType: ContentType.WithFixedCharset - } - - trait WithOpenCharset extends NonBinary { - def toContentType(charset: HttpCharset): ContentType.WithCharset - } - - trait Multipart extends WithOpenCharset { - } - - trait Compressibility { - def compressible: Boolean - } -} - -trait MediaType { - /** - * The main-type of this media-type. - */ - def mainType: String - - /** - * The sub-type of this media-type. - */ - def subType: String - - /** - * True when this media-type is generally compressible. - */ - def isCompressible: Boolean - - /** - * True when this media-type is not character-based. - */ - def binary: Boolean - - def isApplication: Boolean - - def isAudio: Boolean - - def isImage: Boolean - - def isMessage: Boolean - - def isMultipart: Boolean - - def isText: Boolean - - def isVideo: Boolean - - /** - * Creates a media-range from this media-type. - */ - def toRange: MediaRange - - /** - * Creates a media-range from this media-type with a given qValue. - */ - def toRange(qValue: Float): MediaRange -} diff --git a/akka-http-core/src/main/scala/akka/http/javadsl/model/ws/Message.scala b/akka-http-core/src/main/scala/akka/http/javadsl/model/ws/Message.scala deleted file mode 100644 index 0524f02464..0000000000 --- a/akka-http-core/src/main/scala/akka/http/javadsl/model/ws/Message.scala +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.ws - -import akka.http.scaladsl.{ model ⇒ sm } -import akka.stream.javadsl.Source -import akka.util.ByteString - -/** - * Represents a WebSocket message. A message can either be a binary message or a text message. - */ -abstract class Message { - /** - * Is this message a text message? If true, [[asTextMessage]] will return this - * text message, if false, [[asBinaryMessage]] will return this binary message. - */ - def isText: Boolean - - /** - * Returns this TextMessage if it is a text message, throws otherwise. - */ - def asTextMessage: TextMessage - - /** - * Returns this BinaryMessage if it is a binary message, throws otherwise. - */ - def asBinaryMessage: BinaryMessage - - def asScala: sm.ws.Message -} - -object Message { - def adapt(msg: sm.ws.Message): Message = msg match { - case t: sm.ws.TextMessage ⇒ TextMessage.adapt(t) - case b: sm.ws.BinaryMessage ⇒ BinaryMessage.adapt(b) - } -} - -/** - * Represents a WebSocket text message. A text message can either be strict in which case - * the complete data is already available or it can be streamed in which case [[getStreamedText]] - * will return a Source streaming the data as it comes in. - */ -//#message-model -abstract class TextMessage extends Message { - /** - * Returns a source of the text message data. - */ - def getStreamedText: Source[String, _] - - /** Is this message a strict one? */ - def isStrict: Boolean - - /** - * Returns the strict message text if this message is strict, throws otherwise. - */ - def getStrictText: String - //#message-model - def isText: Boolean = true - def asTextMessage: TextMessage = this - def asBinaryMessage: BinaryMessage = throw new ClassCastException("This message is not a binary message.") - def asScala: sm.ws.TextMessage - //#message-model -} -//#message-model - -object TextMessage { - /** - * Creates a strict text message. - */ - def create(text: String): TextMessage = - new TextMessage { - def isStrict: Boolean = true - def getStreamedText: Source[String, _] = Source.single(text) - def getStrictText: String = text - - def asScala: sm.ws.TextMessage = sm.ws.TextMessage.Strict(text) - } - /** - * Creates a streamed text message. - */ - def create(textStream: Source[String, _]): TextMessage = - new TextMessage { - def isStrict: Boolean = false - def getStrictText: String = throw new IllegalStateException("Cannot get strict text for streamed message.") - def getStreamedText: Source[String, _] = textStream - - def asScala: sm.ws.TextMessage = sm.ws.TextMessage(textStream.asScala) - } - - def adapt(msg: sm.ws.TextMessage): TextMessage = msg match { - case sm.ws.TextMessage.Strict(text) ⇒ create(text) - case tm: sm.ws.TextMessage ⇒ create(tm.textStream.asJava) - } -} - -/** - * Represents a WebSocket binary message. A binary message can either be strict in which case - * the complete data is already available or it can be streamed in which case [[getStreamedData]] - * will return a Source streaming the data as it comes in. - */ -abstract class BinaryMessage extends Message { - /** - * Returns a source of the binary message data. - */ - def getStreamedData: Source[ByteString, _] - - /** Is this message a strict one? */ - def isStrict: Boolean - - /** - * Returns the strict message data if this message is strict, throws otherwise. - */ - def getStrictData: ByteString - - def isText: Boolean = false - def asTextMessage: TextMessage = throw new ClassCastException("This message is not a text message.") - def asBinaryMessage: BinaryMessage = this - def asScala: sm.ws.BinaryMessage -} - -object BinaryMessage { - /** - * Creates a strict binary message. - */ - def create(data: ByteString): BinaryMessage = - new BinaryMessage { - def isStrict: Boolean = true - def getStreamedData: Source[ByteString, _] = Source.single(data) - def getStrictData: ByteString = data - - def asScala: sm.ws.BinaryMessage = sm.ws.BinaryMessage.Strict(data) - } - - /** - * Creates a streamed binary message. - */ - def create(dataStream: Source[ByteString, _]): BinaryMessage = - new BinaryMessage { - def isStrict: Boolean = false - def getStrictData: ByteString = throw new IllegalStateException("Cannot get strict data for streamed message.") - def getStreamedData: Source[ByteString, _] = dataStream - - def asScala: sm.ws.BinaryMessage = sm.ws.BinaryMessage(dataStream.asScala) - } - - def adapt(msg: sm.ws.BinaryMessage): BinaryMessage = msg match { - case sm.ws.BinaryMessage.Strict(data) ⇒ create(data) - case bm: sm.ws.BinaryMessage ⇒ create(bm.dataStream.asJava) - } -} diff --git a/akka-http-core/src/main/scala/akka/http/javadsl/model/ws/PeerClosedConnectionException.scala b/akka-http-core/src/main/scala/akka/http/javadsl/model/ws/PeerClosedConnectionException.scala deleted file mode 100644 index 886918c58d..0000000000 --- a/akka-http-core/src/main/scala/akka/http/javadsl/model/ws/PeerClosedConnectionException.scala +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.ws - -/** - * A PeerClosedConnectionException will be reported to the WebSocket handler if the peer has closed the connection. - * `closeCode` and `closeReason` contain close messages as reported by the peer. - */ -trait PeerClosedConnectionException extends RuntimeException { - def closeCode: Int - def closeReason: String -} diff --git a/akka-http-core/src/main/scala/akka/http/javadsl/model/ws/UpgradeToWebSocket.scala b/akka-http-core/src/main/scala/akka/http/javadsl/model/ws/UpgradeToWebSocket.scala deleted file mode 100644 index bc5b3fb8aa..0000000000 --- a/akka-http-core/src/main/scala/akka/http/javadsl/model/ws/UpgradeToWebSocket.scala +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.ws - -import java.lang.{ Iterable ⇒ JIterable } -import akka.http.scaladsl.{ model ⇒ sm } -import akka.http.javadsl.model._ - -import akka.stream._ - -/** - * A virtual header that WebSocket requests will contain. Use [[UpgradeToWebSocket.handleMessagesWith]] to - * create a WebSocket handshake response and handle the WebSocket message stream with the given handler. - */ -trait UpgradeToWebSocket extends sm.HttpHeader { - /** - * Returns the sequence of protocols the client accepts. - * - * See http://tools.ietf.org/html/rfc6455#section-1.9 - */ - def getRequestedProtocols(): JIterable[String] - - /** - * Returns a response that can be used to answer a WebSocket handshake request. The connection will afterwards - * use the given handlerFlow to handle WebSocket messages from the client. - */ - def handleMessagesWith(handlerFlow: Graph[FlowShape[Message, Message], _ <: Any]): HttpResponse - - /** - * Returns a response that can be used to answer a WebSocket handshake request. The connection will afterwards - * use the given handlerFlow to handle WebSocket messages from the client. The given subprotocol must be one - * of the ones offered by the client. - */ - def handleMessagesWith(handlerFlow: Graph[FlowShape[Message, Message], _ <: Any], subprotocol: String): HttpResponse - - /** - * Returns a response that can be used to answer a WebSocket handshake request. The connection will afterwards - * use the given inSink to handle WebSocket messages from the client and the given outSource to send messages to the client. - */ - def handleMessagesWith(inSink: Graph[SinkShape[Message], _ <: Any], outSource: Graph[SourceShape[Message], _ <: Any]): HttpResponse - - /** - * Returns a response that can be used to answer a WebSocket handshake request. The connection will afterwards - * use the given inSink to handle WebSocket messages from the client and the given outSource to send messages to the client. - * - * The given subprotocol must be one of the ones offered by the client. - */ - def handleMessagesWith(inSink: Graph[SinkShape[Message], _ <: Any], outSource: Graph[SourceShape[Message], _ <: Any], subprotocol: String): HttpResponse -} diff --git a/akka-http-core/src/main/scala/akka/http/javadsl/model/ws/WebSocket.scala b/akka-http-core/src/main/scala/akka/http/javadsl/model/ws/WebSocket.scala deleted file mode 100644 index 3eabadaa2a..0000000000 --- a/akka-http-core/src/main/scala/akka/http/javadsl/model/ws/WebSocket.scala +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.ws - -import akka.stream.javadsl.Flow -import akka.http.javadsl.model._ -import akka.http.impl.util.JavaMapping.Implicits._ - -object WebSocket { - /** - * If a given request is a WebSocket request a response accepting the request is returned using the given handler to - * handle the WebSocket message stream. If the request wasn't a WebSocket request a response with status code 400 is - * returned. - */ - def handleWebSocketRequestWith(request: HttpRequest, handler: Flow[Message, Message, _]): HttpResponse = - request.asScala.header[UpgradeToWebSocket] match { - case Some(header) ⇒ header.handleMessagesWith(handler) - case None ⇒ HttpResponse.create().withStatus(StatusCodes.BAD_REQUEST).withEntity("Expected WebSocket request") - } -} diff --git a/akka-http-core/src/main/scala/akka/http/javadsl/model/ws/WebSocketRequest.scala b/akka-http-core/src/main/scala/akka/http/javadsl/model/ws/WebSocketRequest.scala deleted file mode 100644 index f03acf8ed6..0000000000 --- a/akka-http-core/src/main/scala/akka/http/javadsl/model/ws/WebSocketRequest.scala +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.ws - -import akka.http.javadsl.model.{ Uri, HttpHeader } -import akka.http.scaladsl.model.ws.{ WebSocketRequest ⇒ ScalaWebSocketRequest } - -/** - * Represents a WebSocket request. Use `WebSocketRequest.create` to create a request - * for a target URI and then use `addHeader` or `requestSubprotocol` to set optional - * details. - */ -abstract class WebSocketRequest { - /** - * Return a copy of this request that contains the given additional header. - */ - def addHeader(header: HttpHeader): WebSocketRequest - - /** - * Return a copy of this request that will require that the server uses the - * given WebSocket subprotocol. - */ - def requestSubprotocol(subprotocol: String): WebSocketRequest - - def asScala: ScalaWebSocketRequest -} -object WebSocketRequest { - import akka.http.impl.util.JavaMapping.Implicits._ - - /** - * Creates a WebSocketRequest to a target URI. Use the methods on `WebSocketRequest` - * to specify further details. - */ - def create(uri: Uri): WebSocketRequest = - wrap(ScalaWebSocketRequest(uri.asScala)) - - /** - * Creates a WebSocketRequest to a target URI. Use the methods on `WebSocketRequest` - * to specify further details. - */ - def create(uriString: String): WebSocketRequest = - create(Uri.create(uriString)) - - /** - * Wraps a Scala version of WebSocketRequest. - */ - def wrap(scalaRequest: ScalaWebSocketRequest): WebSocketRequest = - new WebSocketRequest { - def addHeader(header: HttpHeader): WebSocketRequest = - transform(s ⇒ s.copy(extraHeaders = s.extraHeaders :+ header.asScala)) - def requestSubprotocol(subprotocol: String): WebSocketRequest = - transform(_.copy(subprotocol = Some(subprotocol))) - - def asScala: ScalaWebSocketRequest = scalaRequest - - def transform(f: ScalaWebSocketRequest ⇒ ScalaWebSocketRequest): WebSocketRequest = - wrap(f(asScala)) - } -} diff --git a/akka-http-core/src/main/scala/akka/http/javadsl/model/ws/WebSocketUpgradeResponse.scala b/akka-http-core/src/main/scala/akka/http/javadsl/model/ws/WebSocketUpgradeResponse.scala deleted file mode 100644 index 3655f53d68..0000000000 --- a/akka-http-core/src/main/scala/akka/http/javadsl/model/ws/WebSocketUpgradeResponse.scala +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model.ws - -import java.util.Optional - -import akka.http.javadsl.model.HttpResponse -import akka.http.scaladsl -import akka.http.scaladsl.model.ws.{ InvalidUpgradeResponse, ValidUpgrade } - -/** - * Represents an upgrade response for a WebSocket upgrade request. Can either be valid, in which - * case the `chosenSubprotocol` method is valid, or if invalid, the `invalidationReason` method - * can be used to find out why the upgrade failed. - */ -trait WebSocketUpgradeResponse { - def isValid: Boolean - - /** - * Returns the response object as received from the server for further inspection. - */ - def response: HttpResponse - - /** - * If valid, returns `Some(subprotocol)` (if any was requested), or `None` if none was - * chosen or offered. - */ - def chosenSubprotocol: Optional[String] - - /** - * If invalid, the reason why the server's upgrade response could not be accepted. - */ - def invalidationReason: String -} - -object WebSocketUpgradeResponse { - import akka.http.impl.util.JavaMapping.Implicits._ - def adapt(scalaResponse: scaladsl.model.ws.WebSocketUpgradeResponse): WebSocketUpgradeResponse = - scalaResponse match { - case ValidUpgrade(resp, chosen) ⇒ - new WebSocketUpgradeResponse { - def isValid: Boolean = true - def response: HttpResponse = resp - def chosenSubprotocol: Optional[String] = chosen.asJava - def invalidationReason: String = - throw new UnsupportedOperationException("invalidationReason must not be called for valid response") - } - case InvalidUpgradeResponse(resp, cause) ⇒ - new WebSocketUpgradeResponse { - def isValid: Boolean = false - def response: HttpResponse = resp - def chosenSubprotocol: Optional[String] = throw new UnsupportedOperationException("chosenSubprotocol must not be called for valid response") - def invalidationReason: String = cause - } - } - -} \ No newline at end of file diff --git a/akka-http-core/src/main/scala/akka/http/javadsl/settings/ClientConnectionSettings.scala b/akka-http-core/src/main/scala/akka/http/javadsl/settings/ClientConnectionSettings.scala deleted file mode 100644 index e435eab660..0000000000 --- a/akka-http-core/src/main/scala/akka/http/javadsl/settings/ClientConnectionSettings.scala +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright (C) 2016 Lightbend Inc. - */ -package akka.http.javadsl.settings - -import java.util.{ Optional, Random } - -import akka.actor.ActorSystem -import akka.http.impl.settings.ClientConnectionSettingsImpl -import akka.http.javadsl.model.headers.UserAgent -import akka.io.Inet.SocketOption -import com.typesafe.config.Config - -import akka.http.impl.util.JavaMapping.Implicits._ -import scala.collection.JavaConverters._ -import scala.compat.java8.OptionConverters._ -import scala.concurrent.duration.{ Duration, FiniteDuration } - -/** - * Public API but not intended for subclassing - */ -abstract class ClientConnectionSettings private[akka] () { self: ClientConnectionSettingsImpl ⇒ - def getUserAgentHeader: Optional[UserAgent] - def getConnectingTimeout: FiniteDuration - def getIdleTimeout: Duration - def getRequestHeaderSizeHint: Int - def getWebsocketRandomFactory: java.util.function.Supplier[Random] - def getSocketOptions: java.lang.Iterable[SocketOption] - def getParserSettings: ParserSettings - - // --- - - def withUserAgentHeader(newValue: Optional[UserAgent]): ClientConnectionSettings = self.copy(userAgentHeader = newValue.asScala.map(_.asScala)) - def withConnectingTimeout(newValue: FiniteDuration): ClientConnectionSettings = self.copy(connectingTimeout = newValue) - def withIdleTimeout(newValue: Duration): ClientConnectionSettings = self.copy(idleTimeout = newValue) - def withRequestHeaderSizeHint(newValue: Int): ClientConnectionSettings = self.copy(requestHeaderSizeHint = newValue) - def withWebsocketRandomFactory(newValue: java.util.function.Supplier[Random]): ClientConnectionSettings = self.copy(websocketRandomFactory = () ⇒ newValue.get()) - def withSocketOptions(newValue: java.lang.Iterable[SocketOption]): ClientConnectionSettings = self.copy(socketOptions = newValue.asScala.toList) - def withParserSettings(newValue: ParserSettings): ClientConnectionSettings = self.copy(parserSettings = newValue.asScala) - -} - -object ClientConnectionSettings extends SettingsCompanion[ClientConnectionSettings] { - def create(config: Config): ClientConnectionSettings = ClientConnectionSettingsImpl(config) - def create(configOverrides: String): ClientConnectionSettings = ClientConnectionSettingsImpl(configOverrides) - override def create(system: ActorSystem): ClientConnectionSettings = create(system.settings.config) -} diff --git a/akka-http-core/src/main/scala/akka/http/javadsl/settings/ConnectionPoolSettings.scala b/akka-http-core/src/main/scala/akka/http/javadsl/settings/ConnectionPoolSettings.scala deleted file mode 100644 index f1398120ac..0000000000 --- a/akka-http-core/src/main/scala/akka/http/javadsl/settings/ConnectionPoolSettings.scala +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (C) 2016 Lightbend Inc. - */ -package akka.http.javadsl.settings - -import akka.actor.ActorSystem -import akka.http.impl.settings.ConnectionPoolSettingsImpl -import com.typesafe.config.Config - -import scala.concurrent.duration.Duration -import akka.http.impl.util.JavaMapping.Implicits._ - -/** - * Public API but not intended for subclassing - */ -abstract class ConnectionPoolSettings private[akka] () { self: ConnectionPoolSettingsImpl ⇒ - def getMaxConnections: Int - def getMinConnections: Int - def getMaxRetries: Int - def getMaxOpenRequests: Int - def getPipeliningLimit: Int - def getIdleTimeout: Duration - def getConnectionSettings: ClientConnectionSettings - - // --- - - def withMaxConnections(n: Int): ConnectionPoolSettings = self.copy(maxConnections = n) - def withMaxRetries(n: Int): ConnectionPoolSettings = self.copy(maxRetries = n) - def withMaxOpenRequests(newValue: Int): ConnectionPoolSettings = self.copy(maxOpenRequests = newValue) - def withPipeliningLimit(newValue: Int): ConnectionPoolSettings = self.copy(pipeliningLimit = newValue) - def withIdleTimeout(newValue: Duration): ConnectionPoolSettings = self.copy(idleTimeout = newValue) - def withConnectionSettings(newValue: ClientConnectionSettings): ConnectionPoolSettings = self.copy(connectionSettings = newValue.asScala) -} - -object ConnectionPoolSettings extends SettingsCompanion[ConnectionPoolSettings] { - override def create(config: Config): ConnectionPoolSettings = ConnectionPoolSettingsImpl(config) - override def create(configOverrides: String): ConnectionPoolSettings = ConnectionPoolSettingsImpl(configOverrides) - override def create(system: ActorSystem): ConnectionPoolSettings = create(system.settings.config) -} \ No newline at end of file diff --git a/akka-http-core/src/main/scala/akka/http/javadsl/settings/ParserSettings.scala b/akka-http-core/src/main/scala/akka/http/javadsl/settings/ParserSettings.scala deleted file mode 100644 index 21279b3bfb..0000000000 --- a/akka-http-core/src/main/scala/akka/http/javadsl/settings/ParserSettings.scala +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright (C) 2016 Lightbend Inc. - */ -package akka.http.javadsl.settings - -import java.util.Optional - -import akka.actor.ActorSystem -import akka.http.impl.engine.parsing.BodyPartParser -import akka.http.impl.settings.ParserSettingsImpl -import java.{ util ⇒ ju } -import akka.http.impl.util.JavaMapping.Implicits._ -import scala.annotation.varargs -import scala.collection.JavaConverters._ - -import akka.http.javadsl.model.{ MediaType, HttpMethod, StatusCode, Uri } -import com.typesafe.config.Config - -/** - * Public API but not intended for subclassing - */ -abstract class ParserSettings private[akka] () extends BodyPartParser.Settings { self: ParserSettingsImpl ⇒ - def getMaxUriLength: Int - def getMaxMethodLength: Int - def getMaxResponseReasonLength: Int - def getMaxHeaderNameLength: Int - def getMaxHeaderValueLength: Int - def getMaxHeaderCount: Int - def getMaxContentLength: Long - def getMaxChunkExtLength: Int - def getMaxChunkSize: Int - def getUriParsingMode: Uri.ParsingMode - def getCookieParsingMode: ParserSettings.CookieParsingMode - def getIllegalHeaderWarnings: Boolean - def getErrorLoggingVerbosity: ParserSettings.ErrorLoggingVerbosity - def getIllegalResponseHeaderValueProcessingMode: ParserSettings.IllegalResponseHeaderValueProcessingMode - def getHeaderValueCacheLimits: ju.Map[String, Int] - def getIncludeTlsSessionInfoHeader: Boolean - def headerValueCacheLimits: Map[String, Int] - def getCustomMethods: java.util.function.Function[String, Optional[HttpMethod]] - def getCustomStatusCodes: java.util.function.Function[Int, Optional[StatusCode]] - def getCustomMediaTypes: akka.japi.function.Function2[String, String, Optional[MediaType]] - - // --- - - def withMaxUriLength(newValue: Int): ParserSettings = self.copy(maxUriLength = newValue) - def withMaxMethodLength(newValue: Int): ParserSettings = self.copy(maxMethodLength = newValue) - def withMaxResponseReasonLength(newValue: Int): ParserSettings = self.copy(maxResponseReasonLength = newValue) - def withMaxHeaderNameLength(newValue: Int): ParserSettings = self.copy(maxHeaderNameLength = newValue) - def withMaxHeaderValueLength(newValue: Int): ParserSettings = self.copy(maxHeaderValueLength = newValue) - def withMaxHeaderCount(newValue: Int): ParserSettings = self.copy(maxHeaderCount = newValue) - def withMaxContentLength(newValue: Long): ParserSettings = self.copy(maxContentLength = newValue) - def withMaxChunkExtLength(newValue: Int): ParserSettings = self.copy(maxChunkExtLength = newValue) - def withMaxChunkSize(newValue: Int): ParserSettings = self.copy(maxChunkSize = newValue) - def withUriParsingMode(newValue: Uri.ParsingMode): ParserSettings = self.copy(uriParsingMode = newValue.asScala) - def withCookieParsingMode(newValue: ParserSettings.CookieParsingMode): ParserSettings = self.copy(cookieParsingMode = newValue.asScala) - def withIllegalHeaderWarnings(newValue: Boolean): ParserSettings = self.copy(illegalHeaderWarnings = newValue) - def withErrorLoggingVerbosity(newValue: ParserSettings.ErrorLoggingVerbosity): ParserSettings = self.copy(errorLoggingVerbosity = newValue.asScala) - def withHeaderValueCacheLimits(newValue: ju.Map[String, Int]): ParserSettings = self.copy(headerValueCacheLimits = newValue.asScala.toMap) - def withIncludeTlsSessionInfoHeader(newValue: Boolean): ParserSettings = self.copy(includeTlsSessionInfoHeader = newValue) - - // special --- - - @varargs - def withCustomMethods(methods: HttpMethod*): ParserSettings = { - val map = methods.map(m ⇒ m.name → m.asScala).toMap - self.copy(customMethods = map.get) - } - @varargs - def withCustomStatusCodes(codes: StatusCode*): ParserSettings = { - val map = codes.map(c ⇒ c.intValue → c.asScala).toMap - self.copy(customStatusCodes = map.get) - } - @varargs - def withCustomMediaTypes(mediaTypes: MediaType*): ParserSettings = { - val map = mediaTypes.map(c ⇒ (c.mainType, c.subType) → c.asScala).toMap - self.copy(customMediaTypes = (main, sub) ⇒ map.get(main → sub)) - } - -} - -object ParserSettings extends SettingsCompanion[ParserSettings] { - trait CookieParsingMode - trait ErrorLoggingVerbosity - trait IllegalResponseHeaderValueProcessingMode - - override def create(config: Config): ParserSettings = ParserSettingsImpl(config) - override def create(configOverrides: String): ParserSettings = ParserSettingsImpl(configOverrides) - override def create(system: ActorSystem): ParserSettings = create(system.settings.config) -} diff --git a/akka-http-core/src/main/scala/akka/http/javadsl/settings/RoutingSettings.scala b/akka-http-core/src/main/scala/akka/http/javadsl/settings/RoutingSettings.scala deleted file mode 100644 index 7983aabc05..0000000000 --- a/akka-http-core/src/main/scala/akka/http/javadsl/settings/RoutingSettings.scala +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2016 Lightbend Inc. - */ -package akka.http.javadsl.settings - -import akka.actor.ActorSystem -import akka.http.impl.settings.RoutingSettingsImpl -import com.typesafe.config.Config - -/** - * Public API but not intended for subclassing - */ -abstract class RoutingSettings private[akka] () { self: RoutingSettingsImpl ⇒ - def getVerboseErrorMessages: Boolean - def getFileGetConditional: Boolean - def getRenderVanityFooter: Boolean - def getRangeCountLimit: Int - def getRangeCoalescingThreshold: Long - def getDecodeMaxBytesPerChunk: Int - def getFileIODispatcher: String - - def withVerboseErrorMessages(verboseErrorMessages: Boolean): RoutingSettings = self.copy(verboseErrorMessages = verboseErrorMessages) - def withFileGetConditional(fileGetConditional: Boolean): RoutingSettings = self.copy(fileGetConditional = fileGetConditional) - def withRenderVanityFooter(renderVanityFooter: Boolean): RoutingSettings = self.copy(renderVanityFooter = renderVanityFooter) - def withRangeCountLimit(rangeCountLimit: Int): RoutingSettings = self.copy(rangeCountLimit = rangeCountLimit) - def withRangeCoalescingThreshold(rangeCoalescingThreshold: Long): RoutingSettings = self.copy(rangeCoalescingThreshold = rangeCoalescingThreshold) - def withDecodeMaxBytesPerChunk(decodeMaxBytesPerChunk: Int): RoutingSettings = self.copy(decodeMaxBytesPerChunk = decodeMaxBytesPerChunk) - def withFileIODispatcher(fileIODispatcher: String): RoutingSettings = self.copy(fileIODispatcher = fileIODispatcher) -} - -object RoutingSettings extends SettingsCompanion[RoutingSettings] { - override def create(config: Config): RoutingSettings = RoutingSettingsImpl(config) - override def create(configOverrides: String): RoutingSettings = RoutingSettingsImpl(configOverrides) - override def create(system: ActorSystem): RoutingSettings = create(system.settings.config) -} diff --git a/akka-http-core/src/main/scala/akka/http/javadsl/settings/ServerSettings.scala b/akka-http-core/src/main/scala/akka/http/javadsl/settings/ServerSettings.scala deleted file mode 100644 index b0bed97109..0000000000 --- a/akka-http-core/src/main/scala/akka/http/javadsl/settings/ServerSettings.scala +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright (C) 2016 Lightbend Inc. - */ -package akka.http.javadsl.settings - -import java.util.{ Optional, Random } - -import akka.actor.ActorSystem -import akka.http.impl.settings.ServerSettingsImpl -import akka.http.javadsl.model.headers.Host -import akka.http.javadsl.model.headers.Server -import akka.io.Inet.SocketOption -import akka.http.impl.util.JavaMapping.Implicits._ -import com.typesafe.config.Config -import scala.collection.JavaConverters._ -import scala.compat.java8.OptionConverters._ - -import scala.concurrent.duration.{ Duration, FiniteDuration } - -/** - * Public API but not intended for subclassing - */ -abstract class ServerSettings { self: ServerSettingsImpl ⇒ - def getServerHeader: Optional[Server] - def getTimeouts: ServerSettings.Timeouts - def getMaxConnections: Int - def getPipeliningLimit: Int - def getRemoteAddressHeader: Boolean - def getRawRequestUriHeader: Boolean - def getTransparentHeadRequests: Boolean - def getVerboseErrorMessages: Boolean - def getResponseHeaderSizeHint: Int - def getBacklog: Int - def getSocketOptions: java.lang.Iterable[SocketOption] - def getDefaultHostHeader: Host - def getWebsocketRandomFactory: java.util.function.Supplier[Random] - def getParserSettings: ParserSettings - - // --- - - def withServerHeader(newValue: Optional[Server]): ServerSettings = self.copy(serverHeader = newValue.asScala.map(_.asScala)) - def withTimeouts(newValue: ServerSettings.Timeouts): ServerSettings = self.copy(timeouts = newValue.asScala) - def withMaxConnections(newValue: Int): ServerSettings = self.copy(maxConnections = newValue) - def withPipeliningLimit(newValue: Int): ServerSettings = self.copy(pipeliningLimit = newValue) - def withRemoteAddressHeader(newValue: Boolean): ServerSettings = self.copy(remoteAddressHeader = newValue) - def withRawRequestUriHeader(newValue: Boolean): ServerSettings = self.copy(rawRequestUriHeader = newValue) - def withTransparentHeadRequests(newValue: Boolean): ServerSettings = self.copy(transparentHeadRequests = newValue) - def withVerboseErrorMessages(newValue: Boolean): ServerSettings = self.copy(verboseErrorMessages = newValue) - def withResponseHeaderSizeHint(newValue: Int): ServerSettings = self.copy(responseHeaderSizeHint = newValue) - def withBacklog(newValue: Int): ServerSettings = self.copy(backlog = newValue) - def withSocketOptions(newValue: java.lang.Iterable[SocketOption]): ServerSettings = self.copy(socketOptions = newValue.asScala.toList) - def withDefaultHostHeader(newValue: Host): ServerSettings = self.copy(defaultHostHeader = newValue.asScala) - def withParserSettings(newValue: ParserSettings): ServerSettings = self.copy(parserSettings = newValue.asScala) - def withWebsocketRandomFactory(newValue: java.util.function.Supplier[Random]): ServerSettings = self.copy(websocketRandomFactory = () ⇒ newValue.get()) - -} - -object ServerSettings extends SettingsCompanion[ServerSettings] { - trait Timeouts { - def idleTimeout: Duration - def requestTimeout: Duration - def bindTimeout: FiniteDuration - - // --- - def withIdleTimeout(newValue: Duration): ServerSettings.Timeouts = self.copy(idleTimeout = newValue) - def withRequestTimeout(newValue: Duration): ServerSettings.Timeouts = self.copy(requestTimeout = newValue) - def withBindTimeout(newValue: FiniteDuration): ServerSettings.Timeouts = self.copy(bindTimeout = newValue) - - /** INTERNAL API */ - protected def self = this.asInstanceOf[ServerSettingsImpl.Timeouts] - } - - override def create(config: Config): ServerSettings = ServerSettingsImpl(config) - override def create(configOverrides: String): ServerSettings = ServerSettingsImpl(configOverrides) - override def create(system: ActorSystem): ServerSettings = create(system.settings.config) -} \ No newline at end of file diff --git a/akka-http-core/src/main/scala/akka/http/javadsl/settings/SettingsCompanion.scala b/akka-http-core/src/main/scala/akka/http/javadsl/settings/SettingsCompanion.scala deleted file mode 100644 index e805fe0e35..0000000000 --- a/akka-http-core/src/main/scala/akka/http/javadsl/settings/SettingsCompanion.scala +++ /dev/null @@ -1,32 +0,0 @@ -package akka.http.javadsl.settings - -import akka.actor.ActorSystem -import com.typesafe.config.Config - -/** INTERNAL API */ -trait SettingsCompanion[T] { - - /** - * WARNING: This MUST overriden in sub-classes as otherwise won't be usable (return type) from Java. - * Creates an instance of settings using the configuration provided by the given ActorSystem. - * - * Java API - */ - def create(system: ActorSystem): T = create(system.settings.config) - - /** - * Creates an instance of settings using the given Config. - * - * Java API - */ - def create(config: Config): T - - /** - * Create an instance of settings using the given String of config overrides to override - * settings set in the class loader of this class (i.e. by application.conf or reference.conf files in - * the class loader of this class). - * - * Java API - */ - def create(configOverrides: String): T -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/ConnectionContext.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/ConnectionContext.scala deleted file mode 100644 index b29c0a3ac8..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/ConnectionContext.scala +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl - -import akka.stream.TLSClientAuth -import akka.stream.TLSProtocol._ -import com.typesafe.sslconfig.akka.AkkaSSLConfig - -import scala.collection.JavaConverters._ -import java.util.{ Optional, Collection ⇒ JCollection } -import javax.net.ssl._ - -import scala.collection.immutable -import scala.compat.java8.OptionConverters._ - -trait ConnectionContext extends akka.http.javadsl.ConnectionContext { - final def defaultPort = getDefaultPort -} - -object ConnectionContext { - //#https-context-creation - // ConnectionContext - def https( - sslContext: SSLContext, - sslConfig: Option[AkkaSSLConfig] = None, - enabledCipherSuites: Option[immutable.Seq[String]] = None, - enabledProtocols: Option[immutable.Seq[String]] = None, - clientAuth: Option[TLSClientAuth] = None, - sslParameters: Option[SSLParameters] = None) = - new HttpsConnectionContext(sslContext, sslConfig, enabledCipherSuites, enabledProtocols, clientAuth, sslParameters) - //#https-context-creation - - // for binary-compatibility, since 2.4.7 - def https( - sslContext: SSLContext, - enabledCipherSuites: Option[immutable.Seq[String]], - enabledProtocols: Option[immutable.Seq[String]], - clientAuth: Option[TLSClientAuth], - sslParameters: Option[SSLParameters]) = - new HttpsConnectionContext(sslContext, None, enabledCipherSuites, enabledProtocols, clientAuth, sslParameters) - - def noEncryption() = HttpConnectionContext -} - -final class HttpsConnectionContext( - val sslContext: SSLContext, - val sslConfig: Option[AkkaSSLConfig] = None, - val enabledCipherSuites: Option[immutable.Seq[String]] = None, - val enabledProtocols: Option[immutable.Seq[String]] = None, - val clientAuth: Option[TLSClientAuth] = None, - val sslParameters: Option[SSLParameters] = None) - extends akka.http.javadsl.HttpsConnectionContext with ConnectionContext { - - // for binary-compatibility, since 2.4.7 - def this( - sslContext: SSLContext, - enabledCipherSuites: Option[immutable.Seq[String]], - enabledProtocols: Option[immutable.Seq[String]], - clientAuth: Option[TLSClientAuth], - sslParameters: Option[SSLParameters]) = - this(sslContext, None, enabledCipherSuites, enabledProtocols, clientAuth, sslParameters) - - def firstSession = NegotiateNewSession(enabledCipherSuites, enabledProtocols, clientAuth, sslParameters) - - override def getSslContext = sslContext - override def getEnabledCipherSuites: Optional[JCollection[String]] = enabledCipherSuites.map(_.asJavaCollection).asJava - override def getEnabledProtocols: Optional[JCollection[String]] = enabledProtocols.map(_.asJavaCollection).asJava - override def getClientAuth: Optional[TLSClientAuth] = clientAuth.asJava - override def getSslParameters: Optional[SSLParameters] = sslParameters.asJava -} - -sealed class HttpConnectionContext extends akka.http.javadsl.HttpConnectionContext with ConnectionContext -final object HttpConnectionContext extends HttpConnectionContext { - /** Java API */ - def getInstance() = this -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/Http.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/Http.scala deleted file mode 100644 index 63c3cc6dff..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/Http.scala +++ /dev/null @@ -1,876 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl - -import java.net.InetSocketAddress -import java.util.concurrent.CompletionStage -import javax.net.ssl._ - -import akka.actor._ -import akka.dispatch.ExecutionContexts -import akka.event.{ Logging, LoggingAdapter } -import akka.http.impl.engine.HttpConnectionTimeoutException -import akka.http.impl.engine.client.PoolMasterActor.{ PoolSize, ShutdownAll } -import akka.http.impl.engine.client._ -import akka.http.impl.engine.server._ -import akka.http.impl.engine.ws.WebSocketClientBlueprint -import akka.http.impl.settings.{ ConnectionPoolSetup, HostConnectionPoolSetup } -import akka.http.impl.util.{ MapError, StreamUtils } -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers.Host -import akka.http.scaladsl.model.ws.{ Message, WebSocketRequest, WebSocketUpgradeResponse } -import akka.http.scaladsl.settings.{ ClientConnectionSettings, ConnectionPoolSettings, ServerSettings } -import akka.http.scaladsl.util.FastFuture -import akka.{ Done, NotUsed } -import akka.stream._ -import akka.stream.TLSProtocol._ -import akka.stream.scaladsl._ -import akka.util.ByteString -import com.typesafe.config.Config -import com.typesafe.sslconfig.akka._ -import com.typesafe.sslconfig.akka.util.AkkaLoggerFactory -import com.typesafe.sslconfig.ssl.ConfigSSLContextBuilder - -import scala.concurrent._ -import scala.util.Try -import scala.util.control.NonFatal -import scala.compat.java8.FutureConverters._ - -class HttpExt(private val config: Config)(implicit val system: ActorSystem) extends akka.actor.Extension - with DefaultSSLContextCreation { - - import Http._ - - override val sslConfig = AkkaSSLConfig(system) - validateAndWarnAboutLooseSettings() - - private[this] val defaultConnectionPoolSettings = ConnectionPoolSettings(system) - - // configured default HttpsContext for the client-side - // SYNCHRONIZED ACCESS ONLY! - private[this] var _defaultClientHttpsConnectionContext: HttpsConnectionContext = _ - private[this] var _defaultServerConnectionContext: ConnectionContext = _ - - // ** SERVER ** // - - private[this] final val DefaultPortForProtocol = -1 // any negative value - - private def fuseServerLayer(settings: ServerSettings, connectionContext: ConnectionContext, log: LoggingAdapter)(implicit mat: Materializer): BidiFlow[HttpResponse, ByteString, ByteString, HttpRequest, NotUsed] = { - val httpLayer = serverLayer(settings, None, log) - val tlsStage = sslTlsStage(connectionContext, Server) - BidiFlow.fromGraph(Fusing.aggressive(GraphDSL.create() { implicit b ⇒ - import GraphDSL.Implicits._ - val http = b.add(httpLayer) - val tls = b.add(tlsStage) - - val timeouts = b.add(Flow[ByteString].recover { - case t: TimeoutException ⇒ throw new HttpConnectionTimeoutException(t.getMessage) - }) - - tls.out2 ~> http.in2 - tls.in1 <~ http.out1 - - tls.out1 ~> timeouts.in - - BidiShape(http.in1, timeouts.out, tls.in2, http.out2) - })) - } - - /** - * Creates a [[akka.stream.scaladsl.Source]] of [[akka.http.scaladsl.Http.IncomingConnection]] instances which represents a prospective HTTP server binding - * on the given `endpoint`. - * - * If the given port is 0 the resulting source can be materialized several times. Each materialization will - * then be assigned a new local port by the operating system, which can then be retrieved by the materialized - * [[akka.http.scaladsl.Http.ServerBinding]]. - * - * If the given port is non-zero subsequent materialization attempts of the produced source will immediately - * fail, unless the first materialization has already been unbound. Unbinding can be triggered via the materialized - * [[akka.http.scaladsl.Http.ServerBinding]]. - * - * If an [[ConnectionContext]] is given it will be used for setting up TLS encryption on the binding. - * Otherwise the binding will be unencrypted. - * - * If no `port` is explicitly given (or the port value is negative) the protocol's default port will be used, - * which is 80 for HTTP and 443 for HTTPS. - * - * To configure additional settings for a server started using this method, - * use the `akka.http.server` config section or pass in a [[akka.http.scaladsl.settings.ServerSettings]] explicitly. - */ - def bind(interface: String, port: Int = DefaultPortForProtocol, - connectionContext: ConnectionContext = defaultServerHttpContext, - settings: ServerSettings = ServerSettings(system), - log: LoggingAdapter = system.log)(implicit fm: Materializer): Source[IncomingConnection, Future[ServerBinding]] = { - val effectivePort = if (port >= 0) port else connectionContext.defaultPort - - val fullLayer = fuseServerLayer(settings, connectionContext, log) - - val connections: Source[Tcp.IncomingConnection, Future[Tcp.ServerBinding]] = - Tcp().bind(interface, effectivePort, settings.backlog, settings.socketOptions, halfClose = false, settings.timeouts.idleTimeout) - connections.map { - case Tcp.IncomingConnection(localAddress, remoteAddress, flow) ⇒ - IncomingConnection(localAddress, remoteAddress, fullLayer join flow) - }.mapMaterializedValue { - _.map(tcpBinding ⇒ ServerBinding(tcpBinding.localAddress)(() ⇒ tcpBinding.unbind()))(fm.executionContext) - } - } - - /** - * Convenience method which starts a new HTTP server at the given endpoint and uses the given `handler` - * [[akka.stream.scaladsl.Flow]] for processing all incoming connections. - * - * The number of concurrently accepted connections can be configured by overriding - * the `akka.http.server.max-connections` setting. - * - * To configure additional settings for a server started using this method, - * use the `akka.http.server` config section or pass in a [[akka.http.scaladsl.settings.ServerSettings]] explicitly. - */ - def bindAndHandle( - handler: Flow[HttpRequest, HttpResponse, Any], - interface: String, port: Int = DefaultPortForProtocol, - connectionContext: ConnectionContext = defaultServerHttpContext, - settings: ServerSettings = ServerSettings(system), - log: LoggingAdapter = system.log)(implicit fm: Materializer): Future[ServerBinding] = { - val effectivePort = if (port >= 0) port else connectionContext.defaultPort - - val fullLayer: Flow[ByteString, ByteString, Future[Done]] = Flow.fromGraph(Fusing.aggressive( - Flow[HttpRequest] - .watchTermination()(Keep.right) - .viaMat(handler)(Keep.left) - .joinMat(fuseServerLayer(settings, connectionContext, log))(Keep.left))) - - val connections: Source[Tcp.IncomingConnection, Future[Tcp.ServerBinding]] = - Tcp().bind(interface, effectivePort, settings.backlog, settings.socketOptions, halfClose = false, settings.timeouts.idleTimeout) - - connections.mapAsyncUnordered(settings.maxConnections) { - case incoming: Tcp.IncomingConnection ⇒ - try { - val layer = - if (settings.remoteAddressHeader) fullLayer.addAttributes(HttpAttributes.remoteAddress(Some(incoming.remoteAddress))) - else fullLayer - - layer.joinMat(incoming.flow)(Keep.left) - .run().recover { - // Ignore incoming errors from the connection as they will cancel the binding. - // As far as it is known currently, these errors can only happen if a TCP error bubbles up - // from the TCP layer through the HTTP layer to the Http.IncomingConnection.flow. - // See https://github.com/akka/akka/issues/17992 - case NonFatal(ex) ⇒ - Done - }(ExecutionContexts.sameThreadExecutionContext) - } catch { - case NonFatal(e) ⇒ - log.error(e, "Could not materialize handling flow for {}", incoming) - throw e - } - }.mapMaterializedValue { - _.map(tcpBinding ⇒ ServerBinding(tcpBinding.localAddress)(() ⇒ tcpBinding.unbind()))(fm.executionContext) - }.to(Sink.ignore).run() - - } - - /** - * Convenience method which starts a new HTTP server at the given endpoint and uses the given `handler` - * [[akka.stream.scaladsl.Flow]] for processing all incoming connections. - * - * The number of concurrently accepted connections can be configured by overriding - * the `akka.http.server.max-connections` setting. - * - * To configure additional settings for a server started using this method, - * use the `akka.http.server` config section or pass in a [[akka.http.scaladsl.settings.ServerSettings]] explicitly. - */ - def bindAndHandleSync( - handler: HttpRequest ⇒ HttpResponse, - interface: String, port: Int = DefaultPortForProtocol, - connectionContext: ConnectionContext = defaultServerHttpContext, - settings: ServerSettings = ServerSettings(system), - log: LoggingAdapter = system.log)(implicit fm: Materializer): Future[ServerBinding] = - bindAndHandle(Flow[HttpRequest].map(handler), interface, port, connectionContext, settings, log) - - /** - * Convenience method which starts a new HTTP server at the given endpoint and uses the given `handler` - * [[akka.stream.scaladsl.Flow]] for processing all incoming connections. - * - * The number of concurrently accepted connections can be configured by overriding - * the `akka.http.server.max-connections` setting. - * - * To configure additional settings for a server started using this method, - * use the `akka.http.server` config section or pass in a [[akka.http.scaladsl.settings.ServerSettings]] explicitly. - */ - def bindAndHandleAsync( - handler: HttpRequest ⇒ Future[HttpResponse], - interface: String, port: Int = DefaultPortForProtocol, - connectionContext: ConnectionContext = defaultServerHttpContext, - settings: ServerSettings = ServerSettings(system), - parallelism: Int = 1, - log: LoggingAdapter = system.log)(implicit fm: Materializer): Future[ServerBinding] = - bindAndHandle(Flow[HttpRequest].mapAsync(parallelism)(handler), interface, port, connectionContext, settings, log) - - type ServerLayer = Http.ServerLayer - - /** - * Constructs a [[akka.http.scaladsl.Http.ServerLayer]] stage using the configured default [[akka.http.scaladsl.settings.ServerSettings]], - * configured using the `akka.http.server` config section. - * - * The returned [[akka.stream.scaladsl.BidiFlow]] can only be materialized once. - */ - def serverLayer()(implicit mat: Materializer): ServerLayer = serverLayer(ServerSettings(system)) - - /** - * Constructs a [[akka.http.scaladsl.Http.ServerLayer]] stage using the given [[akka.http.scaladsl.settings.ServerSettings]]. The returned [[akka.stream.scaladsl.BidiFlow]] isn't reusable and - * can only be materialized once. The `remoteAddress`, if provided, will be added as a header to each [[akka.http.scaladsl.model.HttpRequest]] - * this layer produces if the `akka.http.server.remote-address-header` configuration option is enabled. - */ - def serverLayer( - settings: ServerSettings, - remoteAddress: Option[InetSocketAddress] = None, - log: LoggingAdapter = system.log)(implicit mat: Materializer): ServerLayer = - HttpServerBluePrint(settings, remoteAddress, log) - - // ** CLIENT ** // - - private[this] val poolMasterActorRef = system.actorOf(PoolMasterActor.props, "pool-master") - private[this] val systemMaterializer = ActorMaterializer() - - /** - * Creates a [[akka.stream.scaladsl.Flow]] representing a prospective HTTP client connection to the given endpoint. - * Every materialization of the produced flow will attempt to establish a new outgoing connection. - * - * To configure additional settings for requests made using this method, - * use the `akka.http.client` config section or pass in a [[akka.http.scaladsl.settings.ClientConnectionSettings]] explicitly. - */ - def outgoingConnection(host: String, port: Int = 80, - localAddress: Option[InetSocketAddress] = None, - settings: ClientConnectionSettings = ClientConnectionSettings(system), - log: LoggingAdapter = system.log): Flow[HttpRequest, HttpResponse, Future[OutgoingConnection]] = - _outgoingConnection(host, port, localAddress, settings, ConnectionContext.noEncryption(), log) - - /** - * Same as [[#outgoingConnection]] but for encrypted (HTTPS) connections. - * - * If an explicit [[HttpsConnectionContext]] is given then it rather than the configured default [[HttpsConnectionContext]] will be used - * for encryption on the connection. - * - * To configure additional settings for requests made using this method, - * use the `akka.http.client` config section or pass in a [[akka.http.scaladsl.settings.ClientConnectionSettings]] explicitly. - */ - def outgoingConnectionHttps(host: String, port: Int = 443, - connectionContext: HttpsConnectionContext = defaultClientHttpsContext, - localAddress: Option[InetSocketAddress] = None, - settings: ClientConnectionSettings = ClientConnectionSettings(system), - log: LoggingAdapter = system.log): Flow[HttpRequest, HttpResponse, Future[OutgoingConnection]] = - _outgoingConnection(host, port, localAddress, settings, connectionContext, log) - - private def _outgoingConnection( - host: String, - port: Int, - localAddress: Option[InetSocketAddress], - settings: ClientConnectionSettings, - connectionContext: ConnectionContext, - log: LoggingAdapter): Flow[HttpRequest, HttpResponse, Future[OutgoingConnection]] = { - val hostHeader = if (port == connectionContext.defaultPort) Host(host) else Host(host, port) - val layer = clientLayer(hostHeader, settings, log) - layer.joinMat(_outgoingTlsConnectionLayer(host, port, localAddress, settings, connectionContext, log))(Keep.right) - } - - private def _outgoingTlsConnectionLayer(host: String, port: Int, localAddress: Option[InetSocketAddress], - settings: ClientConnectionSettings, connectionContext: ConnectionContext, - log: LoggingAdapter): Flow[SslTlsOutbound, SslTlsInbound, Future[OutgoingConnection]] = { - val tlsStage = sslTlsStage(connectionContext, Client, Some(host → port)) - // The InetSocketAddress representing the remote address must be created unresolved because akka.io.TcpOutgoingConnection will - // not attempt DNS resolution if the InetSocketAddress is already resolved. That behavior is problematic when it comes to - // connection pools since it means that new connections opened by the pool in the future can end up using a stale IP address. - // By passing an unresolved InetSocketAddress instead, we ensure that DNS resolution is performed for every new connection. - val transportFlow = Tcp().outgoingConnection(InetSocketAddress.createUnresolved(host, port), localAddress, - settings.socketOptions, halfClose = true, settings.connectingTimeout, settings.idleTimeout) - - tlsStage.joinMat(transportFlow) { (_, tcpConnFuture) ⇒ - import system.dispatcher - tcpConnFuture map { tcpConn ⇒ OutgoingConnection(tcpConn.localAddress, tcpConn.remoteAddress) } - } - } - - type ClientLayer = Http.ClientLayer - - /** - * Constructs a [[akka.http.scaladsl.Http.ClientLayer]] stage using the configured default [[akka.http.scaladsl.settings.ClientConnectionSettings]], - * configured using the `akka.http.client` config section. - */ - def clientLayer(hostHeader: Host): ClientLayer = - clientLayer(hostHeader, ClientConnectionSettings(system)) - - /** - * Constructs a [[akka.http.scaladsl.Http.ClientLayer]] stage using the given [[akka.http.scaladsl.settings.ClientConnectionSettings]]. - */ - def clientLayer( - hostHeader: Host, - settings: ClientConnectionSettings, - log: LoggingAdapter = system.log): ClientLayer = - OutgoingConnectionBlueprint(hostHeader, settings, log) - - // ** CONNECTION POOL ** // - - /** - * Starts a new connection pool to the given host and configuration and returns a [[akka.stream.scaladsl.Flow]] which dispatches - * the requests from all its materializations across this pool. - * While the started host connection pool internally shuts itself down automatically after the configured idle - * timeout it will spin itself up again if more requests arrive from an existing or a new client flow - * materialization. The returned flow therefore remains usable for the full lifetime of the application. - * - * Since the underlying transport usually comprises more than a single connection the produced flow might generate - * responses in an order that doesn't directly match the consumed requests. - * For example, if two requests A and B enter the flow in that order the response for B might be produced before the - * response for A. - * In order to allow for easy response-to-request association the flow takes in a custom, opaque context - * object of type `T` from the application which is emitted together with the corresponding response. - * - * To configure additional settings for the pool (and requests made using it), - * use the `akka.http.host-connection-pool` config section or pass in a [[ConnectionPoolSettings]] explicitly. - */ - def newHostConnectionPool[T](host: String, port: Int = 80, - settings: ConnectionPoolSettings = defaultConnectionPoolSettings, - log: LoggingAdapter = system.log)(implicit fm: Materializer): Flow[(HttpRequest, T), (Try[HttpResponse], T), HostConnectionPool] = { - val cps = ConnectionPoolSetup(settings, ConnectionContext.noEncryption(), log) - newHostConnectionPool(HostConnectionPoolSetup(host, port, cps)) - } - - /** - * Same as [[#newHostConnectionPool]] but for encrypted (HTTPS) connections. - * - * If an explicit [[ConnectionContext]] is given then it rather than the configured default [[ConnectionContext]] will be used - * for encryption on the connections. - * - * To configure additional settings for the pool (and requests made using it), - * use the `akka.http.host-connection-pool` config section or pass in a [[ConnectionPoolSettings]] explicitly. - */ - def newHostConnectionPoolHttps[T](host: String, port: Int = 443, - connectionContext: HttpsConnectionContext = defaultClientHttpsContext, - settings: ConnectionPoolSettings = defaultConnectionPoolSettings, - log: LoggingAdapter = system.log)(implicit fm: Materializer): Flow[(HttpRequest, T), (Try[HttpResponse], T), HostConnectionPool] = { - val cps = ConnectionPoolSetup(settings, connectionContext, log) - newHostConnectionPool(HostConnectionPoolSetup(host, port, cps)) - } - - /** - * INTERNAL API - * - * Starts a new connection pool to the given host and configuration and returns a [[akka.stream.scaladsl.Flow]] which dispatches - * the requests from all its materializations across this pool. - * While the started host connection pool internally shuts itself down automatically after the configured idle - * timeout it will spin itself up again if more requests arrive from an existing or a new client flow - * materialization. The returned flow therefore remains usable for the full lifetime of the application. - * - * Since the underlying transport usually comprises more than a single connection the produced flow might generate - * responses in an order that doesn't directly match the consumed requests. - * For example, if two requests A and B enter the flow in that order the response for B might be produced before the - * response for A. - * In order to allow for easy response-to-request association the flow takes in a custom, opaque context - * object of type `T` from the application which is emitted together with the corresponding response. - */ - private[akka] def newHostConnectionPool[T](setup: HostConnectionPoolSetup)( - implicit - fm: Materializer): Flow[(HttpRequest, T), (Try[HttpResponse], T), HostConnectionPool] = { - val gateway = new PoolGateway(poolMasterActorRef, setup, PoolGateway.newUniqueGatewayIdentifier) - gatewayClientFlow(setup, gateway.startPool()) - } - - /** - * Returns a [[akka.stream.scaladsl.Flow]] which dispatches incoming HTTP requests to the per-ActorSystem pool of outgoing - * HTTP connections to the given target host endpoint. For every ActorSystem, target host and pool - * configuration a separate connection pool is maintained. - * The HTTP layer transparently manages idle shutdown and restarting of connections pools as configured. - * The returned [[akka.stream.scaladsl.Flow]] instances therefore remain valid throughout the lifetime of the application. - * - * The internal caching logic guarantees that there will never be more than a single pool running for the - * given target host endpoint and configuration (in this ActorSystem). - * - * Since the underlying transport usually comprises more than a single connection the produced flow might generate - * responses in an order that doesn't directly match the consumed requests. - * For example, if two requests A and B enter the flow in that order the response for B might be produced before the - * response for A. - * In order to allow for easy response-to-request association the flow takes in a custom, opaque context - * object of type `T` from the application which is emitted together with the corresponding response. - * - * To configure additional settings for the pool (and requests made using it), - * use the `akka.http.host-connection-pool` config section or pass in a [[ConnectionPoolSettings]] explicitly. - */ - def cachedHostConnectionPool[T](host: String, port: Int = 80, - settings: ConnectionPoolSettings = defaultConnectionPoolSettings, - log: LoggingAdapter = system.log)(implicit fm: Materializer): Flow[(HttpRequest, T), (Try[HttpResponse], T), HostConnectionPool] = { - val cps = ConnectionPoolSetup(settings, ConnectionContext.noEncryption(), log) - val setup = HostConnectionPoolSetup(host, port, cps) - cachedHostConnectionPool(setup) - } - - /** - * Same as [[#cachedHostConnectionPool]] but for encrypted (HTTPS) connections. - * - * If an explicit [[ConnectionContext]] is given then it rather than the configured default [[ConnectionContext]] will be used - * for encryption on the connections. - * - * To configure additional settings for the pool (and requests made using it), - * use the `akka.http.host-connection-pool` config section or pass in a [[ConnectionPoolSettings]] explicitly. - */ - def cachedHostConnectionPoolHttps[T](host: String, port: Int = 443, - connectionContext: HttpsConnectionContext = defaultClientHttpsContext, - settings: ConnectionPoolSettings = defaultConnectionPoolSettings, - log: LoggingAdapter = system.log)(implicit fm: Materializer): Flow[(HttpRequest, T), (Try[HttpResponse], T), HostConnectionPool] = { - val cps = ConnectionPoolSetup(settings, connectionContext, log) - val setup = HostConnectionPoolSetup(host, port, cps) - cachedHostConnectionPool(setup) - } - - /** - * Returns a [[akka.stream.scaladsl.Flow]] which dispatches incoming HTTP requests to the per-ActorSystem pool of outgoing - * HTTP connections to the given target host endpoint. For every ActorSystem, target host and pool - * configuration a separate connection pool is maintained. - * The HTTP layer transparently manages idle shutdown and restarting of connections pools as configured. - * The returned [[akka.stream.scaladsl.Flow]] instances therefore remain valid throughout the lifetime of the application. - * - * The internal caching logic guarantees that there will never be more than a single pool running for the - * given target host endpoint and configuration (in this ActorSystem). - * - * Since the underlying transport usually comprises more than a single connection the produced flow might generate - * responses in an order that doesn't directly match the consumed requests. - * For example, if two requests A and B enter the flow in that order the response for B might be produced before the - * response for A. - * In order to allow for easy response-to-request association the flow takes in a custom, opaque context - * object of type `T` from the application which is emitted together with the corresponding response. - */ - private def cachedHostConnectionPool[T](setup: HostConnectionPoolSetup)( - implicit - fm: Materializer): Flow[(HttpRequest, T), (Try[HttpResponse], T), HostConnectionPool] = { - gatewayClientFlow(setup, sharedGateway(setup).startPool()) - } - - /** - * Creates a new "super connection pool flow", which routes incoming requests to a (cached) host connection pool - * depending on their respective effective URIs. Note that incoming requests must have an absolute URI. - * - * If an explicit [[ConnectionContext]] is given then it rather than the configured default [[ConnectionContext]] will be used - * for setting up HTTPS connection pools, if required. - * - * Since the underlying transport usually comprises more than a single connection the produced flow might generate - * responses in an order that doesn't directly match the consumed requests. - * For example, if two requests A and B enter the flow in that order the response for B might be produced before the - * response for A. - * In order to allow for easy response-to-request association the flow takes in a custom, opaque context - * object of type `T` from the application which is emitted together with the corresponding response. - * - * To configure additional settings for the pool (and requests made using it), - * use the `akka.http.host-connection-pool` config section or pass in a [[ConnectionPoolSettings]] explicitly. - */ - def superPool[T]( - connectionContext: HttpsConnectionContext = defaultClientHttpsContext, - settings: ConnectionPoolSettings = defaultConnectionPoolSettings, - log: LoggingAdapter = system.log)(implicit fm: Materializer): Flow[(HttpRequest, T), (Try[HttpResponse], T), NotUsed] = - clientFlow[T](settings) { request ⇒ request → sharedGateway(request, settings, connectionContext, log) } - - /** - * Fires a single [[akka.http.scaladsl.model.HttpRequest]] across the (cached) host connection pool for the request's - * effective URI to produce a response future. - * - * If an explicit [[ConnectionContext]] is given then it rather than the configured default [[ConnectionContext]] will be used - * for setting up the HTTPS connection pool, if the request is targeted towards an `https` endpoint. - * - * Note that the request must have an absolute URI, otherwise the future will be completed with an error. - */ - def singleRequest( - request: HttpRequest, - connectionContext: HttpsConnectionContext = defaultClientHttpsContext, - settings: ConnectionPoolSettings = defaultConnectionPoolSettings, - log: LoggingAdapter = system.log)(implicit fm: Materializer): Future[HttpResponse] = - try { - val gateway = sharedGateway(request, settings, connectionContext, log) - gateway(request) - } catch { - case e: IllegalUriException ⇒ FastFuture.failed(e) - } - - /** - * Constructs a [[akka.http.scaladsl.Http.WebSocketClientLayer]] stage using the configured default [[akka.http.scaladsl.settings.ClientConnectionSettings]], - * configured using the `akka.http.client` config section. - * - * The layer is not reusable and must only be materialized once. - */ - def webSocketClientLayer( - request: WebSocketRequest, - settings: ClientConnectionSettings = ClientConnectionSettings(system), - log: LoggingAdapter = system.log): Http.WebSocketClientLayer = - WebSocketClientBlueprint(request, settings, log) - - /** - * Constructs a flow that once materialized establishes a WebSocket connection to the given Uri. - * - * The layer is not reusable and must only be materialized once. - */ - def webSocketClientFlow( - request: WebSocketRequest, - connectionContext: ConnectionContext = defaultClientHttpsContext, - localAddress: Option[InetSocketAddress] = None, - settings: ClientConnectionSettings = ClientConnectionSettings(system), - log: LoggingAdapter = system.log): Flow[Message, Message, Future[WebSocketUpgradeResponse]] = { - import request.uri - require(uri.isAbsolute, s"WebSocket request URI must be absolute but was '$uri'") - - val ctx = uri.scheme match { - case "ws" ⇒ ConnectionContext.noEncryption() - case "wss" if connectionContext.isSecure ⇒ connectionContext - case "wss" ⇒ throw new IllegalArgumentException("Provided connectionContext is not secure, yet request to secure `wss` endpoint detected!") - case scheme ⇒ - throw new IllegalArgumentException(s"Illegal URI scheme '$scheme' in '$uri' for WebSocket request. " + - s"WebSocket requests must use either 'ws' or 'wss'") - } - val host = uri.authority.host.address - val port = uri.effectivePort - - webSocketClientLayer(request, settings, log) - .joinMat(_outgoingTlsConnectionLayer(host, port, localAddress, settings, ctx, log))(Keep.left) - } - - /** - * Runs a single WebSocket conversation given a Uri and a flow that represents the client side of the - * WebSocket conversation. - */ - def singleWebSocketRequest[T]( - request: WebSocketRequest, - clientFlow: Flow[Message, Message, T], - connectionContext: ConnectionContext = defaultClientHttpsContext, - localAddress: Option[InetSocketAddress] = None, - settings: ClientConnectionSettings = ClientConnectionSettings(system), - log: LoggingAdapter = system.log)(implicit mat: Materializer): (Future[WebSocketUpgradeResponse], T) = - webSocketClientFlow(request, connectionContext, localAddress, settings, log) - .joinMat(clientFlow)(Keep.both).run() - - /** - * Triggers an orderly shutdown of all host connections pools currently maintained by the [[akka.actor.ActorSystem]]. - * The returned future is completed when all pools that were live at the time of this method call - * have completed their shutdown process. - * - * If existing pool client flows are re-used or new ones materialized concurrently with or after this - * method call the respective connection pools will be restarted and not contribute to the returned future. - */ - def shutdownAllConnectionPools(): Future[Unit] = { - val shutdownCompletedPromise = Promise[Done]() - poolMasterActorRef ! ShutdownAll(shutdownCompletedPromise) - shutdownCompletedPromise.future.map(_ ⇒ ())(system.dispatcher) - } - - /** - * Gets the current default server-side [[ConnectionContext]] – defaults to plain HTTP. - * Can be modified using [[setDefaultServerHttpContext]], and will then apply for servers bound after that call has completed. - */ - def defaultServerHttpContext: ConnectionContext = - synchronized { - if (_defaultServerConnectionContext == null) - _defaultServerConnectionContext = ConnectionContext.noEncryption() - _defaultServerConnectionContext - } - - /** - * Sets the default server-side [[ConnectionContext]]. - * If it is an instance of [[HttpsConnectionContext]] then the server will be bound using HTTPS. - */ - def setDefaultServerHttpContext(context: ConnectionContext): Unit = - synchronized { - _defaultServerConnectionContext = context - } - - /** - * Gets the current default client-side [[HttpsConnectionContext]]. - * Defaults used here can be configured using ssl-config or the context can be replaced using [[setDefaultClientHttpsContext]] - */ - def defaultClientHttpsContext: HttpsConnectionContext = - synchronized { - _defaultClientHttpsConnectionContext match { - case null ⇒ - val ctx = createDefaultClientHttpsContext() - _defaultClientHttpsConnectionContext = ctx - ctx - case ctx ⇒ ctx - } - } - - /** - * Sets the default client-side [[HttpsConnectionContext]]. - */ - def setDefaultClientHttpsContext(context: HttpsConnectionContext): Unit = - synchronized { - _defaultClientHttpsConnectionContext = context - } - - private def sharedGateway(request: HttpRequest, settings: ConnectionPoolSettings, connectionContext: ConnectionContext, log: LoggingAdapter): PoolGateway = { - if (request.uri.scheme.nonEmpty && request.uri.authority.nonEmpty) { - val httpsCtx = if (request.uri.scheme.equalsIgnoreCase("https")) connectionContext else ConnectionContext.noEncryption() - val setup = ConnectionPoolSetup(settings, httpsCtx, log) - val host = request.uri.authority.host.toString() - val hcps = HostConnectionPoolSetup(host, request.uri.effectivePort, setup) - sharedGateway(hcps) - } else { - val msg = s"Cannot determine request scheme and target endpoint as ${request.method} request to ${request.uri} doesn't have an absolute URI" - throw new IllegalUriException(ErrorInfo(msg)) - } - } - - private def sharedGateway(hcps: HostConnectionPoolSetup): PoolGateway = - new PoolGateway(poolMasterActorRef, hcps, PoolGateway.SharedGateway)(systemMaterializer) - - private def gatewayClientFlow[T]( - hcps: HostConnectionPoolSetup, - gateway: PoolGateway)( - implicit - fm: Materializer): Flow[(HttpRequest, T), (Try[HttpResponse], T), HostConnectionPool] = - clientFlow[T](hcps.setup.settings)(_ → gateway) - .mapMaterializedValue(_ ⇒ HostConnectionPool(hcps)(gateway)) - - private def clientFlow[T](settings: ConnectionPoolSettings)(f: HttpRequest ⇒ (HttpRequest, PoolGateway))( - implicit - system: ActorSystem, fm: Materializer): Flow[(HttpRequest, T), (Try[HttpResponse], T), NotUsed] = { - // a connection pool can never have more than pipeliningLimit * maxConnections requests in flight at any point - val parallelism = settings.pipeliningLimit * settings.maxConnections - Flow[(HttpRequest, T)].mapAsyncUnordered(parallelism) { - case (request, userContext) ⇒ - val (effectiveRequest, gateway) = f(request) - val result = Promise[(Try[HttpResponse], T)]() // TODO: simplify to `transformWith` when on Scala 2.12 - gateway(effectiveRequest).onComplete(responseTry ⇒ result.success(responseTry → userContext))(fm.executionContext) - result.future - } - } - - /** Creates real or placebo SslTls stage based on if ConnectionContext is HTTPS or not. */ - private[http] def sslTlsStage(connectionContext: ConnectionContext, role: TLSRole, hostInfo: Option[(String, Int)] = None) = - connectionContext match { - case hctx: HttpsConnectionContext ⇒ TLS(hctx.sslContext, connectionContext.sslConfig, hctx.firstSession, role, hostInfo = hostInfo) - case other ⇒ TLSPlacebo() // if it's not HTTPS, we don't enable SSL/TLS - } - - /** - * INTERNAL API - * - * For testing only - */ - private[scaladsl] def poolSize: Future[Int] = { - val sizePromise = Promise[Int]() - poolMasterActorRef ! PoolSize(sizePromise) - sizePromise.future - } -} - -object Http extends ExtensionId[HttpExt] with ExtensionIdProvider { - - //#server-layer - /** - * The type of the server-side HTTP layer as a stand-alone BidiFlow - * that can be put atop the TCP layer to form an HTTP server. - * - * {{{ - * +------+ - * HttpResponse ~>| |~> SslTlsOutbound - * | bidi | - * HttpRequest <~| |<~ SslTlsInbound - * +------+ - * }}} - */ - type ServerLayer = BidiFlow[HttpResponse, SslTlsOutbound, SslTlsInbound, HttpRequest, NotUsed] - //# - - //#client-layer - /** - * The type of the client-side HTTP layer as a stand-alone BidiFlow - * that can be put atop the TCP layer to form an HTTP client. - * - * {{{ - * +------+ - * HttpRequest ~>| |~> SslTlsOutbound - * | bidi | - * HttpResponse <~| |<~ SslTlsInbound - * +------+ - * }}} - */ - type ClientLayer = BidiFlow[HttpRequest, SslTlsOutbound, SslTlsInbound, HttpResponse, NotUsed] - //# - - /** - * The type of the client-side WebSocket layer as a stand-alone BidiFlow - * that can be put atop the TCP layer to form an HTTP client. - * - * {{{ - * +------+ - * ws.Message ~>| |~> SslTlsOutbound - * | bidi | - * ws.Message <~| |<~ SslTlsInbound - * +------+ - * }}} - */ - type WebSocketClientLayer = BidiFlow[Message, SslTlsOutbound, SslTlsInbound, Message, Future[WebSocketUpgradeResponse]] - - /** - * Represents a prospective HTTP server binding. - * - * @param localAddress The local address of the endpoint bound by the materialization of the `connections` [[akka.stream.scaladsl.Source]] - * - */ - final case class ServerBinding(localAddress: InetSocketAddress)(private val unbindAction: () ⇒ Future[Unit]) { - - /** - * Asynchronously triggers the unbinding of the port that was bound by the materialization of the `connections` - * [[akka.stream.scaladsl.Source]] - * - * The produced [[scala.concurrent.Future]] is fulfilled when the unbinding has been completed. - */ - def unbind(): Future[Unit] = unbindAction() - } - - /** - * Represents one accepted incoming HTTP connection. - */ - final case class IncomingConnection( - localAddress: InetSocketAddress, - remoteAddress: InetSocketAddress, - flow: Flow[HttpResponse, HttpRequest, NotUsed]) { - - /** - * Handles the connection with the given flow, which is materialized exactly once - * and the respective materialization result returned. - */ - def handleWith[Mat](handler: Flow[HttpRequest, HttpResponse, Mat])(implicit fm: Materializer): Mat = - flow.joinMat(handler)(Keep.right).run() - - /** - * Handles the connection with the given handler function. - */ - def handleWithSyncHandler(handler: HttpRequest ⇒ HttpResponse)(implicit fm: Materializer): Unit = - handleWith(Flow[HttpRequest].map(handler)) - - /** - * Handles the connection with the given handler function. - */ - def handleWithAsyncHandler(handler: HttpRequest ⇒ Future[HttpResponse], parallelism: Int = 1)(implicit fm: Materializer): Unit = - handleWith(Flow[HttpRequest].mapAsync(parallelism)(handler)) - } - - /** - * Represents a prospective outgoing HTTP connection. - */ - final case class OutgoingConnection(localAddress: InetSocketAddress, remoteAddress: InetSocketAddress) - - /** - * Represents a connection pool to a specific target host and pool configuration. - */ - final case class HostConnectionPool private[http] (setup: HostConnectionPoolSetup)( - private[http] val gateway: PoolGateway) { // enable test access - - /** - * Asynchronously triggers the shutdown of the host connection pool. - * - * The produced [[scala.concurrent.Future]] is fulfilled when the shutdown has been completed. - */ - def shutdown()(implicit ec: ExecutionContextExecutor): Future[Done] = gateway.shutdown() - - private[http] def toJava = new akka.http.javadsl.HostConnectionPool { - override def setup = HostConnectionPool.this.setup - override def shutdown(executor: ExecutionContextExecutor): CompletionStage[Done] = HostConnectionPool.this.shutdown()(executor).toJava - } - } - - //////////////////// EXTENSION SETUP /////////////////// - - def apply()(implicit system: ActorSystem): HttpExt = super.apply(system) - - def lookup() = Http - - def createExtension(system: ExtendedActorSystem): HttpExt = - new HttpExt(system.settings.config getConfig "akka.http")(system) -} - -/** - * TLS configuration for an HTTPS server binding or client connection. - * For the sslContext please refer to the com.typeasfe.ssl-config library. - * The remaining four parameters configure the initial session that will - * be negotiated, see [[akka.stream.TLSProtocol.NegotiateNewSession]] for details. - */ -trait DefaultSSLContextCreation { - - protected def system: ActorSystem - protected def sslConfig: AkkaSSLConfig - - // --- log warnings --- - private[this] def log = system.log - - def validateAndWarnAboutLooseSettings() = { - val WarningAboutGlobalLoose = "This is very dangerous and may expose you to man-in-the-middle attacks. " + - "If you are forced to interact with a server that is behaving such that you must disable this setting, " + - "please disable it for a given connection instead, by configuring a specific HttpsConnectionContext " + - "for use only for the trusted target that hostname verification would have blocked." - - if (sslConfig.config.loose.disableHostnameVerification) - log.warning("Detected that Hostname Verification is disabled globally (via ssl-config's akka.ssl-config.loose.disableHostnameVerification) for the Http extension! " + - WarningAboutGlobalLoose) - - if (sslConfig.config.loose.disableSNI) { - log.warning("Detected that Server Name Indication (SNI) is disabled globally (via ssl-config's akka.ssl-config.loose.disableSNI) for the Http extension! " + - WarningAboutGlobalLoose) - - } - } - // --- end of log warnings --- - - def createDefaultClientHttpsContext(): HttpsConnectionContext = - createClientHttpsContext(sslConfig) - - // currently the same configuration as client by default, however we should tune this for server-side apropriately (!) - def createServerHttpsContext(sslConfig: AkkaSSLConfig): HttpsConnectionContext = { - log.warning("Automatic server-side configuration is not supported yet, will attempt to use client-side settings. " + - "Instead it is recommended to construct the Servers HttpsConnectionContext manually (via SSLContext).") - createClientHttpsContext(sslConfig) - } - - def createClientHttpsContext(sslConfig: AkkaSSLConfig): HttpsConnectionContext = { - val config = sslConfig.config - - val log = Logging(system, getClass) - val mkLogger = new AkkaLoggerFactory(system) - - // initial ssl context! - val sslContext = if (sslConfig.config.default) { - log.debug("buildSSLContext: ssl-config.default is true, using default SSLContext") - sslConfig.validateDefaultTrustManager(config) - SSLContext.getDefault - } else { - // break out the static methods as much as we can... - val keyManagerFactory = sslConfig.buildKeyManagerFactory(config) - val trustManagerFactory = sslConfig.buildTrustManagerFactory(config) - new ConfigSSLContextBuilder(mkLogger, config, keyManagerFactory, trustManagerFactory).build() - } - - // protocols! - val defaultParams = sslContext.getDefaultSSLParameters - val defaultProtocols = defaultParams.getProtocols - val protocols = sslConfig.configureProtocols(defaultProtocols, config) - defaultParams.setProtocols(protocols) - - // ciphers! - val defaultCiphers = defaultParams.getCipherSuites - val cipherSuites = sslConfig.configureCipherSuites(defaultCiphers, config) - defaultParams.setCipherSuites(cipherSuites) - - // auth! - import com.typesafe.sslconfig.ssl.{ ClientAuth ⇒ SslClientAuth } - val clientAuth = config.sslParametersConfig.clientAuth match { - case SslClientAuth.Default ⇒ None - case SslClientAuth.Want ⇒ Some(TLSClientAuth.Want) - case SslClientAuth.Need ⇒ Some(TLSClientAuth.Need) - case SslClientAuth.None ⇒ Some(TLSClientAuth.None) - } - - // hostname! - if (!sslConfig.config.loose.disableHostnameVerification) { - defaultParams.setEndpointIdentificationAlgorithm("https") - } - - new HttpsConnectionContext(sslContext, Some(sslConfig), Some(cipherSuites.toList), Some(defaultProtocols.toList), clientAuth, Some(defaultParams)) - } - -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/TimeoutAccess.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/TimeoutAccess.scala deleted file mode 100644 index 16d3ff885e..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/TimeoutAccess.scala +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl - -import scala.concurrent.duration.Duration -import akka.http.scaladsl.model.{ HttpResponse, HttpRequest } - -/** - * Enables programmatic access to the server-side request timeout logic. - */ -trait TimeoutAccess extends akka.http.javadsl.TimeoutAccess { - - /** - * Tries to set a new timeout. - * The timeout period is measured as of the point in time that the end of the request has been received, - * which may be in the past or in the future! - * Use `Duration.Inf` to completely disable request timeout checking for this request. - * - * Due to the inherent raciness it is not guaranteed that the update will be applied before - * the previously set timeout has expired! - */ - def updateTimeout(timeout: Duration): Unit - - /** - * Tries to set a new timeout handler, which produces the timeout response for a - * given request. Note that the handler must produce the response synchronously and shouldn't block! - * - * Due to the inherent raciness it is not guaranteed that the update will be applied before - * the previously set timeout has expired! - */ - def updateHandler(handler: HttpRequest ⇒ HttpResponse): Unit - - /** - * Tries to set a new timeout and handler at the same time. - * - * Due to the inherent raciness it is not guaranteed that the update will be applied before - * the previously set timeout has expired! - */ - def update(timeout: Duration, handler: HttpRequest ⇒ HttpResponse): Unit -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/ContentRange.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/ContentRange.scala deleted file mode 100644 index 087e5de8e7..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/ContentRange.scala +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model - -import java.util.{ OptionalLong, Optional } -import akka.http.impl.util.{ Rendering, ValueRenderable } -import akka.http.javadsl.{ model ⇒ jm } -import scala.compat.java8.OptionConverters._ - -sealed trait ContentRange extends jm.ContentRange with ValueRenderable { - // default implementations to override - def isSatisfiable: Boolean = false - def isOther: Boolean = false - def getSatisfiableFirst: OptionalLong = OptionalLong.empty() - def getSatisfiableLast: OptionalLong = OptionalLong.empty() - def getOtherValue: Optional[String] = Optional.empty() -} - -sealed trait ByteContentRange extends ContentRange { - def instanceLength: Option[Long] - - /** Java API */ - def isByteContentRange: Boolean = true - /** Java API */ - def getInstanceLength: OptionalLong = instanceLength.asPrimitive -} - -// http://tools.ietf.org/html/rfc7233#section-4.2 -object ContentRange { - def apply(first: Long, last: Long): Default = apply(first, last, None) - def apply(first: Long, last: Long, instanceLength: Long): Default = apply(first, last, Some(instanceLength)) - def apply(first: Long, last: Long, instanceLength: Option[Long]): Default = Default(first, last, instanceLength) - - /** - * Models a satisfiable HTTP content-range. - */ - final case class Default(first: Long, last: Long, instanceLength: Option[Long]) extends ByteContentRange { - require(0 <= first && first <= last, "first must be >= 0 and <= last") - require(instanceLength.isEmpty || instanceLength.get > last, "instanceLength must be empty or > last") - - def render[R <: Rendering](r: R): r.type = { - r ~~ first ~~ '-' ~~ last ~~ '/' - if (instanceLength.isDefined) r ~~ instanceLength.get else r ~~ '*' - } - - /** Java API */ - override def isSatisfiable: Boolean = true - /** Java API */ - override def getSatisfiableFirst: OptionalLong = OptionalLong.of(first) - /** Java API */ - override def getSatisfiableLast: OptionalLong = OptionalLong.of(last) - } - - /** - * An unsatisfiable content-range. - */ - final case class Unsatisfiable(length: Long) extends ByteContentRange { - val instanceLength = Some(length) - def render[R <: Rendering](r: R): r.type = r ~~ "*/" ~~ length - } - - /** - * An `other-range-resp`. - */ - final case class Other(override val value: String) extends ContentRange { - def render[R <: Rendering](r: R): r.type = r ~~ value - - /** Java API */ - def isByteContentRange = false - /** Java API */ - def getInstanceLength: OptionalLong = OptionalLong.empty() - /** Java API */ - override def getOtherValue: Optional[String] = Optional.of(value) - } -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/ContentType.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/ContentType.scala deleted file mode 100644 index 6b477ce344..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/ContentType.scala +++ /dev/null @@ -1,112 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model - -import language.implicitConversions -import akka.http.impl.util._ -import java.util.Optional -import akka.http.javadsl.{ model ⇒ jm } -import akka.http.impl.util.JavaMapping.Implicits._ - -final case class ContentTypeRange(mediaRange: MediaRange, charsetRange: HttpCharsetRange) extends jm.ContentTypeRange with ValueRenderable { - def matches(contentType: jm.ContentType) = - contentType match { - case ContentType.Binary(mt) ⇒ mediaRange.matches(mt) - case x: ContentType.NonBinary ⇒ mediaRange.matches(x.mediaType) && charsetRange.matches(x.charset) - } - - def render[R <: Rendering](r: R): r.type = charsetRange match { - case HttpCharsetRange.`*` ⇒ r ~~ mediaRange - case x ⇒ r ~~ mediaRange ~~ ContentType.`; charset=` ~~ x - } -} - -object ContentTypeRange { - val `*` = ContentTypeRange(MediaRanges.`*/*`) - - implicit def apply(mediaType: MediaType): ContentTypeRange = apply(mediaType, HttpCharsetRange.`*`) - implicit def apply(mediaRange: MediaRange): ContentTypeRange = apply(mediaRange, HttpCharsetRange.`*`) - implicit def apply(contentType: ContentType): ContentTypeRange = - contentType match { - case ContentType.Binary(mt) ⇒ ContentTypeRange(mt) - case ContentType.WithFixedCharset(mt) ⇒ ContentTypeRange(mt) - case ContentType.WithCharset(mt, cs) ⇒ ContentTypeRange(mt, cs) - } -} - -/** - * A `ContentType` represents a specific MediaType / HttpCharset combination. - * - * If the MediaType is not flexible with regard to the charset used, e.g. because it's a binary MediaType or - * the charset is fixed, then the `ContentType` is a simple wrapper. - */ -sealed trait ContentType extends jm.ContentType with ValueRenderable { - def mediaType: MediaType - def charsetOption: Option[HttpCharset] - - private[http] def render[R <: Rendering](r: R): r.type = r ~~ mediaType - - /** Java API */ - def getCharsetOption: Optional[jm.HttpCharset] = charsetOption.asJava -} - -object ContentType { - final case class Binary(mediaType: MediaType.Binary) extends jm.ContentType.Binary with ContentType { - def binary = true - def charsetOption = None - } - - sealed trait NonBinary extends jm.ContentType.NonBinary with ContentType { - def binary = false - def charset: HttpCharset - def charsetOption = Some(charset) - } - - final case class WithFixedCharset(val mediaType: MediaType.WithFixedCharset) - extends jm.ContentType.WithFixedCharset with NonBinary { - def charset = mediaType.charset - } - - final case class WithCharset(val mediaType: MediaType.WithOpenCharset, val charset: HttpCharset) - extends jm.ContentType.WithCharset with NonBinary { - - private[http] override def render[R <: Rendering](r: R): r.type = - super.render(r) ~~ ContentType.`; charset=` ~~ charset - } - - implicit def apply(mediaType: MediaType.Binary): Binary = Binary(mediaType) - implicit def apply(mediaType: MediaType.WithFixedCharset): WithFixedCharset = WithFixedCharset(mediaType) - def apply(mediaType: MediaType.WithOpenCharset, charset: HttpCharset): WithCharset = WithCharset(mediaType, charset) - def apply(mediaType: MediaType, charset: () ⇒ HttpCharset): ContentType = - mediaType match { - case x: MediaType.Binary ⇒ ContentType(x) - case x: MediaType.WithFixedCharset ⇒ ContentType(x) - case x: MediaType.WithOpenCharset ⇒ ContentType(x, charset()) - } - - def unapply(contentType: ContentType): Option[(MediaType, Option[HttpCharset])] = - Some(contentType.mediaType → contentType.charsetOption) - - /** - * Tries to parse a `ContentType` value from the given String. Returns `Right(contentType)` if successful and - * `Left(errors)` otherwise. - */ - def parse(value: String): Either[List[ErrorInfo], ContentType] = - headers.`Content-Type`.parseFromValueString(value).right.map(_.contentType) - - private[http] case object `; charset=` extends SingletonValueRenderable -} - -object ContentTypes { - val `application/json` = ContentType(MediaTypes.`application/json`) - val `application/octet-stream` = ContentType(MediaTypes.`application/octet-stream`) - val `text/plain(UTF-8)` = MediaTypes.`text/plain` withCharset HttpCharsets.`UTF-8` - val `text/html(UTF-8)` = MediaTypes.`text/html` withCharset HttpCharsets.`UTF-8` - val `text/xml(UTF-8)` = MediaTypes.`text/xml` withCharset HttpCharsets.`UTF-8` - val `text/csv(UTF-8)` = MediaTypes.`text/csv` withCharset HttpCharsets.`UTF-8` - - // used for explicitly suppressing the rendering of Content-Type headers on requests and responses - val NoContentType = ContentType(MediaTypes.NoMediaType) -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/DateTime.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/DateTime.scala deleted file mode 100644 index f70316b5e3..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/DateTime.scala +++ /dev/null @@ -1,275 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model - -import akka.http.impl.util._ - -/** - * Immutable, fast and efficient Date + Time implementation without any dependencies. - * Does not support TimeZones, all DateTime values are always GMT based. - * Note that this implementation discards milliseconds (i.e. rounds down to full seconds). - */ -final case class DateTime private ( - year: Int, // the year - month: Int, // the month of the year. January is 1. - day: Int, // the day of the month. The first day is 1. - hour: Int, // the hour of the day. The first hour is 0. - minute: Int, // the minute of the hour. The first minute is 0. - second: Int, // the second of the minute. The first second is 0. - weekday: Int, // the day of the week. Sunday is 0. - clicks: Long, // milliseconds since January 1, 1970, 00:00:00 GMT - isLeapYear: Boolean) extends akka.http.javadsl.model.DateTime with Ordered[DateTime] with Renderable { - /** - * The day of the week as a 3 letter abbreviation: - * `Sun`, `Mon`, `Tue`, `Wed`, `Thu`, `Fri` or `Sat` - */ - def weekdayStr: String = DateTime.weekday(weekday) - - /** - * The month as a 3 letter abbreviation: - * `Jan`, `Feb`, `Mar`, `Apr`, `May`, `Jun`, `Jul`, `Aug`, `Sep`, `Oct`, `Nov` or `Dec` - */ - def monthStr: String = DateTime.month(month - 1) - - /** - * Creates a new `DateTime` that represents the point in time the given number of ms later. - */ - def +(millis: Long): DateTime = DateTime(clicks + millis) - - /** - * Creates a new `DateTime` that represents the point in time the given number of ms earlier. - */ - def -(millis: Long): DateTime = DateTime(clicks - millis) - - /** - * Creates a new `DateTime` that represents the point in time the given number of ms earlier. - */ - def minus(millis: Long): DateTime = this - millis - - /** - * Creates a new `DateTime` that represents the point in time the given number of ms later. - */ - def plus(millis: Long): DateTime = this + millis - - /** - * `yyyy-mm-ddThh:mm:ss` - */ - def render[R <: Rendering](r: R): r.type = renderIsoDateTimeString(r) - - /** - * `yyyy-mm-ddThh:mm:ss` - */ - override def toString = toIsoDateTimeString - - /** - * `yyyy-mm-dd` - */ - def renderIsoDate[R <: Rendering](r: R): r.type = put_##(put_##(r ~~ year ~~ '-', month) ~~ '-', day) - - /** - * `yyyy-mm-dd` - */ - def toIsoDateString = renderIsoDate(new StringRendering).get - - /** - * `yyyy-mm-ddThh:mm:ss` - */ - def renderIsoDateTimeString[R <: Rendering](r: R): r.type = - put_##(put_##(put_##(renderIsoDate(r) ~~ 'T', hour) ~~ ':', minute) ~~ ':', second) - - /** - * `yyyy-mm-ddThh:mm:ss` - */ - def toIsoDateTimeString = renderIsoDateTimeString(new StringRendering).get - - /** - * `yyyy-mm-dd hh:mm:ss` - */ - def renderIsoLikeDateTimeString[R <: Rendering](r: R): r.type = - put_##(put_##(put_##(renderIsoDate(r) ~~ ' ', hour) ~~ ':', minute) ~~ ':', second) - - /** - * `yyyy-mm-dd hh:mm:ss` - */ - def toIsoLikeDateTimeString = renderIsoLikeDateTimeString(new StringRendering).get - - /** - * RFC1123 date string, e.g. `Sun, 06 Nov 1994 08:49:37 GMT` - */ - def renderRfc1123DateTimeString[R <: Rendering](r: R): r.type = - put_##(put_##(put_##(put_##(r ~~ weekdayStr ~~ ',' ~~ ' ', day) ~~ ' ' ~~ monthStr ~~ ' ' ~~ year ~~ ' ', hour) ~~ ':', minute) ~~ ':', second) ~~ " GMT" - - /** - * RFC1123 date string, e.g. `Sun, 06 Nov 1994 08:49:37 GMT` - */ - def toRfc1123DateTimeString = renderRfc1123DateTimeString(new StringRendering).get - - private def put_##[R <: Rendering](r: R, i: Int): r.type = r ~~ (i / 10 + '0').toChar ~~ (i % 10 + '0').toChar - - def compare(that: DateTime): Int = math.signum(clicks - that.clicks).toInt - - override def hashCode() = clicks.## - - override def equals(obj: Any) = obj match { - case x: DateTime ⇒ x.clicks == clicks - case _ ⇒ false - } -} - -object DateTime { - private[this] val WEEKDAYS = Array("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat") - private[this] val MONTHS = Array("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec") - - /** - * Returns the three-letter string for the weekday with the given index. Sunday is zero. - */ - def weekday(index: Int): String = WEEKDAYS(index) - - /** - * Returns the three-letter string for the month with the given index. January is zero. - */ - def month(index: Int): String = MONTHS(index) - - val MinValue = DateTime(1800, 1, 1) - val MaxValue = DateTime(2199, 12, 31, 23, 59, 59) - - /** - * Creates a new `DateTime` with the given properties. - * Note that this implementation discards milliseconds (i.e. rounds down to full seconds). - */ - def apply(year: Int, month: Int, day: Int, hour: Int = 0, minute: Int = 0, second: Int = 0): DateTime = { - require(1800 <= year && year <= 9999, "year must be >= 1800 and <= 9999") - require(1 <= month && month <= 12, "month must be >= 1 and <= 12") - require(1 <= day && day <= 31, "day must be >= 1 and <= 31") - require(0 <= hour && hour <= 23, "hour must be >= 0 and <= 23") - require(0 <= minute && minute <= 59, "minute_ must be >= 0 and <= 59") - require(0 <= second && second <= 59, "second must be >= 0 and <= 59") - - // compute yearday from month/monthday - val m = month - 1 - val m7 = m % 7 - var d = m7 * 30 + ((m7 + 1) >> 1) + day - val isLeap = isLeapYear(year) - if (m >= 7) d += 214 - if (d >= 61) d -= 1 // skip non-existent Feb 30 - if (!isLeap && (d >= 60)) d -= 1 // skip non-existent Feb 29 - - // convert year/yearday to days since Jan 1, 1970, 00:00:00 - val y = year - 1 - val yd = y / 100 - d += y * 365 + (y >> 2) - yd + (yd >> 2) - val dn = d - (1969 * 365 + 492 - 19 + 4) - val c = (dn - 1) * 86400L + hour * 3600L + minute * 60L + second // seconds since Jan 1, 1970, 00:00:00 - - new DateTime(year, month, day, hour, minute, second, weekday = d % 7, clicks = c * 1000, isLeapYear = isLeap) - } - - /** - * Creates a new `DateTime` from the number of milli seconds - * since the start of "the epoch", namely January 1, 1970, 00:00:00 GMT. - * Note that this implementation discards milliseconds (i.e. rounds down to full seconds). - */ - def apply(clicks: Long): DateTime = { - require( - DateTime.MinValue.clicks <= clicks && clicks <= DateTime.MaxValue.clicks, - "DateTime value must be >= " + DateTime.MinValue + " and <= " + DateTime.MaxValue) - - // based on a fast RFC1123 implementation (C) 2000 by Tim Kientzle - val c = clicks - clicks % 1000 - - // compute day number, seconds since beginning of day - var s = c - if (s >= 0) s /= 1000 // seconds since 1 Jan 1970 - else s = (s - 999) / 1000 // floor(sec/1000) - - var dn = (s / 86400).toInt - s %= 86400 // positive seconds since beginning of day - if (s < 0) { s += 86400; dn -= 1 } - dn += 1969 * 365 + 492 - 19 + 4 // days since "1 Jan, year 1" - - // convert days since 1 Jan, year 1 to year/yearday - var y = 400 * (dn / 146097) + 1 - var d = dn % 146097 - if (d == 146096) { y += 399; d = 365 } // last year of 400 is long - else { - y += 100 * (d / 36524) - d %= 36524 - y += (d / 1461) << 2 - d %= 1461 - if (d == 1460) { y += 3; d = 365 } // last year out of 4 is long - else { - y += d / 365 - d %= 365 - } - } - - val isLeap = isLeapYear(y) - - // compute month/monthday from year/yearday - if (!isLeap && (d >= 59)) d += 1 // skip non-existent Feb 29 - if (d >= 60) d += 1 // skip non-existent Feb 30 - val d214 = d % 214 - val d214_61 = d214 % 61 - var mon = ((d214 / 61) << 1) + d214_61 / 31 - if (d > 213) mon += 7 - d = d214_61 % 31 + 1 - - // convert second to hour/min/sec - var m = (s / 60).toInt - s %= 60 - val h = m / 60 - m %= 60 - val w = (dn + 1) % 7 // day of week, 0==Sun - - new DateTime(year = y, month = mon + 1, day = d, hour = h, minute = m, second = s.toInt, weekday = w, clicks = c, - isLeapYear = isLeap) - } - - private def isLeapYear(year: Int): Boolean = - ((year & 0x03) == 0) && { - val q = year / 100 - val r = year % 100 - r != 0 || (q & 0x03) == 0 - } - - /** - * Creates a new `DateTime` instance for the current point in time. - * Note that this implementation discards milliseconds (i.e. rounds down to full seconds). - */ - def now: DateTime = apply(System.currentTimeMillis) - - /** - * Creates a new DateTime instance from the given String, - * if it adheres to the format `yyyy-mm-ddThh:mm:ss[.SSSZ]`. - * Note that this implementation discards milliseconds (i.e. rounds down to full seconds). - */ - def fromIsoDateTimeString(string: String): Option[DateTime] = { - def c(ix: Int) = string.charAt(ix) - def isDigit(c: Char) = '0' <= c && c <= '9' - def i(ix: Int) = { - val x = c(ix) - require(isDigit(x)) - x - '0' - } - def check(len: Int): Boolean = - len match { - case 19 ⇒ c(4) == '-' && c(7) == '-' && c(10) == 'T' && c(13) == ':' && c(16) == ':' - case 24 ⇒ check(19) && c(19) == '.' && isDigit(c(20)) && isDigit(c(21)) && isDigit(c(22)) && c(23) == 'Z' - case _ ⇒ false - } - def mul10(i: Int) = (i << 3) + (i << 1) - if (check(string.length)) { - try { - val year = i(0) * 1000 + i(1) * 100 + mul10(i(2)) + i(3) - val month = mul10(i(5)) + i(6) - val day = mul10(i(8)) + i(9) - val hour = mul10(i(11)) + i(12) - val min = mul10(i(14)) + i(15) - val sec = mul10(i(17)) + i(18) - Some(DateTime(year, month, day, hour, min, sec)) - } catch { case _: IllegalArgumentException ⇒ None } - } else None - } -} \ No newline at end of file diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/ErrorInfo.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/ErrorInfo.scala deleted file mode 100644 index 5f3f4ba701..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/ErrorInfo.scala +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model - -import StatusCodes.ClientError - -/** - * Two-level model of error information. - * The summary should explain what is wrong with the request or response *without* directly - * repeating anything present in the message itself (in order to not open holes for XSS attacks), - * while the detail can contain additional information from any source (even the request itself). - */ -final case class ErrorInfo(summary: String = "", detail: String = "") { - def withSummary(newSummary: String) = copy(summary = newSummary) - def withSummaryPrepended(prefix: String) = withSummary(if (summary.isEmpty) prefix else prefix + ": " + summary) - def withFallbackSummary(fallbackSummary: String) = if (summary.isEmpty) withSummary(fallbackSummary) else this - def formatPretty = if (summary.isEmpty) detail else if (detail.isEmpty) summary else summary + ": " + detail - def format(withDetail: Boolean): String = if (withDetail) formatPretty else summary -} - -object ErrorInfo { - /** - * Allows constructing an `ErrorInfo` from a single string. - * Used for example when catching exceptions generated by the header value parser, which doesn't provide - * summary/details information but structures its exception messages accordingly. - */ - def fromCompoundString(message: String): ErrorInfo = message.split(": ", 2) match { - case Array(summary, detail) ⇒ apply(summary, detail) - case _ ⇒ ErrorInfo("", message) - } -} - -/** Marker for exceptions that provide an ErrorInfo */ -abstract class ExceptionWithErrorInfo(info: ErrorInfo) extends RuntimeException(info.formatPretty) - -case class IllegalUriException(info: ErrorInfo) extends ExceptionWithErrorInfo(info) -object IllegalUriException { - def apply(summary: String, detail: String = ""): IllegalUriException = apply(ErrorInfo(summary, detail)) -} - -case class IllegalHeaderException(info: ErrorInfo) extends ExceptionWithErrorInfo(info) -object IllegalHeaderException { - def apply(summary: String, detail: String = ""): IllegalHeaderException = apply(ErrorInfo(summary, detail)) -} - -case class InvalidContentLengthException(info: ErrorInfo) extends ExceptionWithErrorInfo(info) -object InvalidContentLengthException { - def apply(summary: String, detail: String = ""): InvalidContentLengthException = apply(ErrorInfo(summary, detail)) -} - -case class ParsingException(info: ErrorInfo) extends ExceptionWithErrorInfo(info) -object ParsingException { - def apply(summary: String, detail: String = ""): ParsingException = apply(ErrorInfo(summary, detail)) -} - -case class IllegalRequestException(info: ErrorInfo, status: ClientError) extends ExceptionWithErrorInfo(info) -object IllegalRequestException { - def apply(status: ClientError): IllegalRequestException = apply(ErrorInfo(status.defaultMessage), status) - def apply(status: ClientError, info: ErrorInfo): IllegalRequestException = apply(info.withFallbackSummary(status.defaultMessage), status) - def apply(status: ClientError, detail: String): IllegalRequestException = apply(ErrorInfo(status.defaultMessage, detail), status) -} - -case class IllegalResponseException(info: ErrorInfo) extends ExceptionWithErrorInfo(info) -object IllegalResponseException { - def apply(summary: String, detail: String = ""): IllegalResponseException = apply(ErrorInfo(summary, detail)) -} - -case class EntityStreamException(info: ErrorInfo) extends ExceptionWithErrorInfo(info) -object EntityStreamException { - def apply(summary: String, detail: String = ""): EntityStreamException = apply(ErrorInfo(summary, detail)) -} - -/** - * This exception is thrown when the size of the HTTP Entity exceeds the configured limit. - * It is possible to configure the limit using configuration options `akka.http.parsing.max-content-length` - * or specifically for the server or client side by setting `akka.http.[server|client].parsing.max-content-length`. - * - * The limit can also be configured in code, by calling [[HttpEntity#withSizeLimit]] - * on the entity before materializing its `dataBytes` stream. - */ -final case class EntityStreamSizeException(limit: Long, actualSize: Option[Long] = None) extends RuntimeException { - - override def getMessage = toString - - override def toString = - s"EntityStreamSizeException: actual entity size ($actualSize) exceeded content length limit ($limit bytes)! " + - s"You can configure this by setting `akka.http.[server|client].parsing.max-content-length` or calling `HttpEntity.withSizeLimit` " + - s"before materializing the dataBytes stream." -} - -case class RequestTimeoutException(request: HttpRequest, message: String) extends RuntimeException(message) diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/FormData.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/FormData.scala deleted file mode 100644 index 25cbcac373..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/FormData.scala +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model - -import akka.http.impl.model.parser.CharacterClasses -import akka.http.impl.util.StringRendering -import akka.http.scaladsl.model.MediaTypes._ - -/** - * Simple model for `application/x-www-form-urlencoded` form data. - */ -final case class FormData(fields: Uri.Query) { - def toEntity: akka.http.scaladsl.model.RequestEntity = - toEntity(HttpCharsets.`UTF-8`) - - def toEntity(charset: HttpCharset): akka.http.scaladsl.model.RequestEntity = { - val render: StringRendering = UriRendering.renderQuery(new StringRendering, this.fields, charset.nioCharset, CharacterClasses.unreserved) - HttpEntity(`application/x-www-form-urlencoded` withCharset charset, render.get) - } -} - -object FormData { - val Empty = FormData(Uri.Query.Empty) - - def apply(fields: Map[String, String]): FormData = - if (fields.isEmpty) Empty else FormData(Uri.Query(fields)) - - def apply(fields: (String, String)*): FormData = - if (fields.isEmpty) Empty else FormData(Uri.Query(fields: _*)) -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/HttpCharset.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/HttpCharset.scala deleted file mode 100644 index 017b586f47..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/HttpCharset.scala +++ /dev/null @@ -1,131 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model - -import java.lang.{ Iterable ⇒ JIterable } -import language.implicitConversions -import scala.collection.immutable -import scala.util.Try -import java.nio.charset.Charset -import akka.http.javadsl.{ model ⇒ jm } -import akka.http.impl.util._ - -/** - * A charset range as encountered in `Accept-Charset`. Can either be a single charset, or `*` - * if all charsets are supported and optionally a qValue for selecting this choice. - */ -sealed abstract class HttpCharsetRange extends jm.HttpCharsetRange with ValueRenderable with WithQValue[HttpCharsetRange] { - def qValue: Float - def matches(charset: HttpCharset): Boolean - - /** Java API */ - def matches(charset: jm.HttpCharset): Boolean = { - import akka.http.impl.util.JavaMapping.Implicits._ - matches(charset.asScala) - } -} - -object HttpCharsetRange { - case class `*`(qValue: Float) extends HttpCharsetRange { - require(0.0f <= qValue && qValue <= 1.0f, "qValue must be >= 0 and <= 1.0") - final def render[R <: Rendering](r: R): r.type = if (qValue < 1.0f) r ~~ "*;q=" ~~ qValue else r ~~ '*' - def matches(charset: HttpCharset) = true - def withQValue(qValue: Float) = - if (qValue == 1.0f) `*` else if (qValue != this.qValue) `*`(qValue.toFloat) else this - } - object `*` extends `*`(1.0f) - - final case class One(charset: HttpCharset, qValue: Float) extends HttpCharsetRange { - require(0.0f <= qValue && qValue <= 1.0f, "qValue must be >= 0 and <= 1.0") - def matches(charset: HttpCharset) = this.charset.value.equalsIgnoreCase(charset.value) - def withQValue(qValue: Float) = One(charset, qValue) - def render[R <: Rendering](r: R): r.type = if (qValue < 1.0f) r ~~ charset ~~ ";q=" ~~ qValue else r ~~ charset - } - - implicit def apply(charset: HttpCharset): HttpCharsetRange = apply(charset, 1.0f) - def apply(charset: HttpCharset, qValue: Float): HttpCharsetRange = One(charset, qValue) -} - -final case class HttpCharset private[http] (override val value: String)(val aliases: immutable.Seq[String]) - extends jm.HttpCharset with SingletonValueRenderable with WithQValue[HttpCharsetRange] { - @transient private[this] var _nioCharset: Try[Charset] = HttpCharset.findNioCharset(value) - - /** Returns the Charset for this charset if available or throws an exception otherwise */ - def nioCharset: Charset = _nioCharset.get - - private def readObject(in: java.io.ObjectInputStream): Unit = { - in.defaultReadObject() - _nioCharset = HttpCharset.findNioCharset(value) - } - - def withQValue(qValue: Float): HttpCharsetRange = HttpCharsetRange(this, qValue.toFloat) - override def toRange: HttpCharsetRange = HttpCharsetRange(this) - - /** Java API */ - def getAliases: JIterable[String] = { - import collection.JavaConverters._ - aliases.asJava - } -} - -object HttpCharset { - def custom(value: String, aliases: String*): HttpCharset = - HttpCharset(value)(immutable.Seq(aliases: _*)) - - private[http] def findNioCharset(name: String): Try[Charset] = Try(Charset.forName(name)) -} - -// see http://www.iana.org/assignments/character-sets -object HttpCharsets extends ObjectRegistry[String, HttpCharset] { - private def register(charset: HttpCharset): HttpCharset = { - charset.aliases.foreach(alias ⇒ register(alias.toRootLowerCase, charset)) - register(charset.value.toRootLowerCase, charset) - } - - /** Register standard charset that is required to be supported on all platforms */ - private def register(value: String)(aliases: String*): HttpCharset = - register(HttpCharset(value)(immutable.Seq(aliases: _*))) - - /** Register non-standard charsets that may be missing on some platforms */ - private def tryRegister(value: String)(aliases: String*): Unit = - try register(value)(aliases: _*) - catch { - case e: java.nio.charset.UnsupportedCharsetException ⇒ // ignore - } - - // format: OFF - // Only those first 6 are standard charsets known to be supported on all platforms - // by Javadoc of java.nio.charset.Charset - // IANA definition incl. aliases: http://www.iana.org/assignments/character-sets/character-sets.xhtml - val `US-ASCII` = register("US-ASCII")("iso-ir-6", "ANSI_X3.4-1968", "ANSI_X3.4-1986", "ISO_646.irv:1991", "ASCII", "ISO646-US", "us", "IBM367", "cp367", "csASCII") - val `ISO-8859-1` = register("ISO-8859-1")("iso-ir-100", "ISO_8859-1", "latin1", "l1", "IBM819", "CP819", "csISOLatin1") - val `UTF-8` = register("UTF-8")("UTF8") - val `UTF-16` = register("UTF-16")("UTF16") - val `UTF-16BE` = register("UTF-16BE")() - val `UTF-16LE` = register("UTF-16LE")() - - // those are not necessarily supported on every platform so we can't expose them as constants here - // but we try to register them so that all the aliases are registered - tryRegister("ISO-8859-2")("iso-ir-101", "ISO_8859-2", "latin2", "l2", "csISOLatin2") - tryRegister("ISO-8859-3")("iso-ir-109", "ISO_8859-3", "latin3", "l3", "csISOLatin3") - tryRegister("ISO-8859-4")("iso-ir-110", "ISO_8859-4", "latin4", "l4", "csISOLatin4") - tryRegister("ISO-8859-5")("iso-ir-144", "ISO_8859-5", "cyrillic", "csISOLatinCyrillic") - tryRegister("ISO-8859-6")("iso-ir-127", "ISO_8859-6", "ECMA-114", "ASMO-708", "arabic", "csISOLatinArabic") - tryRegister("ISO-8859-7")("iso-ir-126", "ISO_8859-7", "ELOT_928", "ECMA-118", "greek", "greek8", "csISOLatinGreek") - tryRegister("ISO-8859-8")("iso-ir-138", "ISO_8859-8", "hebrew", "csISOLatinHebrew") - tryRegister("ISO-8859-9")("iso-ir-148", "ISO_8859-9", "latin5", "l5", "csISOLatin5") - tryRegister("ISO-8859-10")("iso-ir-157", "l6", "ISO_8859-10", "csISOLatin6", "latin6") - - tryRegister("UTF-32")("UTF32") - tryRegister("UTF-32BE")() - tryRegister("UTF-32LE")() - tryRegister("windows-1250")("cp1250", "cp5346") - tryRegister("windows-1251")("cp1251", "cp5347") - tryRegister("windows-1252")("cp1252", "cp5348") - tryRegister("windows-1253")("cp1253", "cp5349") - tryRegister("windows-1254")("cp1254", "cp5350") - tryRegister("windows-1257")("cp1257", "cp5353") - // format: ON -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/HttpEntity.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/HttpEntity.scala deleted file mode 100644 index 6c2815356c..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/HttpEntity.scala +++ /dev/null @@ -1,700 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model - -import java.util.OptionalLong - -import akka.http.impl.model.JavaInitialization - -import language.implicitConversions -import java.io.File -import java.nio.file.{ Files, Path } -import java.lang.{ Iterable ⇒ JIterable } - -import scala.util.control.NonFatal -import scala.concurrent.Future -import scala.concurrent.duration._ -import scala.collection.immutable -import akka.util.ByteString -import akka.stream.scaladsl._ -import akka.stream.stage._ -import akka.stream._ -import akka.{ Done, NotUsed, stream } -import akka.http.scaladsl.model.ContentType.{ Binary, NonBinary } -import akka.http.scaladsl.util.FastFuture -import akka.http.javadsl.{ model ⇒ jm } -import akka.http.impl.util.{ JavaMapping, StreamUtils } -import akka.http.impl.util.JavaMapping.Implicits._ - -import scala.compat.java8.OptionConverters._ -import scala.compat.java8.FutureConverters._ -import java.util.concurrent.CompletionStage - -import scala.compat.java8.FutureConverters - -/** - * Models the entity (aka "body" or "content) of an HTTP message. - */ -sealed trait HttpEntity extends jm.HttpEntity { - import language.implicitConversions - private implicit def completionStageCovariant[T, U >: T](in: CompletionStage[T]): CompletionStage[U] = in.asInstanceOf[CompletionStage[U]] - - /** - * Determines whether this entity is known to be empty. - */ - override def isKnownEmpty: Boolean - - /** - * The `ContentType` associated with this entity. - */ - def contentType: ContentType - - /** - * Some(content length) if a length is defined for this entity, None otherwise. - * A length is only defined for Strict and Default entity types. - * - * In many cases it's dangerous to rely on the (non-)existence of a content-length. - * HTTP intermediaries like (transparent) proxies are allowed to change the transfer-encoding - * which can result in the entity being delivered as another type as expected. - */ - def contentLengthOption: Option[Long] - - /** - * A stream of the data of this entity. - */ - def dataBytes: Source[ByteString, Any] - - /** - * Collects all possible parts and returns a potentially future Strict entity for easier processing. - * The Future is failed with an TimeoutException if the stream isn't completed after the given timeout. - */ - def toStrict(timeout: FiniteDuration)(implicit fm: Materializer): Future[HttpEntity.Strict] = - dataBytes - .via(new akka.http.impl.util.ToStrict(timeout, contentType)) - .runWith(Sink.head) - - /** - * Discards the entities data bytes by running the `dataBytes` Source contained in this `entity`. - * - * Note: It is crucial that entities are either discarded, or consumed by running the underlying [[akka.stream.scaladsl.Source]] - * as otherwise the lack of consuming of the data will trigger back-pressure to the underlying TCP connection - * (as designed), however possibly leading to an idle-timeout that will close the connection, instead of - * just having ignored the data. - * - * Warning: It is not allowed to discard and/or consume the `entity.dataBytes` more than once - * as the stream is directly attached to the "live" incoming data source from the underlying TCP connection. - * Allowing it to be consumable twice would require buffering the incoming data, thus defeating the purpose - * of its streaming nature. If the dataBytes source is materialized a second time, it will fail with an - * "stream can cannot be materialized more than once" exception. - * - * In future versions, more automatic ways to warn or resolve these situations may be introduced, see issue #18716. - */ - override def discardBytes(mat: Materializer): HttpMessage.DiscardedEntity = - new HttpMessage.DiscardedEntity(dataBytes.runWith(Sink.ignore)(mat)) - - /** - * Returns a copy of the given entity with the ByteString chunks of this entity transformed by the given transformer. - * For a `Chunked` entity, the chunks will be transformed one by one keeping the chunk metadata (but may introduce an - * extra chunk before the `LastChunk` if `transformer.onTermination` returns additional data). - * - * This method may only throw an exception if the `transformer` function throws an exception while creating the transformer. - * Any other errors are reported through the new entity data stream. - */ - def transformDataBytes(transformer: Flow[ByteString, ByteString, Any]): HttpEntity - - /** - * Creates a copy of this HttpEntity with the `contentType` overridden with the given one. - */ - def withContentType(contentType: ContentType): HttpEntity - - /** - * Apply the given size limit to this entity by returning a new entity instance which automatically verifies that the - * data stream encapsulated by this instance produces at most `maxBytes` data bytes. In case this verification fails - * the respective stream will be terminated with an `EntityStreamException` either directly at materialization - * time (if the Content-Length is known) or whenever more data bytes than allowed have been read. - * - * When called on `Strict` entities the method will return the entity itself if the length is within the bound, - * otherwise a `Default` entity with a single element data stream. This allows for potential refinement of the - * entity size limit at a later point (before materialization of the data stream). - * - * By default all message entities produced by the HTTP layer automatically carry the limit that is defined in the - * application's `max-content-length` config setting. If the entity is transformed in a way that changes the - * Content-Length and then another limit is applied then this new limit will be evaluated against the new - * Content-Length. If the entity is transformed in a way that changes the Content-Length and no new limit is applied - * then the previous limit will be applied against the previous Content-Length. - * - * Note that the size limit applied via this method will only have any effect if the `Source` instance contained - * in this entity has been appropriately modified via the `HttpEntity.limitable` method. For all entities created - * by the HTTP layer itself this is always the case, but if you create entities yourself and would like them to - * properly respect limits defined via this method you need to make sure to apply `HttpEntity.limitable` yourself. - */ - override def withSizeLimit(maxBytes: Long): HttpEntity - - /** - * Lift the size limit from this entity by returning a new entity instance which skips the size verification. - * - * By default all message entities produced by the HTTP layer automatically carry the limit that is defined in the - * application's `max-content-length` config setting. It is recommended to always keep an upper limit on accepted - * entities to avoid potential attackers flooding you with too large requests/responses, so use this method with caution. - * - * Note that the size limit applied via this method will only have any effect if the `Source` instance contained - * in this entity has been appropriately modified via the `HttpEntity.limitable` method. For all entities created - * by the HTTP layer itself this is always the case, but if you create entities yourself and would like them to - * properly respect limits defined via this method you need to make sure to apply `HttpEntity.limitable` yourself. - * - * See [[withSizeLimit]] for more details. - */ - override def withoutSizeLimit: HttpEntity - - /** Java API */ - override def getContentType: jm.ContentType = contentType - - /** Java API */ - override def getDataBytes: stream.javadsl.Source[ByteString, AnyRef] = - stream.javadsl.Source.fromGraph(dataBytes.asInstanceOf[Source[ByteString, AnyRef]]) - - /** Java API */ - override def getContentLengthOption: OptionalLong = contentLengthOption.asPrimitive - - // default implementations, should be overridden - override def isCloseDelimited: Boolean = false - override def isIndefiniteLength: Boolean = false - override def isDefault: Boolean = false - override def isChunked: Boolean = false - - /** Java API */ - override def toStrict(timeoutMillis: Long, materializer: Materializer): CompletionStage[jm.HttpEntity.Strict] = - toStrict(timeoutMillis.millis)(materializer).toJava - -} - -/* An entity that can be used for body parts */ -sealed trait BodyPartEntity extends HttpEntity with jm.BodyPartEntity { - override def withContentType(contentType: ContentType): BodyPartEntity - - override def withSizeLimit(maxBytes: Long): BodyPartEntity - override def withoutSizeLimit: BodyPartEntity -} - -/** - * An [[HttpEntity]] that can be used for requests. - * Note that all entities that can be used for requests can also be used for responses. - * (But not the other way around, since [[HttpEntity.CloseDelimited]] can only be used for responses!) - */ -sealed trait RequestEntity extends HttpEntity with jm.RequestEntity with ResponseEntity { - def withContentType(contentType: ContentType): RequestEntity - - /** - * See [[HttpEntity#withSizeLimit]]. - */ - def withSizeLimit(maxBytes: Long): RequestEntity - - /** - * See [[HttpEntity#withoutSizeLimit]]. - */ - def withoutSizeLimit: RequestEntity - - def transformDataBytes(transformer: Flow[ByteString, ByteString, Any]): RequestEntity -} - -/** - * An [[HttpEntity]] that can be used for responses. - * Note that all entities that can be used for requests can also be used for responses. - * (But not the other way around, since [[HttpEntity.CloseDelimited]] can only be used for responses!) - */ -sealed trait ResponseEntity extends HttpEntity with jm.ResponseEntity { - def withContentType(contentType: ContentType): ResponseEntity - - /** - * See [[HttpEntity#withSizeLimit]]. - */ - def withSizeLimit(maxBytes: Long): ResponseEntity - - /** - * See [[HttpEntity#withoutSizeLimit]] - */ - def withoutSizeLimit: ResponseEntity - - def transformDataBytes(transformer: Flow[ByteString, ByteString, Any]): ResponseEntity -} - -object ResponseEntity { - implicit def fromJava(entity: akka.http.javadsl.model.ResponseEntity)(implicit m: JavaMapping[akka.http.javadsl.model.ResponseEntity, ResponseEntity]): ResponseEntity = - JavaMapping.toScala(entity) -} - -/* An entity that can be used for requests, responses, and body parts */ -sealed trait UniversalEntity extends jm.UniversalEntity with MessageEntity with BodyPartEntity { - def withContentType(contentType: ContentType): UniversalEntity - - /** - * See [[HttpEntity#withSizeLimit]]. - */ - def withSizeLimit(maxBytes: Long): UniversalEntity - - /** - * See [[HttpEntity#withoutSizeLimit]] - */ - def withoutSizeLimit: UniversalEntity - - def contentLength: Long - def contentLengthOption: Option[Long] = Some(contentLength) - - /** - * Transforms this' entities data bytes with a transformer that will produce exactly the number of bytes given as - * `newContentLength`. - */ - def transformDataBytes(newContentLength: Long, transformer: Flow[ByteString, ByteString, Any]): UniversalEntity -} - -object HttpEntity { - implicit def apply(string: String): HttpEntity.Strict = apply(ContentTypes.`text/plain(UTF-8)`, string) - implicit def apply(bytes: Array[Byte]): HttpEntity.Strict = apply(ContentTypes.`application/octet-stream`, bytes) - implicit def apply(data: ByteString): HttpEntity.Strict = apply(ContentTypes.`application/octet-stream`, data) - def apply(contentType: ContentType.NonBinary, string: String): HttpEntity.Strict = - if (string.isEmpty) empty(contentType) else apply(contentType, ByteString(string.getBytes(contentType.charset.nioCharset))) - def apply(contentType: ContentType, bytes: Array[Byte]): HttpEntity.Strict = - if (bytes.length == 0) empty(contentType) else apply(contentType, ByteString(bytes)) - def apply(contentType: ContentType, data: ByteString): HttpEntity.Strict = - if (data.isEmpty) empty(contentType) else HttpEntity.Strict(contentType, data) - - def apply(contentType: ContentType, contentLength: Long, data: Source[ByteString, Any]): UniversalEntity = - if (contentLength == 0) empty(contentType) else HttpEntity.Default(contentType, contentLength, data) - def apply(contentType: ContentType, data: Source[ByteString, Any]): HttpEntity.Chunked = - HttpEntity.Chunked.fromData(contentType, data) - - /** - * Returns either the empty entity, if the given file is empty, or a [[HttpEntity.Default]] entity - * consisting of a stream of [[akka.util.ByteString]] instances each containing `chunkSize` bytes - * (except for the final ByteString, which simply contains the remaining bytes). - * - * If the given `chunkSize` is -1 the default chunk size is used. - */ - @deprecated("Use `fromPath` instead", "2.4.5") - def apply(contentType: ContentType, file: File, chunkSize: Int = -1): UniversalEntity = - fromPath(contentType, file.toPath, chunkSize) - - /** - * Returns either the empty entity, if the given file is empty, or a [[HttpEntity.Default]] entity - * consisting of a stream of [[akka.util.ByteString]] instances each containing `chunkSize` bytes - * (except for the final ByteString, which simply contains the remaining bytes). - * - * If the given `chunkSize` is -1 the default chunk size is used. - */ - def fromPath(contentType: ContentType, file: Path, chunkSize: Int = -1): UniversalEntity = { - val fileLength = Files.size(file) - if (fileLength > 0) - HttpEntity.Default(contentType, fileLength, - if (chunkSize > 0) FileIO.fromPath(file, chunkSize) else FileIO.fromPath(file)) - else empty(contentType) - } - - val Empty: HttpEntity.Strict = HttpEntity.Strict(ContentTypes.NoContentType, data = ByteString.empty) - - def empty(contentType: ContentType): HttpEntity.Strict = - if (contentType == Empty.contentType) Empty - else HttpEntity.Strict(contentType, data = ByteString.empty) - - JavaInitialization.initializeStaticFieldWith( - Empty, classOf[jm.HttpEntity].getField("EMPTY")) - - // TODO: re-establish serializability - // TODO: equal/hashcode ? - - /** - * The model for the entity of a "regular" unchunked HTTP message with known, fixed data. - */ - final case class Strict(contentType: ContentType, data: ByteString) - extends jm.HttpEntity.Strict with UniversalEntity { - - override def contentLength: Long = data.length - - override def isKnownEmpty: Boolean = data.isEmpty - - override def dataBytes: Source[ByteString, NotUsed] = Source(data :: Nil) - - override def toStrict(timeout: FiniteDuration)(implicit fm: Materializer) = - FastFuture.successful(this) - - override def transformDataBytes(transformer: Flow[ByteString, ByteString, Any]): MessageEntity = - HttpEntity.Chunked.fromData(contentType, Source.single(data).via(transformer)) - - override def transformDataBytes(newContentLength: Long, transformer: Flow[ByteString, ByteString, Any]): UniversalEntity = - HttpEntity.Default(contentType, newContentLength, Source.single(data) via transformer) - - override def withContentType(contentType: ContentType): HttpEntity.Strict = - if (contentType == this.contentType) this else copy(contentType = contentType) - - override def withSizeLimit(maxBytes: Long): UniversalEntity = - if (data.length <= maxBytes || isKnownEmpty) this - else HttpEntity.Default(contentType, data.length, limitableByteSource(Source.single(data))) withSizeLimit maxBytes - - override def withoutSizeLimit: UniversalEntity = - withSizeLimit(SizeLimit.Disabled) - - override def productPrefix = "HttpEntity.Strict" - - override def toString = { - val dataAsString = contentType match { - case _: Binary ⇒ - data.toString() - case nb: NonBinary ⇒ - try { - val maxBytes = 4096 - if (data.length > maxBytes) { - val truncatedString = data.take(maxBytes).decodeString(nb.charset.value).dropRight(1) - s"$truncatedString ... (${data.length} bytes total)" - } else - data.decodeString(nb.charset.value) - } catch { - case NonFatal(e) ⇒ - data.toString() - } - } - - s"$productPrefix($contentType,$dataAsString)" - } - - /** Java API */ - override def getData = data - } - - /** - * The model for the entity of a "regular" unchunked HTTP message with a known non-zero length. - */ - final case class Default( - contentType: ContentType, - contentLength: Long, - data: Source[ByteString, Any]) - extends jm.HttpEntity.Default with UniversalEntity { - require(contentLength > 0, "contentLength must be positive (use `HttpEntity.empty(contentType)` for empty entities)") - def isKnownEmpty = false - override def isDefault: Boolean = true - - def dataBytes: Source[ByteString, Any] = data - - override def transformDataBytes(transformer: Flow[ByteString, ByteString, Any]): HttpEntity.Chunked = - HttpEntity.Chunked.fromData(contentType, data via transformer) - - override def transformDataBytes(newContentLength: Long, transformer: Flow[ByteString, ByteString, Any]): UniversalEntity = - HttpEntity.Default(contentType, newContentLength, data via transformer) - - def withContentType(contentType: ContentType): HttpEntity.Default = - if (contentType == this.contentType) this else copy(contentType = contentType) - - override def withSizeLimit(maxBytes: Long): HttpEntity.Default = - copy(data = data withAttributes Attributes(SizeLimit(maxBytes, Some(contentLength)))) - - override def withoutSizeLimit: HttpEntity.Default = - withSizeLimit(SizeLimit.Disabled) - - override def productPrefix = "HttpEntity.Default" - - override def toString: String = { - s"$productPrefix($contentType,$contentLength bytes total)" - } - - /** Java API */ - override def getContentLength = contentLength - } - - /** - * Supertype of CloseDelimited and IndefiniteLength. - * - * INTERNAL API - */ - private[http] sealed trait WithoutKnownLength extends HttpEntity { - type Self <: HttpEntity.WithoutKnownLength - def contentType: ContentType - def data: Source[ByteString, Any] - override def contentLengthOption: Option[Long] = None - override def isKnownEmpty = data eq Source.empty - override def dataBytes: Source[ByteString, Any] = data - - override def withSizeLimit(maxBytes: Long): Self = - withData(data withAttributes Attributes(SizeLimit(maxBytes))) - - override def withoutSizeLimit: Self = - withData(data withAttributes Attributes(SizeLimit(SizeLimit.Disabled))) - - override def transformDataBytes(transformer: Flow[ByteString, ByteString, Any]): Self = - withData(data via transformer) - - def withData(data: Source[ByteString, Any]): Self - } - - /** - * The model for the entity of an HTTP response that is terminated by the server closing the connection. - * The content-length of such responses is unknown at the time the response headers have been received. - * Note that this type of HttpEntity can only be used for HttpResponses. - */ - final case class CloseDelimited(contentType: ContentType, data: Source[ByteString, Any]) - extends jm.HttpEntity.CloseDelimited with ResponseEntity with HttpEntity.WithoutKnownLength { - type Self = HttpEntity.CloseDelimited - - override def isCloseDelimited: Boolean = true - override def withContentType(contentType: ContentType): HttpEntity.CloseDelimited = - if (contentType == this.contentType) this else copy(contentType = contentType) - - override def withData(data: Source[ByteString, Any]): HttpEntity.CloseDelimited = copy(data = data) - - override def productPrefix = "HttpEntity.CloseDelimited" - - override def toString: String = { - s"$productPrefix($contentType)" - } - } - - /** - * The model for the entity of a BodyPart with an indefinite length. - * Note that this type of HttpEntity can only be used for BodyParts. - */ - final case class IndefiniteLength(contentType: ContentType, data: Source[ByteString, Any]) - extends jm.HttpEntity.IndefiniteLength with BodyPartEntity with HttpEntity.WithoutKnownLength { - type Self = HttpEntity.IndefiniteLength - - override def isIndefiniteLength: Boolean = true - override def withContentType(contentType: ContentType): HttpEntity.IndefiniteLength = - if (contentType == this.contentType) this else copy(contentType = contentType) - - override def withData(data: Source[ByteString, Any]): HttpEntity.IndefiniteLength = copy(data = data) - - override def productPrefix = "HttpEntity.IndefiniteLength" - - override def toString: String = { - s"$productPrefix($contentType)" - } - } - - /** - * The model for the entity of a chunked HTTP message (with `Transfer-Encoding: chunked`). - */ - final case class Chunked(contentType: ContentType, chunks: Source[ChunkStreamPart, Any]) - extends jm.HttpEntity.Chunked with MessageEntity { - - override def isKnownEmpty = chunks eq Source.empty - override def contentLengthOption: Option[Long] = None - - override def isChunked: Boolean = true - - override def dataBytes: Source[ByteString, Any] = chunks.map(_.data).filter(_.nonEmpty) - - override def withSizeLimit(maxBytes: Long): HttpEntity.Chunked = - copy(chunks = chunks withAttributes Attributes(SizeLimit(maxBytes))) - - override def withoutSizeLimit: HttpEntity.Chunked = - withSizeLimit(SizeLimit.Disabled) - - override def transformDataBytes(transformer: Flow[ByteString, ByteString, Any]): HttpEntity.Chunked = { - val newData = - chunks.map { - case Chunk(data, "") ⇒ data - case LastChunk("", Nil) ⇒ ByteString.empty - case _ ⇒ - throw new IllegalArgumentException("Chunked.transformDataBytes not allowed for chunks with metadata") - } via transformer - - HttpEntity.Chunked.fromData(contentType, newData) - } - - def withContentType(contentType: ContentType): HttpEntity.Chunked = - if (contentType == this.contentType) this else copy(contentType = contentType) - - override def productPrefix = "HttpEntity.Chunked" - - override def toString: String = { - s"$productPrefix($contentType)" - } - - /** Java API */ - def getChunks: stream.javadsl.Source[jm.HttpEntity.ChunkStreamPart, AnyRef] = - stream.javadsl.Source.fromGraph(chunks.asInstanceOf[Source[jm.HttpEntity.ChunkStreamPart, AnyRef]]) - } - object Chunked { - /** - * Returns a `Chunked` entity where one Chunk is produced for every non-empty ByteString produced by the given - * `Source`. - */ - def fromData(contentType: ContentType, chunks: Source[ByteString, Any]): HttpEntity.Chunked = - HttpEntity.Chunked(contentType, chunks.collect[ChunkStreamPart] { - case b: ByteString if b.nonEmpty ⇒ Chunk(b) - }) - } - - /** - * An element of the HttpEntity data stream. - * Can be either a `Chunk` or a `LastChunk`. - */ - sealed abstract class ChunkStreamPart extends jm.HttpEntity.ChunkStreamPart { - def data: ByteString - def extension: String - def isLastChunk: Boolean - } - object ChunkStreamPart { - implicit def apply(string: String): ChunkStreamPart = Chunk(string) - implicit def apply(bytes: Array[Byte]): ChunkStreamPart = Chunk(bytes) - implicit def apply(bytes: ByteString): ChunkStreamPart = Chunk(bytes) - } - - /** - * An intermediate entity chunk guaranteed to carry non-empty data. - */ - final case class Chunk(data: ByteString, extension: String = "") extends HttpEntity.ChunkStreamPart { - require(data.nonEmpty, "An HttpEntity.Chunk must have non-empty data") - def isLastChunk = false - - /** Java API */ - def getTrailerHeaders: JIterable[jm.HttpHeader] = java.util.Collections.emptyList[jm.HttpHeader] - } - object Chunk { - def apply(string: String): Chunk = apply(ByteString(string)) - def apply(bytes: Array[Byte]): Chunk = apply(ByteString(bytes)) - } - - /** - * The final chunk of a chunk stream. - * If you don't need extensions or trailer headers you can save an allocation - * by directly using the `LastChunk` companion object. - */ - case class LastChunk(extension: String = "", trailer: immutable.Seq[HttpHeader] = Nil) extends HttpEntity.ChunkStreamPart { - def data = ByteString.empty - def isLastChunk = true - - /** Java API */ - def getTrailerHeaders: JIterable[jm.HttpHeader] = trailer.asJava - } - object LastChunk extends LastChunk("", Nil) - - /** - * Turns the given source into one that respects the `withSizeLimit` calls when used as a parameter - * to entity constructors. - */ - def limitableByteSource[Mat](source: Source[ByteString, Mat]): Source[ByteString, Mat] = - source.via(new Limitable(sizeOfByteString)) - - /** - * Turns the given source into one that respects the `withSizeLimit` calls when used as a parameter - * to entity constructors. - */ - def limitableChunkSource[Mat](source: Source[ChunkStreamPart, Mat]): Source[ChunkStreamPart, Mat] = - source.via(new Limitable(sizeOfChunkStreamPart)) - - /** - * INTERNAL API - */ - private val sizeOfByteString: ByteString ⇒ Int = _.size - private val sizeOfChunkStreamPart: ChunkStreamPart ⇒ Int = _.data.size - - private val limitableDefaults = Attributes.name("limitable") - - private final class Limitable[T](sizeOf: T ⇒ Int) extends GraphStage[FlowShape[T, T]] { - val in = Inlet[T]("Limitable.in") - val out = Outlet[T]("Limitable.out") - override val shape = FlowShape.of(in, out) - override protected val initialAttributes: Attributes = limitableDefaults - - override def createLogic(attributes: Attributes): GraphStageLogic = new GraphStageLogic(shape) with InHandler with OutHandler { - private var maxBytes = -1L - private var bytesLeft = Long.MaxValue - - override def preStart(): Unit = { - attributes.getFirst[SizeLimit] match { - case Some(limit: SizeLimit) if limit.isDisabled ⇒ - // "no limit" - case Some(SizeLimit(bytes, cl @ Some(contentLength))) ⇒ - if (contentLength > bytes) throw EntityStreamSizeException(bytes, cl) - // else we still count but never throw an error - case Some(SizeLimit(bytes, None)) ⇒ - maxBytes = bytes - bytesLeft = bytes - case None ⇒ - } - } - - override def onPush(): Unit = { - val elem = grab(in) - bytesLeft -= sizeOf(elem) - if (bytesLeft >= 0) push(out, elem) - else failStage(EntityStreamSizeException(maxBytes)) - } - - override def onPull(): Unit = { - pull(in) - } - - setHandlers(in, out, this) - } - } - - /** - * INTERNAL API - */ - private final case class SizeLimit(maxBytes: Long, contentLength: Option[Long] = None) extends Attributes.Attribute { - def isDisabled = maxBytes < 0 - } - private object SizeLimit { - val Disabled = -1 // any negative value will do - } - - /** - * INTERNAL API - */ - private[http] def captureTermination[T <: HttpEntity](entity: T): (T, Future[Unit]) = - entity match { - case x: HttpEntity.Strict ⇒ x.asInstanceOf[T] → FastFuture.successful(()) - case x: HttpEntity.Default ⇒ - val (newData, whenCompleted) = StreamUtils.captureTermination(x.data) - x.copy(data = newData).asInstanceOf[T] → whenCompleted - case x: HttpEntity.Chunked ⇒ - val (newChunks, whenCompleted) = StreamUtils.captureTermination(x.chunks) - x.copy(chunks = newChunks).asInstanceOf[T] → whenCompleted - case x: HttpEntity.CloseDelimited ⇒ - val (newData, whenCompleted) = StreamUtils.captureTermination(x.data) - x.copy(data = newData).asInstanceOf[T] → whenCompleted - case x: HttpEntity.IndefiniteLength ⇒ - val (newData, whenCompleted) = StreamUtils.captureTermination(x.data) - x.copy(data = newData).asInstanceOf[T] → whenCompleted - } - - /** - * Represents the currently being-drained HTTP Entity which triggers completion of the contained - * Future once the entity has been drained for the given HttpMessage completely. - */ - final class DiscardedEntity(f: Future[Done]) extends akka.http.javadsl.model.HttpMessage.DiscardedEntity { - /** - * This future completes successfully once the underlying entity stream has been - * successfully drained (and fails otherwise). - */ - def future: Future[Done] = f - - /** - * This future completes successfully once the underlying entity stream has been - * successfully drained (and fails otherwise). - */ - def completionStage: CompletionStage[Done] = FutureConverters.toJava(f) - } - - /** Adds Scala DSL idiomatic methods to [[HttpEntity]], e.g. versions of methods with an implicit [[Materializer]]. */ - implicit final class HttpEntityScalaDSLSugar(val httpEntity: HttpEntity) extends AnyVal { - /** - * Discards the entities data bytes by running the `dataBytes` Source contained in this `entity`. - * - * Note: It is crucial that entities are either discarded, or consumed by running the underlying [[akka.stream.scaladsl.Source]] - * as otherwise the lack of consuming of the data will trigger back-pressure to the underlying TCP connection - * (as designed), however possibly leading to an idle-timeout that will close the connection, instead of - * just having ignored the data. - * - * Warning: It is not allowed to discard and/or consume the `entity.dataBytes` more than once - * as the stream is directly attached to the "live" incoming data source from the underlying TCP connection. - * Allowing it to be consumable twice would require buffering the incoming data, thus defeating the purpose - * of its streaming nature. If the dataBytes source is materialized a second time, it will fail with an - * "stream can cannot be materialized more than once" exception. - * - * In future versions, more automatic ways to warn or resolve these situations may be introduced, see issue #18716. - */ - def discardBytes()(implicit mat: Materializer): HttpMessage.DiscardedEntity = - httpEntity.discardBytes(mat) - } -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/HttpHeader.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/HttpHeader.scala deleted file mode 100644 index bc73e0527d..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/HttpHeader.scala +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model - -import java.nio.charset.StandardCharsets - -import scala.util.{ Failure, Success } -import akka.parboiled2.{ ParseError, ParserInput } -import akka.http.impl.util.ToStringRenderable -import akka.http.impl.model.parser.{ CharacterClasses, HeaderParser } -import akka.http.javadsl.{ model ⇒ jm } -import akka.http.scaladsl.model.headers._ -import akka.parboiled2.ParserInput.DefaultParserInput -import akka.util.{ ByteString, OptionVal } - -import scala.annotation.tailrec -import scala.collection.immutable - -/** - * The model of an HTTP header. In its most basic form headers are simple name-value pairs. Header names - * are compared in a case-insensitive way. - */ -abstract class HttpHeader extends jm.HttpHeader with ToStringRenderable { - def name: String - def value: String - def lowercaseName: String - def is(nameInLowerCase: String): Boolean = lowercaseName == nameInLowerCase - def isNot(nameInLowerCase: String): Boolean = lowercaseName != nameInLowerCase -} - -object HttpHeader { - /** - * Extract name and value from a header. - * CAUTION: The name must be matched in *all-lowercase*!. - */ - def unapply(header: HttpHeader): Option[(String, String)] = Some((header.lowercaseName, header.value)) - - /** - * Attempts to parse the given header name and value string into a header model instance. - * - * This process has several possible outcomes: - * - * 1. The header name corresponds to a properly modelled header and - * a) the value is valid for this header type. - * In this case the method returns a `ParsingResult.Ok` with the respective header instance and no errors. - * b) the value consists of a number elements, some of which valid and some invalid, and the header type supports - * partial value parsing. In this case the method returns a `ParsingResult.Ok` with the respective header - * instance holding the valid value elements and an [[ErrorInfo]] for each invalid value. - * c) the value has invalid elements and the header type doesn't support partial value parsing. - * In this case the method returns a `ParsingResult.Ok` with a [[akka.http.scaladsl.model.headers.RawHeader]] instance and - * a single [[ErrorInfo]] for the value parsing problem. - * - * 2. The header name does not correspond to a properly modelled header but the header name and the value are both - * syntactically legal according to the basic header requirements from the HTTP specification. - * (http://tools.ietf.org/html/rfc7230#section-3.2) - * In this case the method returns a `ParsingResult.Ok` with a [[akka.http.scaladsl.model.headers.RawHeader]] instance and no errors. - * - * 3. The header name or value are illegal according to the basic requirements for HTTP headers - * (http://tools.ietf.org/html/rfc7230#section-3.2). In this case the method returns a `ParsingResult.Error`. - */ - def parse(name: String, value: String, settings: HeaderParser.Settings = HeaderParser.DefaultSettings): ParsingResult = - if (name.forall(CharacterClasses.tchar)) { - import akka.parboiled2.Parser.DeliveryScheme.Try - val parser = new HeaderParser(value, settings) - parser.`header-field-value`.run() match { - case Success(preProcessedValue) ⇒ - try { - HeaderParser.parseFull(name.toLowerCase, preProcessedValue, settings) match { - case Right(header) ⇒ ParsingResult.Ok(header, Nil) - case Left(info) ⇒ - val errors = info.withSummaryPrepended(s"Illegal HTTP header '$name'") :: Nil - ParsingResult.Ok(RawHeader(name, preProcessedValue), errors) - } - } catch { - case HeaderParser.RuleNotFoundException ⇒ ParsingResult.Ok(RawHeader(name, preProcessedValue), Nil) - } - case Failure(error) ⇒ - val info = error match { - case e: ParseError ⇒ parser.parseError(e) - case e ⇒ parser.failure(e) - } - ParsingResult.Error(info.left.get.withSummaryPrepended(s"Illegal HTTP header value")) - } - } else ParsingResult.Error(ErrorInfo(s"Illegal HTTP header name", name)) - - /** INTERNAL API */ - private[akka] def fastFind[T >: Null <: jm.HttpHeader](clazz: Class[T], headers: immutable.Seq[HttpHeader]): OptionVal[T] = { - val it = headers.iterator - while (it.hasNext) it.next() match { - case h if clazz.isInstance(h) ⇒ return OptionVal.Some[T](h.asInstanceOf[T]) - case _ ⇒ // continue ... - } - OptionVal.None - } - - sealed trait ParsingResult { - def errors: List[ErrorInfo] - } - - object ParsingResult { - /** - * The parsing run produced a result. If there were parsing errors (which did not prevent the run from - * completing) they are reported in the given error list. - */ - final case class Ok(header: HttpHeader, errors: List[ErrorInfo]) extends ParsingResult - - /** - * The parsing run failed due to a fatal parsing error. - */ - final case class Error(error: ErrorInfo) extends ParsingResult { def errors = error :: Nil } - } -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/HttpMessage.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/HttpMessage.scala deleted file mode 100644 index 7805ac204f..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/HttpMessage.scala +++ /dev/null @@ -1,472 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model - -import java.io.File -import java.nio.file.Path -import java.lang.{ Iterable ⇒ JIterable } -import java.util.Optional -import java.util.concurrent.CompletionStage - -import scala.compat.java8.FutureConverters -import scala.concurrent.duration.FiniteDuration -import scala.concurrent.{ ExecutionContext, Future } -import scala.collection.immutable -import scala.compat.java8.OptionConverters._ -import scala.reflect.{ ClassTag, classTag } -import akka.Done -import akka.parboiled2.CharUtils -import akka.stream.Materializer -import akka.util.{ ByteString, HashCode, OptionVal } -import akka.http.impl.util._ -import akka.http.javadsl.{ model ⇒ jm } -import akka.http.scaladsl.util.FastFuture._ -import headers._ - -/** - * Common base class of HttpRequest and HttpResponse. - */ -sealed trait HttpMessage extends jm.HttpMessage { - type Self <: HttpMessage - def self: Self - - def isRequest: Boolean - def isResponse: Boolean - - def headers: immutable.Seq[HttpHeader] - def entity: ResponseEntity - def protocol: HttpProtocol - - /** - * Discards the entities data bytes by running the `dataBytes` Source contained in this HttpMessage. - * - * Note: It is crucial that entities are either discarded, or consumed by running the underlying [[akka.stream.scaladsl.Source]] - * as otherwise the lack of consuming of the data will trigger back-pressure to the underlying TCP connection - * (as designed), however possibly leading to an idle-timeout that will close the connection, instead of - * just having ignored the data. - * - * Warning: It is not allowed to discard and/or consume the `entity.dataBytes` more than once - * as the stream is directly attached to the "live" incoming data source from the underlying TCP connection. - * Allowing it to be consumable twice would require buffering the incoming data, thus defeating the purpose - * of its streaming nature. If the dataBytes source is materialized a second time, it will fail with an - * "stream can cannot be materialized more than once" exception. - * - * In future versions, more automatic ways to warn or resolve these situations may be introduced, see issue #18716. - */ - def discardEntityBytes(mat: Materializer): HttpMessage.DiscardedEntity = entity.discardBytes()(mat) - - /** Returns a copy of this message with the list of headers set to the given ones. */ - def withHeaders(headers: HttpHeader*): Self = withHeaders(headers.toList) - - /** Returns a copy of this message with the list of headers set to the given ones. */ - def withHeaders(headers: immutable.Seq[HttpHeader]): Self - - /** - * Returns a new message that contains all of the given default headers which didn't already - * exist (by case-insensitive header name) in this message. - */ - def withDefaultHeaders(defaultHeaders: HttpHeader*): Self = withDefaultHeaders(defaultHeaders.toList) - - /** - * Returns a new message that contains all of the given default headers which didn't already - * exist (by case-insensitive header name) in this message. - */ - def withDefaultHeaders(defaultHeaders: immutable.Seq[HttpHeader]): Self = - withHeaders { - if (headers.isEmpty) defaultHeaders - else defaultHeaders.foldLeft(headers) { (acc, h) ⇒ if (headers.exists(_ is h.lowercaseName)) acc else h +: acc } - } - - /** Returns a copy of this message with the entity set to the given one. */ - def withEntity(entity: MessageEntity): Self - - /** Returns a sharable and serializable copy of this message with a strict entity. */ - def toStrict(timeout: FiniteDuration)(implicit ec: ExecutionContext, fm: Materializer): Future[Self] = - entity.toStrict(timeout).fast.map(this.withEntity) - - /** Returns a copy of this message with the entity and headers set to the given ones. */ - def withHeadersAndEntity(headers: immutable.Seq[HttpHeader], entity: MessageEntity): Self - - /** Returns a copy of this message with the list of headers transformed by the given function */ - def mapHeaders(f: immutable.Seq[HttpHeader] ⇒ immutable.Seq[HttpHeader]): Self = withHeaders(f(headers)) - - /** - * The content encoding as specified by the Content-Encoding header. If no Content-Encoding header is present the - * default value 'identity' is returned. - */ - def encoding: HttpEncoding = header[`Content-Encoding`] match { - case Some(x) ⇒ x.encodings.head - case None ⇒ HttpEncodings.identity - } - - /** Returns the first header of the given type if there is one */ - def header[T >: Null <: jm.HttpHeader: ClassTag]: Option[T] = { - val clazz = classTag[T].runtimeClass.asInstanceOf[Class[T]] - HttpHeader.fastFind[T](clazz, headers) match { - case OptionVal.Some(h) ⇒ Some(h) - case _ if clazz == classOf[`Content-Type`] ⇒ Some(`Content-Type`(entity.contentType)).asInstanceOf[Option[T]] - case _ ⇒ None - } - } - - /** - * Returns true if this message is an: - * - HttpRequest and the client does not want to reuse the connection after the response for this request has been received - * - HttpResponse and the server will close the connection after this response - */ - def connectionCloseExpected: Boolean = HttpMessage.connectionCloseExpected(protocol, header[Connection]) - - def addHeader(header: jm.HttpHeader): Self = mapHeaders(_ :+ header.asInstanceOf[HttpHeader]) - - def addCredentials(credentials: jm.headers.HttpCredentials): Self = addHeader(jm.headers.Authorization.create(credentials)) - - /** Removes the header with the given name (case-insensitive) */ - def removeHeader(headerName: String): Self = { - val lowerHeaderName = headerName.toRootLowerCase - mapHeaders(_.filterNot(_.is(lowerHeaderName))) - } - - def withEntity(string: String): Self = withEntity(HttpEntity(string)) - def withEntity(bytes: Array[Byte]): Self = withEntity(HttpEntity(bytes)) - def withEntity(bytes: ByteString): Self = withEntity(HttpEntity(bytes)) - def withEntity(contentType: jm.ContentType.NonBinary, string: String): Self = - withEntity(HttpEntity(contentType.asInstanceOf[ContentType.NonBinary], string)) - def withEntity(contentType: jm.ContentType, bytes: Array[Byte]): Self = withEntity(HttpEntity(contentType.asInstanceOf[ContentType], bytes)) - def withEntity(contentType: jm.ContentType, bytes: ByteString): Self = withEntity(HttpEntity(contentType.asInstanceOf[ContentType], bytes)) - - @deprecated("Use withEntity(ContentType, Path) instead", "2.4.5") - def withEntity(contentType: jm.ContentType, file: File): Self = withEntity(HttpEntity(contentType.asInstanceOf[ContentType], file)) - def withEntity(contentType: jm.ContentType, file: Path): Self = withEntity(HttpEntity.fromPath(contentType.asInstanceOf[ContentType], file)) - - import collection.JavaConverters._ - /** Java API */ - def getHeaders: JIterable[jm.HttpHeader] = (headers: immutable.Seq[jm.HttpHeader]).asJava - /** Java API */ - def getHeader[T <: jm.HttpHeader](headerClass: Class[T]): Optional[T] = - HttpHeader.fastFind[jm.HttpHeader](headerClass.asInstanceOf[Class[jm.HttpHeader]], headers) match { - case OptionVal.Some(h) ⇒ Optional.of(h.asInstanceOf[T]) - case _ ⇒ Optional.empty() - } - /** Java API */ - def getHeader(headerName: String): Optional[jm.HttpHeader] = { - val lowerCased = headerName.toRootLowerCase - Util.convertOption(headers.find(_.is(lowerCased))) // Upcast because of invariance - } - /** Java API */ - def addHeaders(headers: JIterable[jm.HttpHeader]): Self = mapHeaders(_ ++ headers.asScala.asInstanceOf[Iterable[HttpHeader]]) -} - -object HttpMessage { - private[http] def connectionCloseExpected(protocol: HttpProtocol, connectionHeader: Option[Connection]): Boolean = - protocol match { - case HttpProtocols.`HTTP/1.1` ⇒ connectionHeader.isDefined && connectionHeader.get.hasClose - case HttpProtocols.`HTTP/1.0` ⇒ connectionHeader.isEmpty || !connectionHeader.get.hasKeepAlive - } - - /** - * Represents the currently being-drained HTTP Entity which triggers completion of the contained - * Future once the entity has been drained for the given HttpMessage completely. - */ - final class DiscardedEntity(f: Future[Done]) extends akka.http.javadsl.model.HttpMessage.DiscardedEntity { - /** - * This future completes successfully once the underlying entity stream has been - * successfully drained (and fails otherwise). - */ - def future: Future[Done] = f - - /** - * This future completes successfully once the underlying entity stream has been - * successfully drained (and fails otherwise). - */ - def completionStage: CompletionStage[Done] = FutureConverters.toJava(f) - } - - /** Adds Scala DSL idiomatic methods to [[HttpMessage]], e.g. versions of methods with an implicit [[Materializer]]. */ - implicit final class HttpMessageScalaDSLSugar(val httpMessage: HttpMessage) extends AnyVal { - /** - * Discards the entities data bytes by running the `dataBytes` Source contained by the `entity` of this HTTP message. - * - * Note: It is crucial that entities are either discarded, or consumed by running the underlying [[akka.stream.scaladsl.Source]] - * as otherwise the lack of consuming of the data will trigger back-pressure to the underlying TCP connection - * (as designed), however possibly leading to an idle-timeout that will close the connection, instead of - * just having ignored the data. - * - * Warning: It is not allowed to discard and/or consume the `entity.dataBytes` more than once - * as the stream is directly attached to the "live" incoming data source from the underlying TCP connection. - * Allowing it to be consumable twice would require buffering the incoming data, thus defeating the purpose - * of its streaming nature. If the dataBytes source is materialized a second time, it will fail with an - * "stream can cannot be materialized more than once" exception. - * - * In future versions, more automatic ways to warn or resolve these situations may be introduced, see issue #18716. - */ - def discardEntityBytes()(implicit mat: Materializer): HttpMessage.DiscardedEntity = - httpMessage.discardEntityBytes(mat) - } -} - -/** - * The immutable model HTTP request model. - */ -final class HttpRequest( - val method: HttpMethod, - val uri: Uri, - val headers: immutable.Seq[HttpHeader], - val entity: RequestEntity, - val protocol: HttpProtocol) - extends jm.HttpRequest with HttpMessage { - - HttpRequest.verifyUri(uri) - require(entity.isKnownEmpty || method.isEntityAccepted, s"Requests with method '${method.value}' must have an empty entity") - require( - protocol != HttpProtocols.`HTTP/1.0` || !entity.isInstanceOf[HttpEntity.Chunked], - "HTTP/1.0 requests must not have a chunked entity") - - type Self = HttpRequest - def self = this - - override def isRequest = true - override def isResponse = false - - /** - * Resolve this request's URI according to the logic defined at - * http://tools.ietf.org/html/rfc7230#section-5.5 - * - * Throws an [[IllegalUriException]] if the URI is relative and the `headers` don't - * include a valid [[akka.http.scaladsl.model.headers.Host]] header or if URI authority and [[akka.http.scaladsl.model.headers.Host]] header don't match. - */ - def effectiveUri(securedConnection: Boolean, defaultHostHeader: Host = Host.empty): Uri = - HttpRequest.effectiveUri(uri, headers, securedConnection, defaultHostHeader) - - /** - * Returns a copy of this request with the URI resolved according to the logic defined at - * http://tools.ietf.org/html/rfc7230#section-5.5 - */ - def withEffectiveUri(securedConnection: Boolean, defaultHostHeader: Host = Host.empty): HttpRequest = - copy(uri = effectiveUri(securedConnection, defaultHostHeader)) - - /** - * All cookies provided by the client in one or more `Cookie` headers. - */ - def cookies: immutable.Seq[HttpCookiePair] = for (`Cookie`(cookies) ← headers; cookie ← cookies) yield cookie - - /** - * Determines whether this request can be safely retried, which is the case only of the request method is idempotent. - */ - def canBeRetried = method.isIdempotent - - override def withHeaders(headers: immutable.Seq[HttpHeader]): HttpRequest = - if (headers eq this.headers) this else copy(headers = headers) - - override def withHeadersAndEntity(headers: immutable.Seq[HttpHeader], entity: RequestEntity): HttpRequest = copy(headers = headers, entity = entity) - override def withEntity(entity: jm.RequestEntity): HttpRequest = copy(entity = entity.asInstanceOf[RequestEntity]) - override def withEntity(entity: MessageEntity): HttpRequest = copy(entity = entity) - - def mapEntity(f: RequestEntity ⇒ RequestEntity): HttpRequest = withEntity(f(entity)) - - override def withMethod(method: akka.http.javadsl.model.HttpMethod): HttpRequest = copy(method = method.asInstanceOf[HttpMethod]) - override def withProtocol(protocol: akka.http.javadsl.model.HttpProtocol): HttpRequest = copy(protocol = protocol.asInstanceOf[HttpProtocol]) - override def withUri(path: String): HttpRequest = withUri(Uri(path)) - def withUri(uri: Uri): HttpRequest = copy(uri = uri) - - import JavaMapping.Implicits._ - /** Java API */ - override def getUri: jm.Uri = uri.asJava - /** Java API */ - override def withUri(uri: jm.Uri): HttpRequest = copy(uri = uri.asScala) - - /* Manual Case Class things, to easen bin-compat */ - - def copy( - method: HttpMethod = method, - uri: Uri = uri, - headers: immutable.Seq[HttpHeader] = headers, - entity: RequestEntity = entity, - protocol: HttpProtocol = protocol) = new HttpRequest(method, uri, headers, entity, protocol) - - override def hashCode(): Int = { - var result = HashCode.SEED - result = HashCode.hash(result, _1) - result = HashCode.hash(result, _2) - result = HashCode.hash(result, _3) - result = HashCode.hash(result, _4) - result = HashCode.hash(result, _5) - result - } - - override def equals(obj: scala.Any): Boolean = obj match { - case HttpRequest(_method, _uri, _headers, _entity, _protocol) ⇒ - method == _method && - uri == _uri && - headers == _headers && - entity == _entity && - protocol == _protocol - case _ ⇒ false - } - - override def toString = s"""HttpRequest(${_1},${_2},${_3},${_4},${_5})""" - - // name-based unapply accessors - def _1 = method - def _2 = uri - def _3 = headers - def _4 = entity - def _5 = protocol - -} - -object HttpRequest { - /** - * Determines the effective request URI according to the logic defined at - * http://tools.ietf.org/html/rfc7230#section-5.5 - * - * Throws an [[IllegalUriException]] if the URI is relative and the `headers` don't - * include a valid [[akka.http.scaladsl.model.headers.Host]] header or if URI authority and [[akka.http.scaladsl.model.headers.Host]] header don't match. - */ - def effectiveUri(uri: Uri, headers: immutable.Seq[HttpHeader], securedConnection: Boolean, defaultHostHeader: Host): Uri = { - def findHost(headers: immutable.Seq[HttpHeader]): OptionVal[Host] = { - val it = headers.iterator - while (it.hasNext) it.next() match { - case h: Host ⇒ return OptionVal.Some(h) - case _ ⇒ // continue ... - } - OptionVal.None - } - val hostHeader: OptionVal[Host] = findHost(headers) - if (uri.isRelative) { - def fail(detail: String) = - throw IllegalUriException(s"Cannot establish effective URI of request to `$uri`, request has a relative URI and $detail") - val Host(host, port) = hostHeader match { - case OptionVal.None ⇒ if (defaultHostHeader.isEmpty) fail("is missing a `Host` header") else defaultHostHeader - case OptionVal.Some(x) if x.isEmpty ⇒ if (defaultHostHeader.isEmpty) fail("an empty `Host` header") else defaultHostHeader - case OptionVal.Some(x) ⇒ x - } - uri.toEffectiveHttpRequestUri(host, port, securedConnection) - } else // http://tools.ietf.org/html/rfc7230#section-5.4 - if (hostHeader.isEmpty || uri.authority.isEmpty && hostHeader.get.isEmpty || - hostHeader.get.host.equalsIgnoreCase(uri.authority.host) && hostHeader.get.port == uri.authority.port) uri - else throw IllegalUriException( - s"'Host' header value of request to `$uri` doesn't match request target authority", - s"Host header: $hostHeader\nrequest target authority: ${uri.authority}") - } - - /** - * Verifies that the given [[Uri]] is non-empty and has either scheme `http`, `https` or no scheme at all. - * If any of these conditions is not met the method throws an [[IllegalArgumentException]]. - */ - def verifyUri(uri: Uri): Unit = - if (uri.isEmpty) throw new IllegalArgumentException("`uri` must not be empty") - else { - def c(i: Int) = CharUtils.toLowerCase(uri.scheme charAt i) - uri.scheme.length match { - case 0 ⇒ // ok - case 4 if c(0) == 'h' && c(1) == 't' && c(2) == 't' && c(3) == 'p' ⇒ // ok - case 5 if c(0) == 'h' && c(1) == 't' && c(2) == 't' && c(3) == 'p' && c(4) == 's' ⇒ // ok - case _ ⇒ throw new IllegalArgumentException("""`uri` must have scheme "http", "https" or no scheme""") - } - } - - /* Manual Case Class things, to easen bin-compat */ - - def apply( - method: HttpMethod = HttpMethods.GET, - uri: Uri = Uri./, - headers: immutable.Seq[HttpHeader] = Nil, - entity: RequestEntity = HttpEntity.Empty, - protocol: HttpProtocol = HttpProtocols.`HTTP/1.1`) = new HttpRequest(method, uri, headers, entity, protocol) - - def unapply(any: HttpRequest) = new OptHttpRequest(any) -} - -/** - * The immutable HTTP response model. - */ -final class HttpResponse( - val status: StatusCode, - val headers: immutable.Seq[HttpHeader], - val entity: ResponseEntity, - val protocol: HttpProtocol) - extends jm.HttpResponse with HttpMessage { - - require(entity.isKnownEmpty || status.allowsEntity, "Responses with this status code must have an empty entity") - require( - protocol == HttpProtocols.`HTTP/1.1` || !entity.isInstanceOf[HttpEntity.Chunked], - "HTTP/1.0 responses must not have a chunked entity") - - type Self = HttpResponse - def self = this - - override def isRequest = false - override def isResponse = true - - override def withHeaders(headers: immutable.Seq[HttpHeader]) = - if (headers eq this.headers) this else copy(headers = headers) - - override def withProtocol(protocol: akka.http.javadsl.model.HttpProtocol): akka.http.javadsl.model.HttpResponse = copy(protocol = protocol.asInstanceOf[HttpProtocol]) - override def withStatus(statusCode: Int): akka.http.javadsl.model.HttpResponse = copy(status = statusCode) - override def withStatus(statusCode: akka.http.javadsl.model.StatusCode): akka.http.javadsl.model.HttpResponse = copy(status = statusCode.asInstanceOf[StatusCode]) - - override def withHeadersAndEntity(headers: immutable.Seq[HttpHeader], entity: MessageEntity): HttpResponse = withHeadersAndEntity(headers, entity: ResponseEntity) - def withHeadersAndEntity(headers: immutable.Seq[HttpHeader], entity: ResponseEntity): HttpResponse = copy(headers = headers, entity = entity) - override def withEntity(entity: jm.ResponseEntity): HttpResponse = copy(entity = entity.asInstanceOf[ResponseEntity]) - override def withEntity(entity: MessageEntity): HttpResponse = copy(entity = entity) - override def withEntity(entity: jm.RequestEntity): HttpResponse = withEntity(entity: jm.ResponseEntity) - - def mapEntity(f: ResponseEntity ⇒ ResponseEntity): HttpResponse = withEntity(f(entity)) - - /* Manual Case Class things, to easen bin-compat */ - - def copy( - status: StatusCode = status, - headers: immutable.Seq[HttpHeader] = headers, - entity: ResponseEntity = entity, - protocol: HttpProtocol = protocol) = new HttpResponse(status, headers, entity, protocol) - - override def equals(obj: scala.Any): Boolean = obj match { - case HttpResponse(_status, _headers, _entity, _protocol) ⇒ - status == _status && - headers == _headers && - entity == _entity && - protocol == _protocol - case _ ⇒ false - } - - override def hashCode: Int = { - var result = HashCode.SEED - result = HashCode.hash(result, _1) - result = HashCode.hash(result, _2) - result = HashCode.hash(result, _3) - result = HashCode.hash(result, _4) - result - } - - override def toString = s"""HttpResponse(${_1},${_2},${_3},${_4})""" - - // name-based unapply accessors - def _1 = this.status - def _2 = this.headers - def _3 = this.entity - def _4 = this.protocol - -} - -object HttpResponse { - /* Manual Case Class things, to easen bin-compat */ - - def apply( - status: StatusCode = StatusCodes.OK, - headers: immutable.Seq[HttpHeader] = Nil, - entity: ResponseEntity = HttpEntity.Empty, - protocol: HttpProtocol = HttpProtocols.`HTTP/1.1`) = new HttpResponse(status, headers, entity, protocol) - - def unapply(any: HttpResponse): OptHttpResponse = new OptHttpResponse(any) -} - -final class OptHttpRequest(val get: HttpRequest) extends AnyVal { - def isEmpty: Boolean = get == null -} - -final class OptHttpResponse(val get: HttpResponse) extends AnyVal { - def isEmpty: Boolean = get == null -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/HttpMethod.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/HttpMethod.scala deleted file mode 100644 index 820678bc09..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/HttpMethod.scala +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model - -import akka.http.impl.util._ -import akka.http.javadsl.{ model ⇒ jm } -import akka.http.scaladsl.model.RequestEntityAcceptance._ - -sealed trait RequestEntityAcceptance extends jm.RequestEntityAcceptance { - def isEntityAccepted: Boolean -} -object RequestEntityAcceptance { - case object Expected extends RequestEntityAcceptance { - override def isEntityAccepted: Boolean = true - } - case object Tolerated extends RequestEntityAcceptance { - override def isEntityAccepted: Boolean = true - } - case object Disallowed extends RequestEntityAcceptance { - override def isEntityAccepted: Boolean = false - } -} - -/** - * The method of an HTTP request. - * @param isSafe true if the resource should not be altered on the server - * @param isIdempotent true if requests can be safely (& automatically) repeated - * @param requestEntityAcceptance Expected if meaning of request entities is properly defined - */ -final case class HttpMethod private[http] ( - override val value: String, - isSafe: Boolean, - isIdempotent: Boolean, - requestEntityAcceptance: RequestEntityAcceptance) extends jm.HttpMethod with SingletonValueRenderable { - override def isEntityAccepted: Boolean = requestEntityAcceptance.isEntityAccepted - override def toString: String = s"HttpMethod($value)" - override def getRequestEntityAcceptance: jm.RequestEntityAcceptance = requestEntityAcceptance -} - -object HttpMethod { - def custom(name: String, safe: Boolean, idempotent: Boolean, requestEntityAcceptance: RequestEntityAcceptance): HttpMethod = { - require(name.nonEmpty, "value must be non-empty") - require(!safe || idempotent, "An HTTP method cannot be safe without being idempotent") - apply(name, safe, idempotent, requestEntityAcceptance) - } - - /** - * Creates a custom method by name and assumes properties conservatively to be - * safe = false, idempotent = false and requestEntityAcceptance = Expected. - */ - def custom(name: String): HttpMethod = custom(name, safe = false, idempotent = false, requestEntityAcceptance = Expected) -} - -object HttpMethods extends ObjectRegistry[String, HttpMethod] { - private def register(method: HttpMethod): HttpMethod = register(method.value, method) - - // format: OFF - val CONNECT = register(HttpMethod("CONNECT", isSafe = false, isIdempotent = false, requestEntityAcceptance = Disallowed)) - val DELETE = register(HttpMethod("DELETE" , isSafe = false, isIdempotent = true , requestEntityAcceptance = Tolerated)) - val GET = register(HttpMethod("GET" , isSafe = true , isIdempotent = true , requestEntityAcceptance = Tolerated)) - val HEAD = register(HttpMethod("HEAD" , isSafe = true , isIdempotent = true , requestEntityAcceptance = Disallowed)) - val OPTIONS = register(HttpMethod("OPTIONS", isSafe = true , isIdempotent = true , requestEntityAcceptance = Expected)) - val PATCH = register(HttpMethod("PATCH" , isSafe = false, isIdempotent = false, requestEntityAcceptance = Expected)) - val POST = register(HttpMethod("POST" , isSafe = false, isIdempotent = false, requestEntityAcceptance = Expected)) - val PUT = register(HttpMethod("PUT" , isSafe = false, isIdempotent = true , requestEntityAcceptance = Expected)) - val TRACE = register(HttpMethod("TRACE" , isSafe = true , isIdempotent = true , requestEntityAcceptance = Disallowed)) - // format: ON -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/HttpProtocol.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/HttpProtocol.scala deleted file mode 100644 index fca9c2fd7e..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/HttpProtocol.scala +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model - -import akka.http.impl.util.{ SingletonValueRenderable, ObjectRegistry } -import akka.http.javadsl.{ model ⇒ jm } - -/** The protocol of an HTTP message */ -final case class HttpProtocol private[http] (override val value: String) extends jm.HttpProtocol with SingletonValueRenderable - -object HttpProtocols extends ObjectRegistry[String, HttpProtocol] { - private def register(p: HttpProtocol): HttpProtocol = register(p.value, p) - - val `HTTP/1.0` = register(HttpProtocol("HTTP/1.0")) - val `HTTP/1.1` = register(HttpProtocol("HTTP/1.1")) -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/MediaRange.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/MediaRange.scala deleted file mode 100644 index ed8c385099..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/MediaRange.scala +++ /dev/null @@ -1,148 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model - -import language.implicitConversions -import java.util -import akka.http.impl.util._ -import akka.http.javadsl.{ model ⇒ jm } - -sealed abstract class MediaRange extends jm.MediaRange with Renderable with WithQValue[MediaRange] { - def value: String - def mainType: String - def params: Map[String, String] - def qValue: Float - def matches(mediaType: MediaType): Boolean - def isApplication = false - def isAudio = false - def isImage = false - def isMessage = false - def isMultipart = false - def isText = false - def isVideo = false - def isWildcard = mainType == "*" - - /** - * Returns a copy of this instance with the params replaced by the given ones. - * If the given map contains a "q" value the `qValue` member is (also) updated. - */ - def withParams(params: Map[String, String]): MediaRange - - /** - * Constructs a `ContentTypeRange` from this instance and the given charset. - */ - def withCharsetRange(charsetRange: HttpCharsetRange): ContentTypeRange = ContentTypeRange(this, charsetRange) - - /** Java API */ - def getParams: util.Map[String, String] = { - import collection.JavaConverters._ - params.asJava - } - /** Java API */ - def matches(mediaType: jm.MediaType): Boolean = { - import akka.http.impl.util.JavaMapping.Implicits._ - matches(mediaType.asScala) - } -} - -object MediaRange { - private[http] def splitOffQValue(params: Map[String, String], defaultQ: Float = 1.0f): (Map[String, String], Float) = - params.get("q") match { - case Some(x) ⇒ (params - "q") → (try x.toFloat catch { case _: NumberFormatException ⇒ 1.0f }) - case None ⇒ params → defaultQ - } - - private final case class Custom(mainType: String, params: Map[String, String], qValue: Float) - extends MediaRange with ValueRenderable { - require(0.0f <= qValue && qValue <= 1.0f, "qValue must be >= 0 and <= 1.0") - def matches(mediaType: MediaType) = mainType == "*" || mediaType.mainType == mainType - def withParams(params: Map[String, String]) = custom(mainType, params, qValue) - def withQValue(qValue: Float) = if (qValue != this.qValue) custom(mainType, params, qValue) else this - def render[R <: Rendering](r: R): r.type = { - r ~~ mainType ~~ '/' ~~ '*' - if (qValue < 1.0f) r ~~ ";q=" ~~ qValue - if (params.nonEmpty) params foreach { case (k, v) ⇒ r ~~ ';' ~~ ' ' ~~ k ~~ '=' ~~# v } - r - } - override def isApplication = mainType == "application" - override def isAudio = mainType == "audio" - override def isImage = mainType == "image" - override def isMessage = mainType == "message" - override def isMultipart = mainType == "multipart" - override def isText = mainType == "text" - override def isVideo = mainType == "video" - } - - def custom(mainType: String, params: Map[String, String] = Map.empty, qValue: Float = 1.0f): MediaRange = { - val (ps, q) = splitOffQValue(params, qValue) - Custom(mainType.toRootLowerCase, ps, q) - } - - final case class One(mediaType: MediaType, qValue: Float) extends MediaRange with ValueRenderable { - require(0.0f <= qValue && qValue <= 1.0f, "qValue must be >= 0 and <= 1.0") - def mainType = mediaType.mainType - def params = mediaType.params - override def isApplication = mediaType.isApplication - override def isAudio = mediaType.isAudio - override def isImage = mediaType.isImage - override def isMessage = mediaType.isMessage - override def isMultipart = mediaType.isMultipart - override def isText = mediaType.isText - override def isVideo = mediaType.isVideo - def matches(mediaType: MediaType) = - this.mediaType.mainType == mediaType.mainType && this.mediaType.subType == mediaType.subType - def withParams(params: Map[String, String]) = copy(mediaType = mediaType.withParams(params)) - def withQValue(qValue: Float) = copy(qValue = qValue) - def render[R <: Rendering](r: R): r.type = if (qValue < 1.0f) r ~~ mediaType ~~ ";q=" ~~ qValue else r ~~ mediaType - } - - implicit def apply(mediaType: MediaType): MediaRange = apply(mediaType, 1.0f) - def apply(mediaType: MediaType, qValue: Float = 1.0f): MediaRange = One(mediaType, qValue) -} - -object MediaRanges extends ObjectRegistry[String, MediaRange] { - - sealed abstract case class PredefinedMediaRange(value: String) extends MediaRange with LazyValueBytesRenderable { - val mainType = value takeWhile (_ != '/') - register(mainType, this) - def params = Map.empty - def qValue = 1.0f - def withParams(params: Map[String, String]) = MediaRange.custom(mainType, params) - def withQValue(qValue: Float) = if (qValue != 1.0f) MediaRange.custom(mainType, params, qValue) else this - } - - val `*/*` = new PredefinedMediaRange("*/*") { - def matches(mediaType: MediaType) = true - } - val `*/*;q=MIN` = `*/*`.withQValue(Float.MinPositiveValue) - val `application/*` = new PredefinedMediaRange("application/*") { - def matches(mediaType: MediaType) = mediaType.isApplication - override def isApplication = true - } - val `audio/*` = new PredefinedMediaRange("audio/*") { - def matches(mediaType: MediaType) = mediaType.isAudio - override def isAudio = true - } - val `image/*` = new PredefinedMediaRange("image/*") { - def matches(mediaType: MediaType) = mediaType.isImage - override def isImage = true - } - val `message/*` = new PredefinedMediaRange("message/*") { - def matches(mediaType: MediaType) = mediaType.isMessage - override def isMessage = true - } - val `multipart/*` = new PredefinedMediaRange("multipart/*") { - def matches(mediaType: MediaType) = mediaType.isMultipart - override def isMultipart = true - } - val `text/*` = new PredefinedMediaRange("text/*") { - def matches(mediaType: MediaType) = mediaType.isText - override def isText = true - } - val `video/*` = new PredefinedMediaRange("video/*") { - def matches(mediaType: MediaType) = mediaType.isVideo - override def isVideo = true - } -} \ No newline at end of file diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/MediaType.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/MediaType.scala deleted file mode 100644 index dc78006599..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/MediaType.scala +++ /dev/null @@ -1,482 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model - -import akka.http.impl.util._ -import akka.http.javadsl.{ model ⇒ jm } -import akka.http.impl.util.JavaMapping.Implicits._ - -/** - * A MediaType describes the type of the content of an HTTP message entity. - * - * While knowledge of the MediaType alone suffices for being able to properly interpret binary content this - * is not generally the case for non-binary (i.e. character-based) content, which also requires the definition - * of a specific character encoding ([[HttpCharset]]). - * Therefore [[MediaType]] instances are frequently encountered as a member of a [[ContentType]], which - * groups a [[MediaType]] with a potentially required [[HttpCharset]] to hold everything required for being - * able to interpret an [[HttpEntity]]. - * - * MediaTypes come in three basic forms: - * - * 1. Binary: These do not need an additional [[HttpCharset]] to be able to form a [[ContentType]]. Therefore - * they can be implicitly converted to the latter. - * - * 2. WithOpenCharset: Most character-based MediaTypes are of this form, which can be combined with all - * [[HttpCharset]] instances to form a [[ContentType]]. - * - * 3. WithFixedCharset: Some character-based MediaTypes prescribe a single, clearly defined charset and as such, - * similarly to binary MediaTypes, do not require the addition of an [[HttpCharset]] instances to form a - * [[ContentType]]. The most prominent example is probably `application/json` which must always be UTF-8 encoded. - * Like binary MediaTypes `WithFixedCharset` types can be implicitly converted to a [[ContentType]]. - */ -sealed abstract class MediaType extends jm.MediaType with LazyValueBytesRenderable with WithQValue[MediaRange] { - import MediaType.Compressibility - - def fileExtensions: List[String] - def params: Map[String, String] - def comp: Compressibility - - override def isApplication: Boolean = false - override def isAudio: Boolean = false - override def isImage: Boolean = false - override def isMessage: Boolean = false - override def isMultipart: Boolean = false - override def isText: Boolean = false - override def isVideo: Boolean = false - - def withParams(params: Map[String, String]): MediaType - def withComp(comp: Compressibility): MediaType - def withQValue(qValue: Float): MediaRange = MediaRange(this, qValue.toFloat) - - override def equals(that: Any): Boolean = - that match { - case x: MediaType ⇒ value equalsIgnoreCase x.value - case _ ⇒ false - } - - override def hashCode(): Int = value.hashCode - - /** - * JAVA API - */ - def toRange = jm.MediaRanges.create(this) - def toRange(qValue: Float) = jm.MediaRanges.create(this, qValue) - def isCompressible: Boolean = comp.compressible -} - -object MediaType { - - def applicationBinary(subType: String, comp: Compressibility, fileExtensions: String*): Binary = - new Binary("application/" + subType, "application", subType, comp, fileExtensions.toList) { - override def isApplication = true - } - - def applicationWithFixedCharset(subType: String, charset: HttpCharset, - fileExtensions: String*): WithFixedCharset = - new WithFixedCharset("application/" + subType, "application", subType, charset, fileExtensions.toList) { - override def isApplication = true - } - - def applicationWithOpenCharset(subType: String, fileExtensions: String*): WithOpenCharset = - new NonMultipartWithOpenCharset("application/" + subType, "application", subType, fileExtensions.toList) { - override def isApplication = true - } - - def audio(subType: String, comp: Compressibility, fileExtensions: String*): Binary = - new Binary("audio/" + subType, "audio", subType, comp, fileExtensions.toList) { - override def isAudio = true - } - - def image(subType: String, comp: Compressibility, fileExtensions: String*): Binary = - new Binary("image/" + subType, "image", subType, comp, fileExtensions.toList) { - override def isImage = true - } - - def message(subType: String, comp: Compressibility, fileExtensions: String*): Binary = - new Binary("message/" + subType, "message", subType, comp, fileExtensions.toList) { - override def isMessage = true - } - - def text(subType: String, fileExtensions: String*): WithOpenCharset = - new NonMultipartWithOpenCharset("text/" + subType, "text", subType, fileExtensions.toList) { - override def isText = true - } - - def video(subType: String, comp: Compressibility, fileExtensions: String*): Binary = - new Binary("video/" + subType, "video", subType, comp, fileExtensions.toList) { - override def isVideo = true - } - - def customBinary(mainType: String, subType: String, comp: Compressibility, fileExtensions: List[String] = Nil, - params: Map[String, String] = Map.empty, allowArbitrarySubtypes: Boolean = false): Binary = { - require(mainType != "multipart", "Cannot create a MediaType.Multipart here, use `customMultipart` instead!") - require(allowArbitrarySubtypes || subType != "*", "Cannot create a MediaRange here, use `MediaRange.custom` instead!") - val _params = params - new Binary(renderValue(mainType, subType, params), mainType, subType, comp, fileExtensions) { - override def params = _params - override def isApplication = mainType == "application" - override def isAudio = mainType == "audio" - override def isImage = mainType == "image" - override def isMessage = mainType == "message" - override def isText = mainType == "text" - override def isVideo = mainType == "video" - } - } - - def customWithFixedCharset(mainType: String, subType: String, charset: HttpCharset, fileExtensions: List[String] = Nil, - params: Map[String, String] = Map.empty, - allowArbitrarySubtypes: Boolean = false): WithFixedCharset = { - require(mainType != "multipart", "Cannot create a MediaType.Multipart here, use `customMultipart` instead!") - require(allowArbitrarySubtypes || subType != "*", "Cannot create a MediaRange here, use `MediaRange.custom` instead!") - val _params = params - new WithFixedCharset(renderValue(mainType, subType, params), mainType, subType, charset, fileExtensions) { - override def params = _params - override def isApplication = mainType == "application" - override def isAudio = mainType == "audio" - override def isImage = mainType == "image" - override def isMessage = mainType == "message" - override def isText = mainType == "text" - override def isVideo = mainType == "video" - } - } - - def customWithOpenCharset(mainType: String, subType: String, fileExtensions: List[String] = Nil, - params: Map[String, String] = Map.empty, - allowArbitrarySubtypes: Boolean = false): WithOpenCharset = { - require(mainType != "multipart", "Cannot create a MediaType.Multipart here, use `customMultipart` instead!") - require(allowArbitrarySubtypes || subType != "*", "Cannot create a MediaRange here, use `MediaRange.custom` instead!") - val _params = params - new NonMultipartWithOpenCharset(renderValue(mainType, subType, params), mainType, subType, fileExtensions) { - override def params = _params - override def isApplication = mainType == "application" - override def isAudio = mainType == "audio" - override def isImage = mainType == "image" - override def isMessage = mainType == "message" - override def isText = mainType == "text" - override def isVideo = mainType == "video" - } - } - - def customMultipart(subType: String, params: Map[String, String]): Multipart = { - require(subType != "*", "Cannot create a MediaRange here, use MediaRanges.`multipart/*` instead!") - new Multipart(subType, params) - } - - def custom(value: String, binary: Boolean, comp: Compressibility = Compressible, - fileExtensions: List[String] = Nil): MediaType = { - val parts = value.split('/') - require(parts.length == 2, s"`$value` is not a valid media-type. It must consist of two parts separated by '/'.") - if (binary) customBinary(parts(0), parts(1), comp, fileExtensions) - else customWithOpenCharset(parts(0), parts(1), fileExtensions) - } - - /** - * Tries to parse a `MediaType` value from the given String. - * Returns `Right(mediaType)` if successful and `Left(errors)` otherwise. - */ - def parse(value: String): Either[List[ErrorInfo], MediaType] = - ContentType.parse(value).right.map(_.mediaType) - - def unapply(mediaType: MediaType): Option[String] = Some(mediaType.value) - - ///////////////////////////////////////////////////////////////////////// - - private def renderValue(mainType: String, subType: String, params: Map[String, String]): String = { - val r = new StringRendering ~~ mainType ~~ '/' ~~ subType - if (params.nonEmpty) params foreach { case (k, v) ⇒ r ~~ ';' ~~ ' ' ~~ k ~~ '=' ~~# v } - r.get - } - - sealed abstract class Binary(val value: String, val mainType: String, val subType: String, val comp: Compressibility, - val fileExtensions: List[String]) extends MediaType with jm.MediaType.Binary { - def binary = true - def params: Map[String, String] = Map.empty - def withParams(params: Map[String, String]): Binary with MediaType = - customBinary(mainType, subType, comp, fileExtensions, params) - def withComp(comp: Compressibility): Binary with MediaType = - customBinary(mainType, subType, comp, fileExtensions, params) - - /** - * JAVA API - */ - def toContentType: ContentType.Binary = ContentType(this) - } - - sealed abstract class NonBinary extends MediaType with jm.MediaType.NonBinary { - def binary = false - def comp = Compressible - def withComp(comp: Compressibility): Binary with MediaType = - customBinary(mainType, subType, comp, fileExtensions, params) - } - - sealed abstract class WithFixedCharset(val value: String, val mainType: String, val subType: String, - val charset: HttpCharset, val fileExtensions: List[String]) - extends NonBinary with jm.MediaType.WithFixedCharset { - def params: Map[String, String] = Map.empty - def withParams(params: Map[String, String]): WithFixedCharset with MediaType = - customWithFixedCharset(mainType, subType, charset, fileExtensions, params) - - /** - * JAVA API - */ - def toContentType: ContentType.WithFixedCharset = ContentType(this) - } - - sealed abstract class WithOpenCharset extends NonBinary with jm.MediaType.WithOpenCharset { - def withCharset(charset: HttpCharset): ContentType.WithCharset = ContentType(this, charset) - - /** - * JAVA API - */ - def toContentType(charset: jm.HttpCharset): ContentType.WithCharset = withCharset(charset.asScala) - } - - sealed abstract class NonMultipartWithOpenCharset(val value: String, val mainType: String, val subType: String, - val fileExtensions: List[String]) extends WithOpenCharset { - def params: Map[String, String] = Map.empty - def withParams(params: Map[String, String]): WithOpenCharset with MediaType = - customWithOpenCharset(mainType, subType, fileExtensions, params) - } - - final class Multipart(val subType: String, val params: Map[String, String]) - extends WithOpenCharset with jm.MediaType.Multipart { - val value = renderValue(mainType, subType, params) - override def mainType = "multipart" - override def isMultipart = true - override def fileExtensions = Nil - def withParams(params: Map[String, String]): MediaType.Multipart = new MediaType.Multipart(subType, params) - def withBoundary(boundary: String): MediaType.Multipart = - withParams(if (boundary.isEmpty) params - "boundary" else params.updated("boundary", boundary)) - } - - sealed class Compressibility(val compressible: Boolean) extends jm.MediaType.Compressibility - case object Compressible extends Compressibility(compressible = true) - case object NotCompressible extends Compressibility(compressible = false) - case object Gzipped extends Compressibility(compressible = false) -} - -object MediaTypes extends ObjectRegistry[(String, String), MediaType] { - type FindCustom = (String, String) ⇒ Option[MediaType] - - private[this] var extensionMap = Map.empty[String, MediaType] - - def forExtensionOption(ext: String): Option[MediaType] = extensionMap.get(ext.toLowerCase) - def forExtension(ext: String): MediaType = extensionMap.getOrElse(ext.toLowerCase, `application/octet-stream`) - - private def registerFileExtensions[T <: MediaType](mediaType: T): T = { - mediaType.fileExtensions.foreach { ext ⇒ - val lcExt = ext.toLowerCase - require(!extensionMap.contains(lcExt), s"Extension '$ext' clash: media-types '${extensionMap(lcExt)}' and '$mediaType'") - extensionMap = extensionMap.updated(lcExt, mediaType) - } - mediaType - } - - private def register[T <: MediaType](mediaType: T): T = { - registerFileExtensions(mediaType) - register(mediaType.mainType.toRootLowerCase → mediaType.subType.toRootLowerCase, mediaType) - } - - import MediaType._ - - /////////////////////////// PREDEFINED MEDIA-TYPE DEFINITION //////////////////////////// - // format: OFF - - private def abin(st: String, c: Compressibility, fe: String*) = register(applicationBinary(st, c, fe: _*)) - private def awfc(st: String, cs: HttpCharset, fe: String*) = register(applicationWithFixedCharset(st, cs, fe: _*)) - private def awoc(st: String, fe: String*) = register(applicationWithOpenCharset(st, fe: _*)) - private def aud(st: String, c: Compressibility, fe: String*) = register(audio(st, c, fe: _*)) - private def img(st: String, c: Compressibility, fe: String*) = register(image(st, c, fe: _*)) - private def msg(st: String, fe: String*) = register(message(st, Compressible, fe: _*)) - private def txt(st: String, fe: String*) = register(text(st, fe: _*)) - private def vid(st: String, fe: String*) = register(video(st, NotCompressible, fe: _*)) - - // dummy value currently only used by ContentType.NoContentType - private[http] val NoMediaType = MediaType.customBinary("none", "none", comp = NotCompressible) - - val `application/atom+xml` = awoc("atom+xml", "atom") - val `application/base64` = awoc("base64", "mm", "mme") - val `application/excel` = abin("excel", NotCompressible, "xl", "xla", "xlb", "xlc", "xld", "xlk", "xll", "xlm", "xls", "xlt", "xlv", "xlw") - val `application/font-woff` = abin("font-woff", NotCompressible, "woff") - val `application/gnutar` = abin("gnutar", NotCompressible, "tgz") - val `application/java-archive` = abin("java-archive", NotCompressible, "jar", "war", "ear") - val `application/javascript` = awoc("javascript", "js") - val `application/json` = awfc("json", HttpCharsets.`UTF-8`, "json") - val `application/json-patch+json` = awfc("json-patch+json", HttpCharsets.`UTF-8`) - val `application/lha` = abin("lha", NotCompressible, "lha") - val `application/lzx` = abin("lzx", NotCompressible, "lzx") - val `application/mspowerpoint` = abin("mspowerpoint", NotCompressible, "pot", "pps", "ppt", "ppz") - val `application/msword` = abin("msword", NotCompressible, "doc", "dot", "w6w", "wiz", "word", "wri") - val `application/octet-stream` = abin("octet-stream", NotCompressible, "a", "bin", "class", "dump", "exe", "lhx", "lzh", "o", "psd", "saveme", "zoo") - val `application/pdf` = abin("pdf", NotCompressible, "pdf") - val `application/postscript` = abin("postscript", Compressible, "ai", "eps", "ps") - val `application/rss+xml` = awoc("rss+xml", "rss") - val `application/soap+xml` = awoc("soap+xml") - val `application/vnd.api+json` = awfc("vnd.api+json", HttpCharsets.`UTF-8`) - val `application/vnd.google-earth.kml+xml` = awoc("vnd.google-earth.kml+xml", "kml") - val `application/vnd.google-earth.kmz` = abin("vnd.google-earth.kmz", NotCompressible, "kmz") - val `application/vnd.ms-fontobject` = abin("vnd.ms-fontobject", Compressible, "eot") - val `application/vnd.oasis.opendocument.chart` = abin("vnd.oasis.opendocument.chart", Compressible, "odc") - val `application/vnd.oasis.opendocument.database` = abin("vnd.oasis.opendocument.database", Compressible, "odb") - val `application/vnd.oasis.opendocument.formula` = abin("vnd.oasis.opendocument.formula", Compressible, "odf") - val `application/vnd.oasis.opendocument.graphics` = abin("vnd.oasis.opendocument.graphics", Compressible, "odg") - val `application/vnd.oasis.opendocument.image` = abin("vnd.oasis.opendocument.image", Compressible, "odi") - val `application/vnd.oasis.opendocument.presentation` = abin("vnd.oasis.opendocument.presentation", Compressible, "odp") - val `application/vnd.oasis.opendocument.spreadsheet` = abin("vnd.oasis.opendocument.spreadsheet", Compressible, "ods") - val `application/vnd.oasis.opendocument.text` = abin("vnd.oasis.opendocument.text", Compressible, "odt") - val `application/vnd.oasis.opendocument.text-master` = abin("vnd.oasis.opendocument.text-master", Compressible, "odm", "otm") - val `application/vnd.oasis.opendocument.text-web` = abin("vnd.oasis.opendocument.text-web", Compressible, "oth") - val `application/vnd.openxmlformats-officedocument.presentationml.presentation` = abin("vnd.openxmlformats-officedocument.presentationml.presentation", Compressible, "pptx") - val `application/vnd.openxmlformats-officedocument.presentationml.slide` = abin("vnd.openxmlformats-officedocument.presentationml.slide", Compressible, "sldx") - val `application/vnd.openxmlformats-officedocument.presentationml.slideshow` = abin("vnd.openxmlformats-officedocument.presentationml.slideshow", Compressible, "ppsx") - val `application/vnd.openxmlformats-officedocument.presentationml.template` = abin("vnd.openxmlformats-officedocument.presentationml.template", Compressible, "potx") - val `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` = abin("vnd.openxmlformats-officedocument.spreadsheetml.sheet", Compressible, "xlsx") - val `application/vnd.openxmlformats-officedocument.spreadsheetml.template` = abin("vnd.openxmlformats-officedocument.spreadsheetml.template", Compressible, "xltx") - val `application/vnd.openxmlformats-officedocument.wordprocessingml.document` = abin("vnd.openxmlformats-officedocument.wordprocessingml.document", Compressible, "docx") - val `application/vnd.openxmlformats-officedocument.wordprocessingml.template` = abin("vnd.openxmlformats-officedocument.wordprocessingml.template", Compressible, "dotx") - val `application/x-7z-compressed` = abin("x-7z-compressed", NotCompressible, "7z", "s7z") - val `application/x-ace-compressed` = abin("x-ace-compressed", NotCompressible, "ace") - val `application/x-apple-diskimage` = abin("x-apple-diskimage", NotCompressible, "dmg") - val `application/x-arc-compressed` = abin("x-arc-compressed", NotCompressible, "arc") - val `application/x-bzip` = abin("x-bzip", NotCompressible, "bz") - val `application/x-bzip2` = abin("x-bzip2", NotCompressible, "boz", "bz2") - val `application/x-chrome-extension` = abin("x-chrome-extension", NotCompressible, "crx") - val `application/x-compress` = abin("x-compress", NotCompressible, "z") - val `application/x-compressed` = abin("x-compressed", NotCompressible, "gz") - val `application/x-debian-package` = abin("x-debian-package", Compressible, "deb") - val `application/x-dvi` = abin("x-dvi", Compressible, "dvi") - val `application/x-font-truetype` = abin("x-font-truetype", Compressible, "ttf") - val `application/x-font-opentype` = abin("x-font-opentype", Compressible, "otf") - val `application/x-gtar` = abin("x-gtar", NotCompressible, "gtar") - val `application/x-gzip` = abin("x-gzip", NotCompressible, "gzip") - val `application/x-latex` = awoc("x-latex", "latex", "ltx") - val `application/x-rar-compressed` = abin("x-rar-compressed", NotCompressible, "rar") - val `application/x-redhat-package-manager` = abin("x-redhat-package-manager", NotCompressible, "rpm") - val `application/x-shockwave-flash` = abin("x-shockwave-flash", NotCompressible, "swf") - val `application/x-tar` = abin("x-tar", Compressible, "tar") - val `application/x-tex` = abin("x-tex", Compressible, "tex") - val `application/x-texinfo` = abin("x-texinfo", Compressible, "texi", "texinfo") - val `application/x-vrml` = awoc("x-vrml", "vrml") - val `application/x-www-form-urlencoded` = awoc("x-www-form-urlencoded") - val `application/x-x509-ca-cert` = abin("x-x509-ca-cert", Compressible, "der") - val `application/x-xpinstall` = abin("x-xpinstall", NotCompressible, "xpi") - val `application/xhtml+xml` = awoc("xhtml+xml") - val `application/xml-dtd` = awoc("xml-dtd") - val `application/xml` = awoc("xml") - val `application/zip` = abin("zip", NotCompressible, "zip") - - val `audio/aiff` = aud("aiff", Compressible, "aif", "aifc", "aiff") - val `audio/basic` = aud("basic", Compressible, "au", "snd") - val `audio/midi` = aud("midi", Compressible, "mid", "midi", "kar") - val `audio/mod` = aud("mod", NotCompressible, "mod") - val `audio/mpeg` = aud("mpeg", NotCompressible, "m2a", "mp2", "mp3", "mpa", "mpga") - val `audio/ogg` = aud("ogg", NotCompressible, "oga", "ogg") - val `audio/voc` = aud("voc", NotCompressible, "voc") - val `audio/vorbis` = aud("vorbis", NotCompressible, "vorbis") - val `audio/voxware` = aud("voxware", NotCompressible, "vox") - val `audio/wav` = aud("wav", Compressible, "wav") - val `audio/x-realaudio` = aud("x-pn-realaudio", NotCompressible, "ra", "ram", "rmm", "rmp") - val `audio/x-psid` = aud("x-psid", Compressible, "sid") - val `audio/xm` = aud("xm", NotCompressible, "xm") - val `audio/webm` = aud("webm", NotCompressible) - - val `image/gif` = img("gif", NotCompressible, "gif") - val `image/jpeg` = img("jpeg", NotCompressible, "jpe", "jpeg", "jpg") - val `image/pict` = img("pict", Compressible, "pic", "pict") - val `image/png` = img("png", NotCompressible, "png") - val `image/svg+xml` = img("svg+xml", Compressible, "svg") - val `image/svgz` = registerFileExtensions(image("svg+xml", Gzipped, "svgz")) - val `image/tiff` = img("tiff", Compressible, "tif", "tiff") - val `image/x-icon` = img("x-icon", Compressible, "ico") - val `image/x-ms-bmp` = img("x-ms-bmp", Compressible, "bmp") - val `image/x-pcx` = img("x-pcx", Compressible, "pcx") - val `image/x-pict` = img("x-pict", Compressible, "pct") - val `image/x-quicktime` = img("x-quicktime", NotCompressible, "qif", "qti", "qtif") - val `image/x-rgb` = img("x-rgb", Compressible, "rgb") - val `image/x-xbitmap` = img("x-xbitmap", Compressible, "xbm") - val `image/x-xpixmap` = img("x-xpixmap", Compressible, "xpm") - val `image/webp` = img("webp", NotCompressible, "webp") - - val `message/http` = msg("http") - val `message/delivery-status` = msg("delivery-status") - val `message/rfc822` = msg("rfc822", "eml", "mht", "mhtml", "mime") - - object multipart { - def mixed (params: Map[String, String]) = new MediaType.Multipart("mixed", params) - def alternative(params: Map[String, String]) = new MediaType.Multipart("alternative", params) - def related (params: Map[String, String]) = new MediaType.Multipart("related", params) - def `form-data`(params: Map[String, String]) = new MediaType.Multipart("form-data", params) - def signed (params: Map[String, String]) = new MediaType.Multipart("signed", params) - def encrypted (params: Map[String, String]) = new MediaType.Multipart("encrypted", params) - def byteRanges (params: Map[String, String]) = new MediaType.Multipart("byteranges", params) - } - - val `multipart/mixed` = multipart.mixed(Map.empty) - val `multipart/alternative` = multipart.alternative(Map.empty) - val `multipart/related` = multipart.related(Map.empty) - val `multipart/form-data` = multipart.`form-data`(Map.empty) - val `multipart/signed` = multipart.signed(Map.empty) - val `multipart/encrypted` = multipart.encrypted(Map.empty) - val `multipart/byteranges` = multipart.byteRanges(Map.empty) - - val `text/asp` = txt("asp", "asp") - val `text/cache-manifest` = txt("cache-manifest", "manifest") - val `text/calendar` = txt("calendar", "ics") - val `text/css` = txt("css", "css") - val `text/csv` = txt("csv", "csv") - val `text/html` = txt("html", "htm", "html", "htmls", "htx") - val `text/markdown` = txt("markdown", "markdown", "md") - val `text/mcf` = txt("mcf", "mcf") - val `text/plain` = txt("plain", "conf", "text", "txt", "properties") - val `text/richtext` = txt("richtext", "rtf", "rtx") - val `text/tab-separated-values` = txt("tab-separated-values", "tsv") - val `text/uri-list` = txt("uri-list", "uni", "unis", "uri", "uris") - val `text/vnd.wap.wml` = txt("vnd.wap.wml", "wml") - val `text/vnd.wap.wmlscript` = txt("vnd.wap.wmlscript", "wmls") - val `text/x-asm` = txt("x-asm", "asm", "s") - val `text/x-c` = txt("x-c", "c", "cc", "cpp") - val `text/x-component` = txt("x-component", "htc") - val `text/x-h` = txt("x-h", "h", "hh") - val `text/x-java-source` = txt("x-java-source", "jav", "java") - val `text/x-pascal` = txt("x-pascal", "p") - val `text/x-script` = txt("x-script", "hlb") - val `text/x-scriptcsh` = txt("x-scriptcsh", "csh") - val `text/x-scriptelisp` = txt("x-scriptelisp", "el") - val `text/x-scriptksh` = txt("x-scriptksh", "ksh") - val `text/x-scriptlisp` = txt("x-scriptlisp", "lsp") - val `text/x-scriptperl` = txt("x-scriptperl", "pl") - val `text/x-scriptperl-module` = txt("x-scriptperl-module", "pm") - val `text/x-scriptphyton` = txt("x-scriptphyton", "py") - val `text/x-scriptrexx` = txt("x-scriptrexx", "rexx") - val `text/x-scriptscheme` = txt("x-scriptscheme", "scm") - val `text/x-scriptsh` = txt("x-scriptsh", "sh") - val `text/x-scripttcl` = txt("x-scripttcl", "tcl") - val `text/x-scripttcsh` = txt("x-scripttcsh", "tcsh") - val `text/x-scriptzsh` = txt("x-scriptzsh", "zsh") - val `text/x-server-parsed-html` = txt("x-server-parsed-html", "shtml", "ssi") - val `text/x-setext` = txt("x-setext", "etx") - val `text/x-sgml` = txt("x-sgml", "sgm", "sgml") - val `text/x-speech` = txt("x-speech", "spc", "talk") - val `text/x-uuencode` = txt("x-uuencode", "uu", "uue") - val `text/x-vcalendar` = txt("x-vcalendar", "vcs") - val `text/x-vcard` = txt("x-vcard", "vcf", "vcard") - val `text/xml` = txt("xml", "xml") - - val `video/avs-video` = vid("avs-video", "avs") - val `video/divx` = vid("divx", "divx") - val `video/gl` = vid("gl", "gl") - val `video/mp4` = vid("mp4", "mp4") - val `video/mpeg` = vid("mpeg", "m1v", "m2v", "mpe", "mpeg", "mpg") - val `video/ogg` = vid("ogg", "ogv") - val `video/quicktime` = vid("quicktime", "moov", "mov", "qt") - val `video/x-dv` = vid("x-dv", "dif", "dv") - val `video/x-flv` = vid("x-flv", "flv") - val `video/x-motion-jpeg` = vid("x-motion-jpeg", "mjpg") - val `video/x-ms-asf` = vid("x-ms-asf", "asf") - val `video/x-msvideo` = vid("x-msvideo", "avi") - val `video/x-sgi-movie` = vid("x-sgi-movie", "movie", "mv") - val `video/webm` = vid("webm", "webm") - // format: ON -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/Multipart.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/Multipart.scala deleted file mode 100644 index e57b1578ba..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/Multipart.scala +++ /dev/null @@ -1,624 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model -import java.io.File -import java.nio.file.Path -import java.util.Optional -import akka.http.impl.util.Util -import scala.concurrent.duration.FiniteDuration -import scala.concurrent.Future -import scala.collection.immutable -import scala.collection.JavaConverters._ -import scala.util.{ Failure, Success, Try } -import akka.event.{ NoLogging, LoggingAdapter } -import akka.stream.impl.ConstantFun -import akka.stream.Materializer -import akka.stream.javadsl.{ Source ⇒ JSource } -import akka.stream.scaladsl._ -import akka.http.scaladsl.util.FastFuture -import akka.http.scaladsl.model.headers._ -import akka.http.impl.engine.rendering.BodyPartRenderer -import akka.http.javadsl.{ model ⇒ jm } -import FastFuture._ -import akka.http.impl.util.JavaMapping.Implicits._ -import scala.compat.java8.FutureConverters._ -import java.util.concurrent.CompletionStage - -/** - * The model of multipart content for media-types `multipart/\*` (general multipart content), - * `multipart/form-data` and `multipart/byteranges`. - * - * The basic modelling classes for these media-types ([[Multipart.General]], [[Multipart.FormData]] and - * [[Multipart.ByteRanges]], respectively) are stream-based but each have a strict counterpart - * (namely [[Multipart.General.Strict]], [[Multipart.FormData.Strict]] and [[Multipart.ByteRanges.Strict]]). - */ -sealed trait Multipart extends jm.Multipart { - - /** - * The media-type this multipart content carries. - */ - def mediaType: MediaType.Multipart - - /** - * The stream of body parts this content consists of. - */ - def parts: Source[Multipart.BodyPart, Any] - - /** - * Converts this content into its strict counterpart. - * The given `timeout` denotes the max time that an individual part must be read in. - * The Future is failed with an TimeoutException if one part isn't read completely after the given timeout. - */ - def toStrict(timeout: FiniteDuration)(implicit fm: Materializer): Future[Multipart.Strict] - - /** - * Creates a [[akka.http.scaladsl.model.MessageEntity]] from this multipart object. - */ - def toEntity( - charset: HttpCharset = HttpCharsets.`UTF-8`, - boundary: String = BodyPartRenderer.randomBoundary())(implicit log: LoggingAdapter = NoLogging): MessageEntity = { - val chunks = - parts - .via(BodyPartRenderer.streamed(boundary, charset.nioCharset, partHeadersSizeHint = 128, log)) - .flatMapConcat(ConstantFun.scalaIdentityFunction) - HttpEntity.Chunked(mediaType withBoundary boundary withCharset charset, chunks) - } - - /** Java API */ - def getMediaType: jm.MediaType.Multipart = mediaType - - /** Java API */ - def getParts: JSource[_ <: jm.Multipart.BodyPart, AnyRef] = - JSource.fromGraph(parts.asInstanceOf[Source[Multipart.BodyPart, AnyRef]]) - - /** Java API */ - def toStrict(timeoutMillis: Long, materializer: Materializer): CompletionStage[_ <: jm.Multipart.Strict] = - toStrict(FiniteDuration(timeoutMillis, concurrent.duration.MILLISECONDS))(materializer).toJava - - /** Java API */ - def toEntity(charset: jm.HttpCharset, boundary: String): jm.RequestEntity = - toEntity(charset.asInstanceOf[HttpCharset], boundary) -} - -object Multipart { - - /** - * A type of multipart content for which all parts have already been loaded into memory - * and are therefore allow random access. - */ - trait Strict extends Multipart with jm.Multipart.Strict { - - def parts: Source[Multipart.BodyPart.Strict, Any] - - /** - * The parts of this content as a strict collection. - */ - def strictParts: immutable.Seq[Multipart.BodyPart.Strict] - - override def toEntity(charset: HttpCharset, boundary: String)(implicit log: LoggingAdapter = NoLogging): HttpEntity.Strict = { - val data = BodyPartRenderer.strict(strictParts, boundary, charset.nioCharset, partHeadersSizeHint = 128, log) - HttpEntity(mediaType withBoundary boundary withCharset charset, data) - } - - /** Java API */ - override def getParts: JSource[_ <: jm.Multipart.BodyPart.Strict, AnyRef] = - super.getParts.asInstanceOf[JSource[_ <: jm.Multipart.BodyPart.Strict, AnyRef]] - - /** Java API */ - override def getStrictParts: java.lang.Iterable[_ <: jm.Multipart.BodyPart.Strict] = - (strictParts: immutable.Seq[jm.Multipart.BodyPart.Strict]).asJava - - /** Java API */ - override def toEntity(charset: jm.HttpCharset, boundary: String): jm.HttpEntity.Strict = - super.toEntity(charset, boundary).asInstanceOf[jm.HttpEntity.Strict] - } - - /** - * The general model for a single part of a multipart message. - */ - trait BodyPart extends jm.Multipart.BodyPart { - - /** - * The entity of the part. - */ - def entity: BodyPartEntity - - /** - * The headers the part carries. - */ - def headers: immutable.Seq[HttpHeader] - - /** - * The potentially present [[`Content-Disposition`]] header. - */ - def contentDispositionHeader: Option[`Content-Disposition`] = - headers.collectFirst { case x: `Content-Disposition` ⇒ x } - - /** - * The parameters of the potentially present [[`Content-Disposition`]] header. - * Returns an empty map if no such header is present. - */ - def dispositionParams: Map[String, String] = - contentDispositionHeader match { - case Some(`Content-Disposition`(_, params)) ⇒ params - case None ⇒ Map.empty - } - - /** - * The [[akka.http.scaladsl.model.headers.ContentDispositionType]] of the potentially present [[`Content-Disposition`]] header. - */ - def dispositionType: Option[ContentDispositionType] = - contentDispositionHeader.map(_.dispositionType) - - def toStrict(timeout: FiniteDuration)(implicit fm: Materializer): Future[BodyPart.Strict] - - /** Java API */ - def getEntity: jm.BodyPartEntity = entity - - /** Java API */ - def getHeaders: java.lang.Iterable[jm.HttpHeader] = (headers: immutable.Seq[jm.HttpHeader]).asJava - - /** Java API */ - def getContentDispositionHeader: Optional[jm.headers.ContentDisposition] = Util.convertOption(contentDispositionHeader) - - /** Java API */ - def getDispositionParams: java.util.Map[String, String] = dispositionParams.asJava - - /** Java API */ - def getDispositionType: Optional[jm.headers.ContentDispositionType] = Util.convertOption(dispositionType) - - /** Java API */ - def toStrict(timeoutMillis: Long, materializer: Materializer): CompletionStage[_ <: jm.Multipart.BodyPart.Strict] = - toStrict(FiniteDuration(timeoutMillis, concurrent.duration.MILLISECONDS))(materializer).toJava - } - - object BodyPart { - - /** - * A [[BodyPart]] whose entity has already been loaded in its entirety and is therefore - * full and readily available as a [[HttpEntity.Strict]]. - */ - trait Strict extends Multipart.BodyPart with jm.Multipart.BodyPart.Strict { - override def entity: HttpEntity.Strict - - /** Java API */ - override def getEntity: jm.HttpEntity.Strict = entity - } - } - - private def strictify[BP <: Multipart.BodyPart, BPS <: Multipart.BodyPart.Strict](parts: Source[BP, Any])(f: BP ⇒ Future[BPS])(implicit fm: Materializer): Future[Vector[BPS]] = { - import fm.executionContext - parts.mapAsync(Int.MaxValue)(f).runWith(Sink.seq).fast.map(_.toVector) - } - - //////////////////////// CONCRETE multipart types ///////////////////////// - - /** - * Basic model for general multipart content as defined by http://tools.ietf.org/html/rfc2046. - */ - sealed abstract class General extends Multipart with jm.Multipart.General { - def parts: Source[Multipart.General.BodyPart, Any] - - def toStrict(timeout: FiniteDuration)(implicit fm: Materializer): Future[Multipart.General.Strict] = { - import fm.executionContext - strictify(parts)(_.toStrict(timeout)).fast.map(General.Strict(mediaType, _)) - } - - /** Java API */ - override def getParts: JSource[_ <: jm.Multipart.General.BodyPart, AnyRef] = - super.getParts.asInstanceOf[JSource[_ <: jm.Multipart.General.BodyPart, AnyRef]] - - /** Java API */ - override def toStrict(timeoutMillis: Long, materializer: Materializer): CompletionStage[jm.Multipart.General.Strict] = - super.toStrict(timeoutMillis, materializer).toScala.asInstanceOf[Future[jm.Multipart.General.Strict]].toJava - } - object General { - def apply(mediaType: MediaType.Multipart, parts: BodyPart.Strict*): Strict = Strict(mediaType, parts.toVector) - - def apply(_mediaType: MediaType.Multipart, _parts: Source[Multipart.General.BodyPart, Any]): Multipart.General = - new Multipart.General { - def mediaType = _mediaType - def parts = _parts - override def toString = s"General($mediaType, $parts)" - } - - def unapply(value: Multipart.General): Option[(MediaType.Multipart, Source[Multipart.General.BodyPart, Any])] = - Some(value.mediaType → value.parts) - - /** - * Strict [[General]] multipart content. - */ - case class Strict(mediaType: MediaType.Multipart, strictParts: immutable.Seq[Multipart.General.BodyPart.Strict]) - extends Multipart.General with Multipart.Strict with jm.Multipart.General.Strict { - def parts: Source[Multipart.General.BodyPart.Strict, Any] = Source(strictParts) - override def toStrict(timeout: FiniteDuration)(implicit fm: Materializer) = FastFuture.successful(this) - override def productPrefix = "General.Strict" - - /** Java API */ - override def getParts: JSource[jm.Multipart.General.BodyPart.Strict, AnyRef] = - super.getParts.asInstanceOf[JSource[_ <: jm.Multipart.General.BodyPart.Strict, AnyRef]] - - /** Java API */ - override def getStrictParts: java.lang.Iterable[jm.Multipart.General.BodyPart.Strict] = - super.getStrictParts.asInstanceOf[java.lang.Iterable[jm.Multipart.General.BodyPart.Strict]] - } - - /** - * Body part of the [[General]] model. - */ - sealed abstract class BodyPart extends Multipart.BodyPart with jm.Multipart.General.BodyPart { - def toStrict(timeout: FiniteDuration)(implicit fm: Materializer): Future[Multipart.General.BodyPart.Strict] = { - import fm.executionContext - entity.toStrict(timeout).map(BodyPart.Strict(_, headers)) - } - def toFormDataBodyPart: Try[Multipart.FormData.BodyPart] - def toByteRangesBodyPart: Try[Multipart.ByteRanges.BodyPart] - - /** Java API */ - override def toStrict(timeoutMillis: Long, materializer: Materializer): CompletionStage[jm.Multipart.General.BodyPart.Strict] = - super.toStrict(timeoutMillis, materializer).toScala.asInstanceOf[Future[jm.Multipart.General.BodyPart.Strict]].toJava - - private[BodyPart] def tryCreateFormDataBodyPart[T](f: (String, Map[String, String], immutable.Seq[HttpHeader]) ⇒ T): Try[T] = { - val params = dispositionParams - params.get("name") match { - case Some(name) ⇒ Success(f(name, params - "name", headers.filterNot(_ is "content-disposition"))) - case None ⇒ Failure(IllegalHeaderException("multipart/form-data part must contain `Content-Disposition` header with `name` parameter")) - } - } - private[BodyPart] def tryCreateByteRangesBodyPart[T](f: (ContentRange, RangeUnit, immutable.Seq[HttpHeader]) ⇒ T): Try[T] = - headers.collectFirst { case x: `Content-Range` ⇒ x } match { - case Some(`Content-Range`(unit, range)) ⇒ Success(f(range, unit, headers.filterNot(_ is "content-range"))) - case None ⇒ Failure(IllegalHeaderException("multipart/byteranges part must contain `Content-Range` header")) - } - } - object BodyPart { - def apply(_entity: BodyPartEntity, _headers: immutable.Seq[HttpHeader] = Nil): Multipart.General.BodyPart = - new Multipart.General.BodyPart { - def entity = _entity - def headers: immutable.Seq[HttpHeader] = _headers - def toFormDataBodyPart: Try[Multipart.FormData.BodyPart] = - tryCreateFormDataBodyPart(FormData.BodyPart(_, entity, _, _)) - def toByteRangesBodyPart: Try[Multipart.ByteRanges.BodyPart] = - tryCreateByteRangesBodyPart(ByteRanges.BodyPart(_, entity, _, _)) - override def toString = s"General.BodyPart($entity, $headers)" - } - - def unapply(value: BodyPart): Option[(BodyPartEntity, immutable.Seq[HttpHeader])] = Some(value.entity → value.headers) - - /** - * Strict [[General.BodyPart]]. - */ - case class Strict(entity: HttpEntity.Strict, headers: immutable.Seq[HttpHeader] = Nil) - extends BodyPart with Multipart.BodyPart.Strict with jm.Multipart.General.BodyPart.Strict { - override def toStrict(timeout: FiniteDuration)(implicit fm: Materializer): Future[Multipart.General.BodyPart.Strict] = - FastFuture.successful(this) - override def toFormDataBodyPart: Try[Multipart.FormData.BodyPart.Strict] = - tryCreateFormDataBodyPart(FormData.BodyPart.Strict(_, entity, _, _)) - override def toByteRangesBodyPart: Try[Multipart.ByteRanges.BodyPart.Strict] = - tryCreateByteRangesBodyPart(ByteRanges.BodyPart.Strict(_, entity, _, _)) - override def productPrefix = "General.BodyPart.Strict" - } - } - } - - /** - * Model for `multipart/form-data` content as defined in http://tools.ietf.org/html/rfc2388. - * All parts must have distinct names. (This is not verified!) - */ - sealed abstract class FormData extends Multipart with jm.Multipart.FormData { - def mediaType = MediaTypes.`multipart/form-data` - - def parts: Source[Multipart.FormData.BodyPart, Any] - - def toStrict(timeout: FiniteDuration)(implicit fm: Materializer): Future[Multipart.FormData.Strict] = { - import fm.executionContext - strictify(parts)(_.toStrict(timeout)).fast.map(Multipart.FormData.Strict(_)) - } - - /** Java API */ - override def getParts: JSource[_ <: jm.Multipart.FormData.BodyPart, AnyRef] = - super.getParts.asInstanceOf[JSource[_ <: jm.Multipart.FormData.BodyPart, AnyRef]] - - /** Java API */ - override def toStrict(timeoutMillis: Long, materializer: Materializer): CompletionStage[jm.Multipart.FormData.Strict] = - super.toStrict(timeoutMillis, materializer).toScala.asInstanceOf[Future[jm.Multipart.FormData.Strict]].toJava - } - object FormData { - def apply(parts: Multipart.FormData.BodyPart.Strict*): Multipart.FormData.Strict = Strict(parts.toVector) - def apply(parts: Multipart.FormData.BodyPart*): Multipart.FormData = Multipart.FormData(Source(parts.toVector)) - - // FIXME: SI-2991 workaround - two functions below. Remove when (hopefully) this issue is fixed - /** INTERNAL API */ - private[akka] def createStrict(parts: Multipart.FormData.BodyPart.Strict*): Multipart.FormData.Strict = Strict(parts.toVector) - /** INTERNAL API */ - private[akka] def createNonStrict(parts: Multipart.FormData.BodyPart*): Multipart.FormData = Multipart.FormData(Source(parts.toVector)) - /** INTERNAL API */ - private[akka] def createStrict(fields: Map[String, akka.http.javadsl.model.HttpEntity.Strict]): Multipart.FormData.Strict = Multipart.FormData.Strict { - fields.map { case (name, entity: akka.http.scaladsl.model.HttpEntity.Strict) ⇒ Multipart.FormData.BodyPart.Strict(name, entity) }(collection.breakOut) - } - - def apply(fields: Map[String, HttpEntity.Strict]): Multipart.FormData.Strict = Multipart.FormData.Strict { - fields.map { case (name, entity) ⇒ Multipart.FormData.BodyPart.Strict(name, entity) }(collection.breakOut) - } - - def apply(_parts: Source[Multipart.FormData.BodyPart, Any]): Multipart.FormData = - new Multipart.FormData { - def parts = _parts - override def toString = s"FormData($parts)" - } - - /** - * Creates a FormData instance that contains a single part backed by the given file. - * - * To create an instance with several parts or for multiple files, use - * `FormData(BodyPart.fromFile("field1", ...), BodyPart.fromFile("field2", ...)` - */ - @deprecated("Use `fromPath` instead", "2.4.5") - def fromFile(name: String, contentType: ContentType, file: File, chunkSize: Int = -1): Multipart.FormData = - fromPath(name, contentType, file.toPath, chunkSize) - - /** - * Creates a FormData instance that contains a single part backed by the given file. - * - * To create an instance with several parts or for multiple files, use - * `FormData(BodyPart.fromPath("field1", ...), BodyPart.fromPath("field2", ...)` - */ - def fromPath(name: String, contentType: ContentType, file: Path, chunkSize: Int = -1): Multipart.FormData = - Multipart.FormData(Source.single(Multipart.FormData.BodyPart.fromPath(name, contentType, file, chunkSize))) - - /** - * Strict [[FormData]]. - */ - case class Strict(strictParts: immutable.Seq[Multipart.FormData.BodyPart.Strict]) - extends FormData with Multipart.Strict with jm.Multipart.FormData.Strict { - def parts: Source[Multipart.FormData.BodyPart.Strict, Any] = Source(strictParts) - override def toStrict(timeout: FiniteDuration)(implicit fm: Materializer) = FastFuture.successful(this) - override def productPrefix = "FormData.Strict" - - /** Java API */ - override def getParts: JSource[jm.Multipart.FormData.BodyPart.Strict, AnyRef] = - super.getParts.asInstanceOf[JSource[jm.Multipart.FormData.BodyPart.Strict, AnyRef]] - - /** Java API */ - override def getStrictParts: java.lang.Iterable[jm.Multipart.FormData.BodyPart.Strict] = - super.getStrictParts.asInstanceOf[java.lang.Iterable[jm.Multipart.FormData.BodyPart.Strict]] - } - - /** - * Body part of the [[FormData]] model. - */ - sealed abstract class BodyPart extends Multipart.BodyPart with jm.Multipart.FormData.BodyPart { - - /** - * The name of this part. - */ - def name: String - - /** - * The Content-Disposition parameters, not including the `name` parameter. - */ - def additionalDispositionParams: Map[String, String] - - /** - * Part headers, not including the Content-Disposition header. - */ - def additionalHeaders: immutable.Seq[HttpHeader] - - override def headers = contentDispositionHeader.get +: additionalHeaders - override def contentDispositionHeader = Some(`Content-Disposition`(dispositionType.get, dispositionParams)) - override def dispositionParams = additionalDispositionParams.updated("name", name) - override def dispositionType = Some(ContentDispositionTypes.`form-data`) - - /** - * The value of the `filename` Content-Disposition parameter, if available. - */ - def filename: Option[String] = additionalDispositionParams.get("filename") - - def toStrict(timeout: FiniteDuration)(implicit fm: Materializer): Future[Multipart.FormData.BodyPart.Strict] = { - import fm.executionContext - entity.toStrict(timeout).map(Multipart.FormData.BodyPart.Strict(name, _, additionalDispositionParams, additionalHeaders)) - } - - /** Java API */ - def getName: String = name - - /** Java API */ - def getAdditionalDispositionParams: java.util.Map[String, String] = additionalDispositionParams.asJava - - /** Java API */ - def getAdditionalHeaders: java.lang.Iterable[jm.HttpHeader] = - (additionalHeaders: immutable.Seq[jm.HttpHeader]).asJava - - /** Java API */ - def getFilename: Optional[String] = filename.asJava - - /** Java API */ - override def toStrict(timeoutMillis: Long, materializer: Materializer): CompletionStage[jm.Multipart.FormData.BodyPart.Strict] = - super.toStrict(timeoutMillis, materializer).toScala.asInstanceOf[Future[jm.Multipart.FormData.BodyPart.Strict]].toJava - } - object BodyPart { - def apply(_name: String, _entity: BodyPartEntity, - _additionalDispositionParams: Map[String, String] = Map.empty, - _additionalHeaders: immutable.Seq[HttpHeader] = Nil): Multipart.FormData.BodyPart = - new Multipart.FormData.BodyPart { - def name = _name - def additionalDispositionParams = _additionalDispositionParams - def additionalHeaders = _additionalHeaders - def entity = _entity - override def toString = s"FormData.BodyPart($name, $entity, $additionalDispositionParams, $additionalHeaders)" - } - - /** - * Creates a BodyPart backed by a File that will be streamed using a FileSource. - */ - @deprecated("Use `fromPath` instead", since = "2.4.5") - def fromFile(name: String, contentType: ContentType, file: File, chunkSize: Int = -1): BodyPart = - fromPath(name, contentType, file.toPath, chunkSize) - - /** - * Creates a BodyPart backed by a file that will be streamed using a FileSource. - */ - def fromPath(name: String, contentType: ContentType, file: Path, chunkSize: Int = -1): BodyPart = - BodyPart(name, HttpEntity.fromPath(contentType, file, chunkSize), Map("filename" → file.getFileName.toString)) - - def unapply(value: BodyPart): Option[(String, BodyPartEntity, Map[String, String], immutable.Seq[HttpHeader])] = - Some((value.name, value.entity, value.additionalDispositionParams, value.additionalHeaders)) - - /** - * Strict [[FormData.BodyPart]]. - */ - case class Strict(name: String, entity: HttpEntity.Strict, - additionalDispositionParams: Map[String, String] = Map.empty, - additionalHeaders: immutable.Seq[HttpHeader] = Nil) - extends Multipart.FormData.BodyPart with Multipart.BodyPart.Strict with jm.Multipart.FormData.BodyPart.Strict { - override def toStrict(timeout: FiniteDuration)(implicit fm: Materializer): Future[Multipart.FormData.BodyPart.Strict] = - FastFuture.successful(this) - override def productPrefix = "FormData.BodyPart.Strict" - } - - /** INTERNAL API */ - private[akka] object Builder { - def create(_name: String, _entity: BodyPartEntity, - _additionalDispositionParams: Map[String, String], - _additionalHeaders: Iterable[akka.http.javadsl.model.HttpHeader]): Multipart.FormData.BodyPart = { - val _headers = _additionalHeaders.to[immutable.Seq] map { case h: akka.http.scaladsl.model.HttpHeader ⇒ h } - apply(_name, _entity, _additionalDispositionParams, _headers) - } - } - - /** INTERNAL API */ - private[akka] object StrictBuilder { - def createStrict(_name: String, _entity: HttpEntity.Strict, - _additionalDispositionParams: Map[String, String], - _additionalHeaders: Iterable[akka.http.javadsl.model.HttpHeader]): Multipart.FormData.BodyPart.Strict = { - val _headers = _additionalHeaders.to[immutable.Seq] map { case h: akka.http.scaladsl.model.HttpHeader ⇒ h } - Strict(_name, _entity, _additionalDispositionParams, _headers) - } - } - } - } - - /** - * Model for `multipart/byteranges` content as defined by - * https://tools.ietf.org/html/rfc7233#section-5.4.1 and https://tools.ietf.org/html/rfc7233#appendix-A - */ - sealed abstract class ByteRanges extends Multipart with jm.Multipart.ByteRanges { - def mediaType = MediaTypes.`multipart/byteranges` - def parts: Source[Multipart.ByteRanges.BodyPart, Any] - def toStrict(timeout: FiniteDuration)(implicit fm: Materializer): Future[Multipart.ByteRanges.Strict] = { - import fm.executionContext - strictify(parts)(_.toStrict(timeout)).fast.map(ByteRanges.Strict(_)) - } - - /** Java API */ - override def getParts: JSource[_ <: jm.Multipart.ByteRanges.BodyPart, AnyRef] = - super.getParts.asInstanceOf[JSource[_ <: jm.Multipart.ByteRanges.BodyPart, AnyRef]] - - /** Java API */ - override def toStrict(timeoutMillis: Long, materializer: Materializer): CompletionStage[jm.Multipart.ByteRanges.Strict] = - super.toStrict(timeoutMillis, materializer).toScala.asInstanceOf[Future[jm.Multipart.ByteRanges.Strict]].toJava - } - object ByteRanges { - def apply(parts: Multipart.ByteRanges.BodyPart.Strict*): Strict = Strict(parts.toVector) - - def apply(_parts: Source[Multipart.ByteRanges.BodyPart, Any]): Multipart.ByteRanges = - new Multipart.ByteRanges { - def parts = _parts - override def toString = s"ByteRanges($parts)" - } - - /** - * Strict [[ByteRanges]]. - */ - case class Strict(strictParts: immutable.Seq[Multipart.ByteRanges.BodyPart.Strict]) - extends Multipart.ByteRanges with Multipart.Strict with jm.Multipart.ByteRanges.Strict { - def parts: Source[Multipart.ByteRanges.BodyPart.Strict, Any] = Source(strictParts) - override def toStrict(timeout: FiniteDuration)(implicit fm: Materializer) = FastFuture.successful(this) - override def productPrefix = "ByteRanges.Strict" - - /** Java API */ - override def getParts: JSource[jm.Multipart.ByteRanges.BodyPart.Strict, AnyRef] = - super.getParts.asInstanceOf[JSource[jm.Multipart.ByteRanges.BodyPart.Strict, AnyRef]] - - /** Java API */ - override def getStrictParts: java.lang.Iterable[jm.Multipart.ByteRanges.BodyPart.Strict] = - super.getStrictParts.asInstanceOf[java.lang.Iterable[jm.Multipart.ByteRanges.BodyPart.Strict]] - } - - /** - * Body part of the [[ByteRanges]] model. - */ - sealed abstract class BodyPart extends Multipart.BodyPart with jm.Multipart.ByteRanges.BodyPart { - - /** - * The [[ContentRange]] contained in this part. - */ - def contentRange: ContentRange - - /** - * The [[akka.http.scaladsl.model.headers.RangeUnit]] for the `contentRange`. - */ - def rangeUnit: RangeUnit - - /** - * Part headers, not including the Content-Range header. - */ - def additionalHeaders: immutable.Seq[HttpHeader] - - /** - * The `Content-Range` header of this part. - */ - def contentRangeHeader = `Content-Range`(rangeUnit, contentRange) - - override def headers = contentRangeHeader +: additionalHeaders - def toStrict(timeout: FiniteDuration)(implicit fm: Materializer): Future[Multipart.ByteRanges.BodyPart.Strict] = { - import fm.executionContext - entity.toStrict(timeout).map(Multipart.ByteRanges.BodyPart.Strict(contentRange, _, rangeUnit, additionalHeaders)) - } - - /** Java API */ - def getContentRange: jm.ContentRange = contentRange - - /** Java API */ - def getRangeUnit: RangeUnit = rangeUnit - - /** Java API */ - def getAdditionalHeaders: java.lang.Iterable[jm.HttpHeader] = - (additionalHeaders: immutable.Seq[jm.HttpHeader]).asJava - - /** Java API */ - def getContentRangeHeader: jm.headers.ContentRange = contentRangeHeader - - /** Java API */ - override def toStrict(timeoutMillis: Long, materializer: Materializer): CompletionStage[jm.Multipart.ByteRanges.BodyPart.Strict] = - super.toStrict(timeoutMillis, materializer).toScala.asInstanceOf[Future[jm.Multipart.ByteRanges.BodyPart.Strict]].toJava - } - object BodyPart { - def apply(_contentRange: ContentRange, _entity: BodyPartEntity, _rangeUnit: RangeUnit = RangeUnits.Bytes, - _additionalHeaders: immutable.Seq[HttpHeader] = Nil): Multipart.ByteRanges.BodyPart = - new Multipart.ByteRanges.BodyPart { - def contentRange = _contentRange - def entity = _entity - def rangeUnit = _rangeUnit - def additionalHeaders = _additionalHeaders - override def toString = s"ByteRanges.BodyPart($contentRange, $entity, $rangeUnit, $additionalHeaders)" - } - - def unapply(value: Multipart.ByteRanges.BodyPart): Option[(ContentRange, BodyPartEntity, RangeUnit, immutable.Seq[HttpHeader])] = - Some((value.contentRange, value.entity, value.rangeUnit, value.additionalHeaders)) - - /** - * Strict [[ByteRanges.BodyPart]]. - */ - case class Strict(contentRange: ContentRange, entity: HttpEntity.Strict, rangeUnit: RangeUnit = RangeUnits.Bytes, - additionalHeaders: immutable.Seq[HttpHeader] = Nil) - extends Multipart.ByteRanges.BodyPart with Multipart.BodyPart.Strict with jm.Multipart.ByteRanges.BodyPart.Strict { - override def toStrict(timeout: FiniteDuration)(implicit fm: Materializer): Future[Multipart.ByteRanges.BodyPart.Strict] = - FastFuture.successful(this) - override def productPrefix = "ByteRanges.BodyPart.Strict" - } - } - } -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/RemoteAddress.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/RemoteAddress.scala deleted file mode 100644 index 0492d09f3b..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/RemoteAddress.scala +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model - -import java.net.{ InetSocketAddress, UnknownHostException, InetAddress } -import java.util.Optional -import akka.http.impl.model.JavaInitialization -import akka.http.impl.util._ -import akka.http.javadsl.{ model ⇒ jm } -import akka.http.impl.util.JavaMapping.Implicits._ -import akka.util.Unsafe - -sealed abstract class RemoteAddress extends jm.RemoteAddress with ValueRenderable { - def toOption: Option[InetAddress] - def toIP: Option[RemoteAddress.IP] - def isUnknown: Boolean - - /** Java API */ - def getAddress: Optional[InetAddress] = toOption.asJava - - /** Java API */ - def getPort: Int = toIP.flatMap(_.port).getOrElse(0) -} - -object RemoteAddress { - case object Unknown extends RemoteAddress { - def toOption = None - def toIP = None - - def render[R <: Rendering](r: R): r.type = r ~~ "unknown" - - def isUnknown = true - } - - final case class IP(ip: InetAddress, port: Option[Int] = None) extends RemoteAddress { - def toOption: Option[InetAddress] = Some(ip) - def toIP = Some(this) - def render[R <: Rendering](r: R): r.type = { - r ~~ ip.getHostAddress - if (port.isDefined) r ~~ ":" ~~ port.get - - r - } - - def isUnknown = false - } - - def apply(a: InetAddress, port: Option[Int] = None): IP = IP(a, port) - - def apply(a: InetSocketAddress): IP = IP(a.getAddress, Some(a.getPort)) - - def apply(bytes: Array[Byte]): RemoteAddress = { - require(bytes.length == 4 || bytes.length == 16) - try IP(InetAddress.getByAddress(bytes)) catch { case _: UnknownHostException ⇒ Unknown } - } - - JavaInitialization.initializeStaticFieldWith( - Unknown, classOf[jm.RemoteAddress].getField("UNKNOWN")) - -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/StatusCode.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/StatusCode.scala deleted file mode 100644 index 6a0770fa55..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/StatusCode.scala +++ /dev/null @@ -1,172 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model - -import language.implicitConversions -import akka.http.impl.util._ -import akka.http.javadsl.{ model ⇒ jm } - -/** The result status code of an HTTP response. */ -sealed abstract class StatusCode extends jm.StatusCode with LazyValueBytesRenderable { - def intValue: Int - def value: String = intValue.toString + ' ' + reason - def reason: String - def defaultMessage: String - def isSuccess: Boolean - def isFailure: Boolean - def isRedirection: Boolean - def allowsEntity: Boolean -} - -object StatusCode { - import StatusCodes._ - implicit def int2StatusCode(code: Int): StatusCode = - getForKey(code).getOrElse( - throw new RuntimeException( - "Non-standard status codes cannot be created by implicit conversion. Use `StatusCodes.custom` instead.")) -} - -object StatusCodes extends ObjectRegistry[Int, StatusCode] { - sealed protected abstract class HttpSuccess extends StatusCode { - def isSuccess = true - def isFailure = false - } - sealed protected abstract class HttpFailure extends StatusCode { - def isSuccess = false - def isFailure = true - def isRedirection: Boolean = false - - def allowsEntity = true - } - - // format: OFF - final case class Informational private[StatusCodes] (intValue: Int)(val reason: String, - val defaultMessage: String) extends HttpSuccess { - def allowsEntity = false - def isRedirection: Boolean = false - } - final case class Success private[StatusCodes] (intValue: Int)(val reason: String, val defaultMessage: String, - val allowsEntity: Boolean = true) extends HttpSuccess { - def isRedirection: Boolean = false - } - final case class Redirection private[StatusCodes] (intValue: Int)(val reason: String, val defaultMessage: String, - val htmlTemplate: String, val allowsEntity: Boolean = true) extends HttpSuccess { - def isRedirection: Boolean = true - } - final case class ClientError private[StatusCodes] (intValue: Int)(val reason: String, val defaultMessage: String) extends HttpFailure - final case class ServerError private[StatusCodes] (intValue: Int)(val reason: String, val defaultMessage: String) extends HttpFailure - - final case class CustomStatusCode private[StatusCodes] (intValue: Int)( - val reason: String, - val defaultMessage: String, - val isSuccess: Boolean, - val allowsEntity: Boolean) extends StatusCode { - def isFailure: Boolean = !isSuccess - def isRedirection: Boolean = false - } - - private def reg[T <: StatusCode](code: T): T = { - require(getForKey(code.intValue).isEmpty, s"Status code for ${code.intValue} already registered as '${getForKey(code.intValue).get}'.") - - register(code.intValue, code) - } - - /** - * Create a custom status code and allow full customization of behavior. The value of `allowsEntity` - * changes the parser behavior: If it is set to true, a response with this status code is required to include a - * `Content-Length` header to be parsed correctly when keep-alive is enabled (which is the default in HTTP/1.1). - * If `allowsEntity` is false, an entity is never expected. - */ - def custom(intValue: Int, reason: String, defaultMessage: String, isSuccess: Boolean, allowsEntity: Boolean): StatusCode = - StatusCodes.CustomStatusCode(intValue)(reason, defaultMessage, isSuccess, allowsEntity) - - /** Create a custom status code with default behavior for its value region. */ - def custom(intValue: Int, reason: String, defaultMessage: String = ""): StatusCode = - if (100 to 199 contains intValue) Informational(intValue)(reason, defaultMessage) - else if (200 to 299 contains intValue) Success(intValue)(reason, defaultMessage) - else if (300 to 399 contains intValue) Redirection(intValue)(reason, defaultMessage, defaultMessage) - else if (400 to 499 contains intValue) ClientError(intValue)(reason, defaultMessage) - else if (500 to 599 contains intValue) ServerError(intValue)(reason, defaultMessage) - else throw new IllegalArgumentException("Can't register status code in non-standard region") - - import Informational.{apply => i} - import Success .{apply => s} - import Redirection .{apply => r} - import ClientError .{apply => c} - import ServerError .{apply => e} - - // main source: http://en.wikipedia.org/wiki/List_of_HTTP_status_codes - - val Continue = reg(i(100)("Continue", "The server has received the request headers, and the client should proceed to send the request body.")) - val SwitchingProtocols = reg(i(101)("Switching Protocols", "The server is switching protocols, because the client requested the switch.")) - val Processing = reg(i(102)("Processing", "The server is processing the request, but no response is available yet.")) - - val OK = reg(s(200)("OK", "OK")) - val Created = reg(s(201)("Created", "The request has been fulfilled and resulted in a new resource being created.")) - val Accepted = reg(s(202)("Accepted", "The request has been accepted for processing, but the processing has not been completed.")) - val NonAuthoritativeInformation = reg(s(203)("Non-Authoritative Information", "The server successfully processed the request, but is returning information that may be from another source.")) - val NoContent = reg(s(204)("No Content", "The server successfully processed the request and is not returning any content.", allowsEntity = false)) - val ResetContent = reg(s(205)("Reset Content", "The server successfully processed the request, but is not returning any content.")) - val PartialContent = reg(s(206)("Partial Content", "The server is delivering only part of the resource due to a range header sent by the client.")) - val MultiStatus = reg(s(207)("Multi-Status", "The message body that follows is an XML message and can contain a number of separate response codes, depending on how many sub-requests were made.")) - val AlreadyReported = reg(s(208)("Already Reported", "The members of a DAV binding have already been enumerated in a previous reply to this request, and are not being included again.")) - val IMUsed = reg(s(226)("IM Used", "The server has fulfilled a GET request for the resource, and the response is a representation of the result of one or more instance-manipulations applied to the current instance.")) - - val MultipleChoices = reg(r(300)("Multiple Choices", "There are multiple options for the resource that the client may follow.", "There are multiple options for the resource that the client may follow. The preferred one is this URI.")) - val MovedPermanently = reg(r(301)("Moved Permanently", "This and all future requests should be directed to the given URI.", "This and all future requests should be directed to this URI.")) - val Found = reg(r(302)("Found", "The resource was found, but at a different URI.", "The requested resource temporarily resides under this URI.")) - val SeeOther = reg(r(303)("See Other", "The response to the request can be found under another URI using a GET method.", "The response to the request can be found under this URI using a GET method.")) - val NotModified = reg(r(304)("Not Modified", "The resource has not been modified since last requested.", "", allowsEntity = false)) - val UseProxy = reg(r(305)("Use Proxy", "This single request is to be repeated via the proxy given by the Location field.", "This single request is to be repeated via the proxy under this URI.")) - val TemporaryRedirect = reg(r(307)("Temporary Redirect", "The request should be repeated with another URI, but future requests can still use the original URI.", "The request should be repeated with this URI, but future requests can still use the original URI.")) - val PermanentRedirect = reg(r(308)("Permanent Redirect", "The request, and all future requests should be repeated using another URI.", "The request, and all future requests should be repeated using this URI.")) - - val BadRequest = reg(c(400)("Bad Request", "The request contains bad syntax or cannot be fulfilled.")) - val Unauthorized = reg(c(401)("Unauthorized", "Authentication is possible but has failed or not yet been provided.")) - val PaymentRequired = reg(c(402)("Payment Required", "Reserved for future use.")) - val Forbidden = reg(c(403)("Forbidden", "The request was a legal request, but the server is refusing to respond to it.")) - val NotFound = reg(c(404)("Not Found", "The requested resource could not be found but may be available again in the future.")) - val MethodNotAllowed = reg(c(405)("Method Not Allowed", "A request was made of a resource using a request method not supported by that resource;")) - val NotAcceptable = reg(c(406)("Not Acceptable", "The requested resource is only capable of generating content not acceptable according to the Accept headers sent in the request.")) - val ProxyAuthenticationRequired = reg(c(407)("Proxy Authentication Required", "Proxy authentication is required to access the requested resource.")) - val RequestTimeout = reg(c(408)("Request Timeout", "The server timed out waiting for the request.")) - val Conflict = reg(c(409)("Conflict", "The request could not be processed because of conflict in the request, such as an edit conflict.")) - val Gone = reg(c(410)("Gone", "The resource requested is no longer available and will not be available again.")) - val LengthRequired = reg(c(411)("Length Required", "The request did not specify the length of its content, which is required by the requested resource.")) - val PreconditionFailed = reg(c(412)("Precondition Failed", "The server does not meet one of the preconditions that the requester put on the request.")) - val RequestEntityTooLarge = reg(c(413)("Request Entity Too Large", "The request is larger than the server is willing or able to process.")) - val RequestUriTooLong = reg(c(414)("Request-URI Too Long", "The URI provided was too long for the server to process.")) - val UnsupportedMediaType = reg(c(415)("Unsupported Media Type", "The request entity has a media type which the server or resource does not support.")) - val RequestedRangeNotSatisfiable = reg(c(416)("Requested Range Not Satisfiable", "The client has asked for a portion of the file, but the server cannot supply that portion.")) - val ExpectationFailed = reg(c(417)("Expectation Failed", "The server cannot meet the requirements of the Expect request-header field.")) - val EnhanceYourCalm = reg(c(420)("Enhance Your Calm", "You are being rate-limited.")) // Twitter only - val UnprocessableEntity = reg(c(422)("Unprocessable Entity", "The request was well-formed but was unable to be followed due to semantic errors.")) - val Locked = reg(c(423)("Locked", "The resource that is being accessed is locked.")) - val FailedDependency = reg(c(424)("Failed Dependency", "The request failed due to failure of a previous request.")) - val UnorderedCollection = reg(c(425)("Unordered Collection", "The collection is unordered.")) - val UpgradeRequired = reg(c(426)("Upgrade Required", "The client should switch to a different protocol.")) - val PreconditionRequired = reg(c(428)("Precondition Required", "The server requires the request to be conditional.")) - val TooManyRequests = reg(c(429)("Too Many Requests", "The user has sent too many requests in a given amount of time.")) - val RequestHeaderFieldsTooLarge = reg(c(431)("Request Header Fields Too Large", "The server is unwilling to process the request because either an individual header field, or all the header fields collectively, are too large.")) - val RetryWith = reg(c(449)("Retry With", "The request should be retried after doing the appropriate action.")) - val BlockedByParentalControls = reg(c(450)("Blocked by Windows Parental Controls", "Windows Parental Controls are turned on and are blocking access to the given webpage.")) - val UnavailableForLegalReasons = reg(c(451)("Unavailable For Legal Reasons", "Resource access is denied for legal reasons.")) - - val InternalServerError = reg(e(500)("Internal Server Error", "There was an internal server error.")) - val NotImplemented = reg(e(501)("Not Implemented", "The server either does not recognize the request method, or it lacks the ability to fulfill the request.")) - val BadGateway = reg(e(502)("Bad Gateway", "The server was acting as a gateway or proxy and received an invalid response from the upstream server.")) - val ServiceUnavailable = reg(e(503)("Service Unavailable", "The server is currently unavailable (because it is overloaded or down for maintenance).")) - val GatewayTimeout = reg(e(504)("Gateway Timeout", "The server was acting as a gateway or proxy and did not receive a timely response from the upstream server.")) - val HTTPVersionNotSupported = reg(e(505)("HTTP Version Not Supported", "The server does not support the HTTP protocol version used in the request.")) - val VariantAlsoNegotiates = reg(e(506)("Variant Also Negotiates", "Transparent content negotiation for the request, results in a circular reference.")) - val InsufficientStorage = reg(e(507)("Insufficient Storage", "Insufficient storage to complete the request.")) - val LoopDetected = reg(e(508)("Loop Detected", "The server detected an infinite loop while processing the request.")) - val BandwidthLimitExceeded = reg(e(509)("Bandwidth Limit Exceeded", "Bandwidth limit has been exceeded.")) - val NotExtended = reg(e(510)("Not Extended", "Further extensions to the request are required for the server to fulfill it.")) - val NetworkAuthenticationRequired = reg(e(511)("Network Authentication Required", "The client needs to authenticate to gain network access.")) - val NetworkReadTimeout = reg(e(598)("Network read timeout error", "")) - val NetworkConnectTimeout = reg(e(599)("Network connect timeout error", "")) - // format: ON -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/TransferEncoding.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/TransferEncoding.scala deleted file mode 100644 index 0ac7724cec..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/TransferEncoding.scala +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model - -import akka.http.impl.util.{ Rendering, SingletonValueRenderable, Renderable } -import akka.http.javadsl.{ model ⇒ jm } -import akka.http.impl.util.JavaMapping.Implicits._ - -sealed abstract class TransferEncoding extends jm.TransferEncoding with Renderable { - def name: String - def params: Map[String, String] - - def getParams: java.util.Map[String, String] = params.asJava -} - -object TransferEncodings { - protected abstract class Predefined extends TransferEncoding with SingletonValueRenderable { - def name: String = value - def params: Map[String, String] = Map.empty - } - - case object chunked extends Predefined - case object compress extends Predefined - case object deflate extends Predefined - case object gzip extends Predefined - final case class Extension(name: String, params: Map[String, String] = Map.empty) extends TransferEncoding { - def render[R <: Rendering](r: R): r.type = { - r ~~ name - params foreach { case (k, v) ⇒ r ~~ "; " ~~ k ~~ '=' ~~# v } - r - } - } -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/Uri.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/Uri.scala deleted file mode 100644 index 6a91a8151c..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/Uri.scala +++ /dev/null @@ -1,895 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model - -import language.implicitConversions -import java.net.{ Inet4Address, Inet6Address, InetAddress } -import java.lang.{ StringBuilder ⇒ JStringBuilder, Iterable } -import java.nio.charset.Charset -import scala.annotation.tailrec -import scala.collection.{ immutable, mutable, LinearSeqOptimized } -import scala.collection.immutable.LinearSeq -import akka.parboiled2.{ CharUtils, CharPredicate, ParserInput } -import akka.http.javadsl.{ model ⇒ jm } -import akka.http.impl.model.parser.UriParser -import akka.http.impl.model.parser.CharacterClasses._ -import akka.http.impl.util._ -import Uri._ - -/** - * An immutable model of an internet URI as defined by http://tools.ietf.org/html/rfc3986. - * All members of this class represent the *decoded* URI elements (i.e. without percent-encoding). - */ -sealed abstract case class Uri(scheme: String, authority: Authority, path: Path, rawQueryString: Option[String], - fragment: Option[String]) { - - def isAbsolute: Boolean = !isRelative - def isRelative: Boolean = scheme.isEmpty - def isEmpty: Boolean - - /** - * Parses the rawQueryString member into a Query instance. - */ - def query(charset: Charset = UTF8, mode: Uri.ParsingMode = Uri.ParsingMode.Relaxed): Query = rawQueryString match { - case Some(q) ⇒ new UriParser(q, charset, mode).parseQuery() - case None ⇒ Query.Empty - } - - /** - * Returns the query part of the Uri in its decoded form. - */ - def queryString(charset: Charset = UTF8): Option[String] = rawQueryString.map(s ⇒ decode(s, charset)) - - /** - * The effective port of this Uri given the currently set authority and scheme values. - * If the authority has an explicitly set port (i.e. a non-zero port value) then this port - * is the effective port. Otherwise the default port for the current scheme is returned. - */ - def effectivePort: Int = if (authority.port != 0) authority.port else defaultPorts(scheme) - - /** - * Returns a copy of this Uri with the given components. - */ - def copy(scheme: String = scheme, authority: Authority = authority, path: Path = path, - rawQueryString: Option[String] = rawQueryString, fragment: Option[String] = fragment): Uri = - Uri(scheme, authority, path, rawQueryString, fragment) - - /** - * Returns a copy of this Uri with the given scheme. The `scheme` change of the Uri has the following - * effect on the port value: - * - If the Uri has a non-default port for the scheme before the change this port will remain unchanged. - * - If the Uri has the default port for the scheme before the change it will have the default port for - * the '''new''' scheme after the change. - */ - def withScheme(scheme: String): Uri = copy(scheme = scheme) - - /** - * Returns a copy of this Uri with the given authority. - */ - def withAuthority(authority: Authority): Uri = copy(authority = authority) - - /** - * Returns a copy of this Uri with a Authority created using the given host, port and userinfo. - */ - def withAuthority(host: Host, port: Int, userinfo: String = ""): Uri = copy(authority = Authority(host, port, userinfo)) - - /** - * Returns a copy of this Uri with a Authority created using the given host and port. - */ - def withAuthority(host: String, port: Int): Uri = copy(authority = Authority(Host(host), port)) - - /** - * Returns a copy of this Uri with the given host. - */ - def withHost(host: Host): Uri = copy(authority = authority.copy(host = host)) - - /** - * Returns a copy of this Uri with the given host. - */ - def withHost(host: String): Uri = copy(authority = authority.copy(host = Host(host))) - - /** - * Returns a copy of this Uri with the given port. - */ - def withPort(port: Int): Uri = copy(authority = authority.copy(port = port)) - - /** - * Returns a copy of this Uri with the given path. - */ - def withPath(path: Path): Uri = copy(path = path) - - /** - * Returns a copy of this Uri with the given userinfo. - */ - def withUserInfo(userinfo: String): Uri = copy(authority = authority.copy(userinfo = userinfo)) - - /** - * Returns a copy of this Uri with the given query. - */ - def withQuery(query: Query): Uri = copy(rawQueryString = if (query.isEmpty) None else Some(query.toString)) - - /** - * Returns a copy of this Uri with a Query created using the given query string. - */ - def withRawQueryString(rawQuery: String): Uri = copy(rawQueryString = Some(rawQuery)) - - /** - * Returns a copy of this Uri with the given fragment. - */ - def withFragment(fragment: String): Uri = copy(fragment = fragment.toOption) - - /** - * Returns a new absolute Uri that is the result of the resolution process defined by - * http://tools.ietf.org/html/rfc3986#section-5.2.2 - * The given base Uri must be absolute. - */ - def resolvedAgainst(base: Uri): Uri = - resolve(scheme, authority.userinfo, authority.host, authority.port, path, rawQueryString, fragment, base) - - /** - * Converts this URI to an "effective HTTP request URI" as defined by - * http://tools.ietf.org/html/rfc7230#section-5.5 - */ - def toEffectiveHttpRequestUri(hostHeaderHost: Host, hostHeaderPort: Int, securedConnection: Boolean = false, - defaultAuthority: Authority = Authority.Empty): Uri = - effectiveHttpRequestUri(scheme, authority.host, authority.port, path, rawQueryString, fragment, securedConnection, - hostHeaderHost, hostHeaderPort, defaultAuthority) - - /** - * Converts this URI into a relative URI by keeping the path, query and fragment, but dropping the scheme and authority. - */ - def toRelative = - Uri(path = if (path.isEmpty) Uri.Path./ else path, queryString = rawQueryString, fragment = fragment) - - /** - * Converts this URI into an HTTP request target "origin-form" as defined by - * https://tools.ietf.org/html/rfc7230#section-5.3. - * - * Note that the resulting URI instance is not a valid RFC 3986 URI! (As it might - * be a "relative" URI with a part component starting with a double slash.) - */ - def toHttpRequestTargetOriginForm = - create("", Authority.Empty, if (path.isEmpty) Uri.Path./ else path, rawQueryString, None) - - /** - * Drops the fragment from this URI - */ - def withoutFragment = - copy(fragment = None) - - override def toString = UriRendering.UriRenderer.render(new StringRendering, this).get -} - -object Uri { - object Empty extends Uri("", Authority.Empty, Path.Empty, None, None) { - def isEmpty = true - } - - val / : Uri = "/" - - /** - * Parses a valid URI string into a normalized URI reference as defined - * by http://tools.ietf.org/html/rfc3986#section-4.1. - * Percent-encoded octets are UTF-8 decoded. - * Accepts unencoded visible 7-bit ASCII characters in addition to the RFC. - * If the given string is not a valid URI the method throws an `IllegalUriException`. - */ - implicit def apply(input: String): Uri = apply(input: ParserInput, UTF8, Uri.ParsingMode.Relaxed) - - /** - * Parses a valid URI string into a normalized URI reference as defined - * by http://tools.ietf.org/html/rfc3986#section-4.1. - * Percent-encoded octets are decoded using the given charset (where specified by the RFC). - * Accepts unencoded visible 7-bit ASCII characters in addition to the rfc. - * If the given string is not a valid URI the method throws an `IllegalUriException`. - */ - def apply(input: ParserInput): Uri = apply(input, UTF8, Uri.ParsingMode.Relaxed) - - /** - * Parses a valid URI string into a normalized URI reference as defined - * by http://tools.ietf.org/html/rfc3986#section-4.1. - * Percent-encoded octets are decoded using the given charset (where specified by the RFC). - * If strict is `false`, accepts unencoded visible 7-bit ASCII characters in addition to the RFC. - * If the given string is not a valid URI the method throws an `IllegalUriException`. - */ - def apply(input: ParserInput, mode: Uri.ParsingMode): Uri = apply(input, UTF8, mode) - - /** - * Parses a valid URI string into a normalized URI reference as defined - * by http://tools.ietf.org/html/rfc3986#section-4.1. - * Percent-encoded octets are decoded using the given charset (where specified by the RFC). - * If strict is `false`, accepts unencoded visible 7-bit ASCII characters in addition to the RFC. - * If the given string is not a valid URI the method throws an `IllegalUriException`. - */ - def apply(input: ParserInput, charset: Charset, mode: Uri.ParsingMode): Uri = - new UriParser(input, charset, mode).parseUriReference() - - /** - * Creates a new Uri instance from the given components. - * All components are verified and normalized except the authority which is kept as provided. - * If the given combination of components does not constitute a valid URI as defined by - * http://tools.ietf.org/html/rfc3986 the method throws an `IllegalUriException`. - */ - def apply(scheme: String = "", authority: Authority = Authority.Empty, path: Path = Path.Empty, - queryString: Option[String] = None, fragment: Option[String] = None): Uri = { - val p = verifyPath(path, scheme, authority.host) - create( - scheme = normalizeScheme(scheme), - authority = authority, - path = if (scheme.isEmpty) p else collapseDotSegments(p), - queryString = queryString, - fragment = fragment) - } - - /** - * Creates a new Uri instance from the given components. - * All components are verified and normalized. - * If the given combination of components does not constitute a valid URI as defined by - * http://tools.ietf.org/html/rfc3986 the method throws an `IllegalUriException`. - */ - def from(scheme: String = "", userinfo: String = "", host: String = "", port: Int = 0, path: String = "", - queryString: Option[String] = None, fragment: Option[String] = None, - mode: Uri.ParsingMode = Uri.ParsingMode.Relaxed): Uri = - apply(scheme, Authority(Host(host, UTF8, mode), normalizePort(port, scheme), userinfo), Path(path), queryString, fragment) - - /** - * Parses a string into a normalized absolute URI as defined by http://tools.ietf.org/html/rfc3986#section-4.3. - * Percent-encoded octets are decoded using the given charset (where specified by the RFC). - * If strict is `false`, accepts unencoded visible 7-bit ASCII characters in addition to the RFC. - * If the given string is not a valid URI the method throws an `IllegalUriException`. - */ - def parseAbsolute(input: ParserInput, charset: Charset = UTF8, mode: Uri.ParsingMode = Uri.ParsingMode.Relaxed): Uri = - new UriParser(input, charset, mode).parseAbsoluteUri() - - /** - * Parses a string into a normalized URI reference that is immediately resolved against the given base URI as - * defined by http://tools.ietf.org/html/rfc3986#section-5.2. - * Note that the given base Uri must be absolute (i.e. define a scheme). - * Percent-encoded octets are decoded using the given charset (where specified by the RFC). - * If strict is `false`, accepts unencoded visible 7-bit ASCII characters in addition to the RFC. - * If the given string is not a valid URI the method throws an `IllegalUriException`. - */ - def parseAndResolve(string: ParserInput, base: Uri, charset: Charset = UTF8, - mode: Uri.ParsingMode = Uri.ParsingMode.Relaxed): Uri = - new UriParser(string, charset, mode).parseAndResolveUriReference(base) - - /** - * Parses the given string into an HTTP request target URI as defined by - * http://tools.ietf.org/html/rfc7230#section-5.3. - * If strict is `false`, accepts unencoded visible 7-bit ASCII characters in addition to the RFC. - * If the given string is not a valid URI the method throws an `IllegalUriException`. - */ - def parseHttpRequestTarget(requestTarget: ParserInput, charset: Charset = UTF8, - mode: Uri.ParsingMode = Uri.ParsingMode.Relaxed): Uri = - new UriParser(requestTarget, charset, mode).parseHttpRequestTarget() - - /** - * Normalizes the given URI string by performing the following normalizations: - * - the `scheme` and `host` components are converted to lowercase - * - a potentially existing `port` component is removed if it matches one of the defined default ports for the scheme - * - percent-encoded octets are decoded if allowed, otherwise they are converted to uppercase hex notation - * - `.` and `..` path segments are resolved as far as possible - * - * If strict is `false`, accepts unencoded visible 7-bit ASCII characters in addition to the RFC. - * If the given string is not a valid URI the method throws an `IllegalUriException`. - */ - def normalize(uri: ParserInput, charset: Charset = UTF8, mode: Uri.ParsingMode = Uri.ParsingMode.Relaxed): String = { - val parsed = apply(uri, charset, mode) - val normalized = parsed.copy(authority = parsed.authority.normalizedFor(parsed.scheme)) - UriRendering.renderUri(new StringRendering, normalized, charset).get - } - - /** - * Converts a set of URI components to an "effective HTTP request URI" as defined by - * http://tools.ietf.org/html/rfc7230#section-5.5. - */ - def effectiveHttpRequestUri(scheme: String, host: Host, port: Int, path: Path, query: Option[String], fragment: Option[String], - securedConnection: Boolean, hostHeaderHost: Host, hostHeaderPort: Int, - defaultAuthority: Authority = Authority.Empty): Uri = { - var _scheme = scheme - var _host = host - var _port = port - if (_scheme.isEmpty) { - _scheme = httpScheme(securedConnection) - if (_host.isEmpty) { - if (hostHeaderHost.isEmpty) { - _host = defaultAuthority.host - _port = defaultAuthority.port - } else { - _host = hostHeaderHost - _port = hostHeaderPort - } - } - } - create(_scheme, "", _host, _port, collapseDotSegments(path), query, fragment) - } - - def httpScheme(securedConnection: Boolean = false) = if (securedConnection) "https" else "http" - - /** - * @param port A port number that may be `0` to signal the default port of for scheme. - * In general what you want is not the value of this field but [[Uri.effectivePort]]. - */ - final case class Authority(host: Host, port: Int = 0, userinfo: String = "") { - def isEmpty = equals(Authority.Empty) - def nonEmpty = !isEmpty - def normalizedForHttp(encrypted: Boolean = false) = - normalizedFor(httpScheme(encrypted)) - def normalizedFor(scheme: String): Authority = { - val normalizedPort = normalizePort(port, scheme) - if (normalizedPort == port) this else copy(port = normalizedPort) - } - override def toString = UriRendering.AuthorityRenderer.render(new StringRendering, this).get - } - object Authority { - val Empty = Authority(Host.Empty) - } - - sealed abstract class Host extends jm.Host { - def address: String - def isEmpty: Boolean - def toOption: Option[NonEmptyHost] - def inetAddresses: immutable.Seq[InetAddress] - - def equalsIgnoreCase(other: Host): Boolean - override def toString = UriRendering.HostRenderer.render(new StringRendering, this).get - - // default implementations - def isNamedHost: Boolean = false - def isIPv6: Boolean = false - def isIPv4: Boolean = false - - /** Java API */ - def getInetAddresses: Iterable[InetAddress] = { - import akka.http.impl.util.JavaMapping.Implicits._ - inetAddresses.asJava - } - } - object Host { - case object Empty extends Host { - def address: String = "" - def isEmpty = true - def toOption = None - def inetAddresses: immutable.Seq[InetAddress] = Nil - - def equalsIgnoreCase(other: Host): Boolean = other eq this - } - def apply(string: String, charset: Charset = UTF8, mode: Uri.ParsingMode = Uri.ParsingMode.Relaxed): Host = - if (!string.isEmpty) new UriParser(string, UTF8, mode).parseHost() else Empty - - def apply(address: InetAddress): Host = address match { - case ipv4: Inet4Address ⇒ apply(ipv4) - case ipv6: Inet6Address ⇒ apply(ipv6) - case _ ⇒ throw new IllegalArgumentException(s"Unexpected address type(${address.getClass.getSimpleName}): $address") - } - def apply(address: Inet4Address): IPv4Host = IPv4Host(address.getAddress, address.getHostAddress) - def apply(address: Inet6Address): IPv6Host = IPv6Host(address.getAddress, address.getHostAddress) - } - - sealed abstract class NonEmptyHost extends Host { - def isEmpty = false - def toOption = Some(this) - } - final case class IPv4Host private[http] (bytes: immutable.Seq[Byte], address: String) extends NonEmptyHost { - require(bytes.length == 4, "bytes array must have length 4") - require(!address.isEmpty, "address must not be empty") - def equalsIgnoreCase(other: Host): Boolean = other match { - case IPv4Host(`bytes`, _) ⇒ true - case _ ⇒ false - } - - override def isIPv4: Boolean = true - def inetAddresses = immutable.Seq(InetAddress.getByAddress(bytes.toArray)) - } - object IPv4Host { - def apply(address: String): IPv4Host = apply(address.split('.').map(_.toInt.toByte)) - def apply(byte1: Byte, byte2: Byte, byte3: Byte, byte4: Byte): IPv4Host = apply(Array(byte1, byte2, byte3, byte4)) - def apply(bytes: Array[Byte]): IPv4Host = apply(bytes, bytes.map(_ & 0xFF).mkString(".")) - - private[http] def apply(bytes: Array[Byte], address: String): IPv4Host = IPv4Host(immutable.Seq(bytes: _*), address) - } - final case class IPv6Host private (bytes: immutable.Seq[Byte], address: String) extends NonEmptyHost { - require(bytes.length == 16, "bytes array must have length 16") - require(!address.isEmpty, "address must not be empty") - def equalsIgnoreCase(other: Host): Boolean = other match { - case IPv6Host(`bytes`, _) ⇒ true - case _ ⇒ false - } - - override def isIPv6: Boolean = true - def inetAddresses = immutable.Seq(InetAddress.getByAddress(bytes.toArray)) - } - object IPv6Host { - def apply(bytes: Array[Byte]): IPv6Host = Host(InetAddress.getByAddress(bytes).asInstanceOf[Inet6Address]) - def apply(bytes: immutable.Seq[Byte]): IPv6Host = apply(bytes.toArray) - - private[http] def apply(bytes: String, address: String): IPv6Host = { - import CharUtils.{ hexValue ⇒ hex } - require(bytes.length == 32, "`bytes` must be a 32 character hex string") - apply(bytes.toCharArray.grouped(2).map(s ⇒ (hex(s(0)) * 16 + hex(s(1))).toByte).toArray, address) - } - private[http] def apply(bytes: Array[Byte], address: String): IPv6Host = apply(immutable.Seq(bytes: _*), address) - } - final case class NamedHost(address: String) extends NonEmptyHost { - def equalsIgnoreCase(other: Host): Boolean = other match { - case NamedHost(otherAddress) ⇒ address equalsIgnoreCase otherAddress - case _ ⇒ false - } - - override def isNamedHost: Boolean = true - def inetAddresses = InetAddress.getAllByName(address).toList - } - - sealed abstract class Path { - type Head - def isEmpty: Boolean - def startsWithSlash: Boolean - def startsWithSegment: Boolean - def endsWithSlash: Boolean = { - import Path.{ Empty ⇒ PEmpty, _ } - @tailrec def check(path: Path): Boolean = path match { - case PEmpty ⇒ false - case Slash(PEmpty) ⇒ true - case Slash(tail) ⇒ check(tail) - case Segment(head, tail) ⇒ check(tail) - } - check(this) - } - def head: Head - def tail: Path - def length: Int - def charCount: Int // count of decoded (!) chars, i.e. the ones contained directly in this high-level model - def ::(c: Char): Path = { require(c == '/'); Path.Slash(this) } - def ::(segment: String): Path - def +(pathString: String): Path = this ++ Path(pathString) - def ++(suffix: Path): Path - def reverse: Path = reverseAndPrependTo(Path.Empty) - def reverseAndPrependTo(prefix: Path): Path - def /(segment: String): Path = this ++ Path.Slash(segment :: Path.Empty) - def startsWith(that: Path): Boolean - def dropChars(count: Int): Path - override def toString = UriRendering.PathRenderer.render(new StringRendering, this).get - } - object Path { - val SingleSlash = Slash(Empty) - def / : Path = SingleSlash - def /(path: Path): Path = Slash(path) - def /(segment: String): Path = Slash(segment :: Empty) - def apply(string: String, charset: Charset = UTF8): Path = { - @tailrec def build(path: Path = Empty, ix: Int = string.length - 1, segmentEnd: Int = 0): Path = - if (ix >= 0) - if (string.charAt(ix) == '/') - if (segmentEnd == 0) build(Slash(path), ix - 1) - else build(Slash(decode(string.substring(ix + 1, segmentEnd), charset) :: path), ix - 1) - else if (segmentEnd == 0) build(path, ix - 1, ix + 1) - else build(path, ix - 1, segmentEnd) - else if (segmentEnd == 0) path else decode(string.substring(0, segmentEnd), charset) :: path - build() - } - def unapply(path: Path): Option[String] = Some(path.toString) - def unapply(uri: Uri): Option[String] = unapply(uri.path) - sealed abstract class SlashOrEmpty extends Path { - def startsWithSegment = false - } - case object Empty extends SlashOrEmpty { - type Head = Nothing - def isEmpty = true - def startsWithSlash = false - def head: Head = throw new NoSuchElementException("head of empty path") - def tail: Path = throw new UnsupportedOperationException("tail of empty path") - def length = 0 - def charCount = 0 - def ::(segment: String) = if (segment.isEmpty) this else Segment(segment, this) - def ++(suffix: Path) = suffix - def reverseAndPrependTo(prefix: Path) = prefix - def startsWith(that: Path): Boolean = that.isEmpty - def dropChars(count: Int) = this - } - final case class Slash(tail: Path) extends SlashOrEmpty { - type Head = Char - def head = '/' - def startsWithSlash = true - def isEmpty = false - def length: Int = tail.length + 1 - def charCount: Int = tail.charCount + 1 - def ::(segment: String) = if (segment.isEmpty) this else Segment(segment, this) - def ++(suffix: Path) = Slash(tail ++ suffix) - def reverseAndPrependTo(prefix: Path) = tail.reverseAndPrependTo(Slash(prefix)) - def startsWith(that: Path): Boolean = that.isEmpty || that.startsWithSlash && tail.startsWith(that.tail) - def dropChars(count: Int): Path = if (count < 1) this else tail.dropChars(count - 1) - } - final case class Segment(head: String, tail: SlashOrEmpty) extends Path { - if (head.isEmpty) throw new IllegalArgumentException("Path segment must not be empty") - type Head = String - def isEmpty = false - def startsWithSlash = false - def startsWithSegment = true - def length: Int = tail.length + 1 - def charCount: Int = head.length + tail.charCount - def ::(segment: String) = if (segment.isEmpty) this else Segment(segment + head, tail) - def ++(suffix: Path) = head :: (tail ++ suffix) - def reverseAndPrependTo(prefix: Path): Path = tail.reverseAndPrependTo(head :: prefix) - def startsWith(that: Path): Boolean = that match { - case Segment(`head`, t) ⇒ tail.startsWith(t) - case Segment(h, Empty) ⇒ head.startsWith(h) - case x ⇒ x.isEmpty - } - def dropChars(count: Int): Path = - if (count < 1) this - else if (count >= head.length) tail.dropChars(count - head.length) - else head.substring(count) :: tail - } - object ~ { - def unapply(cons: Segment): Option[(String, Path)] = Some((cons.head, cons.tail)) - def unapply(cons: Slash): Option[(Char, Path)] = Some(('/', cons.tail)) - } - } - - sealed abstract class Query extends LinearSeq[(String, String)] with LinearSeqOptimized[(String, String), Query] { - def key: String - def value: String - def +:(kvp: (String, String)) = Query.Cons(kvp._1, kvp._2, this) - def get(key: String): Option[String] = { - @tailrec def g(q: Query): Option[String] = if (q.isEmpty) None else if (q.key == key) Some(q.value) else g(q.tail) - g(this) - } - def getOrElse(key: String, default: ⇒ String): String = { - @tailrec def g(q: Query): String = if (q.isEmpty) default else if (q.key == key) q.value else g(q.tail) - g(this) - } - def getAll(key: String): List[String] = { - @tailrec def fetch(q: Query, result: List[String] = Nil): List[String] = - if (q.isEmpty) result else fetch(q.tail, if (q.key == key) q.value :: result else result) - fetch(this) - } - def toMap: Map[String, String] = { - @tailrec def append(map: Map[String, String], q: Query): Map[String, String] = - if (q.isEmpty) map else append(map.updated(q.key, q.value), q.tail) - append(Map.empty, this) - } - def toMultiMap: Map[String, List[String]] = { - @tailrec def append(map: Map[String, List[String]], q: Query): Map[String, List[String]] = - if (q.isEmpty) map else append(map.updated(q.key, q.value :: map.getOrElse(q.key, Nil)), q.tail) - append(Map.empty, this) - } - override def newBuilder: mutable.Builder[(String, String), Query] = Query.newBuilder - override def toString = UriRendering.QueryRenderer.render(new StringRendering, this).get - } - object Query { - /** A special empty String value which will be rendered without a '=' after the key. */ - val EmptyValue: String = new String(Array.empty[Char]) - - /** - * Parses the given String into a Query instance. - * Note that this method will never return Query.Empty, even for the empty String. - * Empty strings will be parsed to `("", "") +: Query.Empty` - * If you want to allow for Query.Empty creation use the apply overload taking an `Option[String]`. - */ - def apply(string: String): Query = apply(string: ParserInput, UTF8, Uri.ParsingMode.Relaxed) - def apply(input: ParserInput, charset: Charset = UTF8, mode: Uri.ParsingMode = Uri.ParsingMode.Relaxed): Query = - new UriParser(input, charset, mode).parseQuery() - def apply(input: Option[String]): Query = apply(input, UTF8, Uri.ParsingMode.Relaxed) - def apply(input: Option[String], charset: Charset, mode: Uri.ParsingMode): Query = input match { - case None ⇒ Query.Empty - case Some(string) ⇒ apply(string, charset, mode) - } - def apply(params: (String, String)*): Query = - params.foldRight(Query.Empty: Query) { case ((key, value), acc) ⇒ Cons(key, value, acc) } - def apply(params: Map[String, String]): Query = apply(params.toSeq: _*) - - def newBuilder: mutable.Builder[(String, String), Query] = new mutable.Builder[(String, String), Query] { - val b = mutable.ArrayBuffer.newBuilder[(String, String)] - def +=(elem: (String, String)): this.type = { b += elem; this } - def clear() = b.clear() - def result() = apply(b.result(): _*) - } - - case object Empty extends Query { - def key = throw new NoSuchElementException("key of empty path") - def value = throw new NoSuchElementException("value of empty path") - override def isEmpty = true - override def head = throw new NoSuchElementException("head of empty list") - override def tail = throw new UnsupportedOperationException("tail of empty query") - } - final case class Cons(key: String, value: String, override val tail: Query) extends Query { - override def isEmpty = false - override def head = (key, value) - } - } - - private val defaultPorts: Map[String, Int] = - Map("ftp" → 21, "ssh" → 22, "telnet" → 23, "smtp" → 25, "domain" → 53, "tftp" → 69, "http" → 80, "ws" → 80, - "pop3" → 110, "nntp" → 119, "imap" → 143, "snmp" → 161, "ldap" → 389, "https" → 443, "wss" → 443, "imaps" → 993, - "nfs" → 2049).withDefaultValue(-1) - - sealed trait ParsingMode extends akka.http.javadsl.model.Uri.ParsingMode - object ParsingMode { - case object Strict extends ParsingMode - case object Relaxed extends ParsingMode - - def apply(string: String): ParsingMode = - string match { - case "strict" ⇒ Strict - case "relaxed" ⇒ Relaxed - case x ⇒ throw new IllegalArgumentException(x + " is not a legal UriParsingMode") - } - } - - // http://tools.ietf.org/html/rfc3986#section-5.2.2 - private[http] def resolve(scheme: String, userinfo: String, host: Host, port: Int, path: Path, query: Option[String], - fragment: Option[String], base: Uri): Uri = { - require(base.isAbsolute, "Resolution base Uri must be absolute") - if (scheme.isEmpty) - if (host.isEmpty) - if (path.isEmpty) { - val q = if (query.isEmpty) base.rawQueryString else query - create(base.scheme, base.authority, base.path, q, fragment) - } else { - // http://tools.ietf.org/html/rfc3986#section-5.2.3 - def mergePaths(base: Uri, path: Path): Path = - if (!base.authority.isEmpty && base.path.isEmpty) Path.Slash(path) - else { - import Path._ - def replaceLastSegment(p: Path, replacement: Path): Path = p match { - case Path.Empty | Segment(_, Path.Empty) ⇒ replacement - case Segment(string, tail) ⇒ string :: replaceLastSegment(tail, replacement) - case Slash(tail) ⇒ Slash(replaceLastSegment(tail, replacement)) - } - replaceLastSegment(base.path, path) - } - val p = if (path.startsWithSlash) path else mergePaths(base, path) - create(base.scheme, base.authority, collapseDotSegments(p), query, fragment) - } - else create(base.scheme, userinfo, host, port, collapseDotSegments(path), query, fragment) - else create(scheme, userinfo, host, port, collapseDotSegments(path), query, fragment) - } - - private[http] def decode(string: String, charset: Charset): String = { - val ix = string.indexOf('%') - if (ix >= 0) decode(string, charset, ix)() else string - } - - @tailrec - private[http] def decode(string: String, charset: Charset, ix: Int)(sb: JStringBuilder = new JStringBuilder(string.length).append(string, 0, ix)): String = - if (ix < string.length) string.charAt(ix) match { - case '%' ⇒ - def intValueOfHexWord(i: Int) = { - def intValueOfHexChar(j: Int) = { - val c = string.charAt(j) - if (HEXDIG(c)) CharUtils.hexValue(c) - else throw new IllegalArgumentException("Illegal percent-encoding at pos " + j) - } - intValueOfHexChar(i) * 16 + intValueOfHexChar(i + 1) - } - - var lastPercentSignIndexPlus3 = ix + 3 - while (lastPercentSignIndexPlus3 < string.length && string.charAt(lastPercentSignIndexPlus3) == '%') - lastPercentSignIndexPlus3 += 3 - val bytesCount = (lastPercentSignIndexPlus3 - ix) / 3 - val bytes = new Array[Byte](bytesCount) - - @tailrec def decodeBytes(i: Int = 0, oredBytes: Int = 0): Int = - if (i < bytesCount) { - val byte = intValueOfHexWord(ix + 3 * i + 1) - bytes(i) = byte.toByte - decodeBytes(i + 1, oredBytes | byte) - } else oredBytes - - // if we have only ASCII chars and the charset is ASCII compatible we don't need to involve it in decoding - if (((decodeBytes() >> 7) == 0) && UriRendering.isAsciiCompatible(charset)) { - @tailrec def appendBytes(i: Int = 0): Unit = - if (i < bytesCount) { sb.append(bytes(i).toChar); appendBytes(i + 1) } - appendBytes() - } else sb.append(new String(bytes, charset)) - decode(string, charset, lastPercentSignIndexPlus3)(sb) - - case x ⇒ decode(string, charset, ix + 1)(sb.append(x)) - } - else sb.toString - - private[http] def normalizeScheme(scheme: String): String = { - @tailrec def verify(ix: Int = scheme.length - 1, allowed: CharPredicate = ALPHA, allLower: Boolean = true): Int = - if (ix >= 0) { - val c = scheme.charAt(ix) - if (allowed(c)) verify(ix - 1, `scheme-char`, allLower && !UPPER_ALPHA(c)) else ix - } else if (allLower) -1 else -2 - verify() match { - case -2 ⇒ scheme.toLowerCase - case -1 ⇒ scheme - case ix ⇒ fail(s"Invalid URI scheme, unexpected character at pos $ix ('${scheme charAt ix}')") - } - } - - private[http] def normalizePort(port: Int, scheme: String): Int = - if ((port >> 16) == 0) - if (port != 0 && defaultPorts(scheme) == port) 0 else port - else fail("Invalid port " + port) - - private[http] def verifyPath(path: Path, scheme: String, host: Host): Path = { - if (host.isEmpty) { - if (path.startsWithSlash && path.tail.startsWithSlash) - fail("""The path of an URI without authority must not begin with "//"""") - } else if (path.startsWithSegment) - fail("The path of an URI containing an authority must either be empty or start with a '/' (slash) character") - path - } - - private[http] def collapseDotSegments(path: Path): Path = { - @tailrec def hasDotOrDotDotSegment(p: Path): Boolean = p match { - case Path.Empty ⇒ false - case Path.Segment(".", _) | Path.Segment("..", _) ⇒ true - case _ ⇒ hasDotOrDotDotSegment(p.tail) - } - // http://tools.ietf.org/html/rfc3986#section-5.2.4 - @tailrec def process(input: Path, output: Path = Path.Empty): Path = { - import Path._ - input match { - case Path.Empty ⇒ output.reverse - case Segment("." | "..", Slash(tail)) ⇒ process(tail, output) - case Slash(Segment(".", tail)) ⇒ process(if (tail.isEmpty) Path./ else tail, output) - case Slash(Segment("..", tail)) ⇒ process( - input = if (tail.isEmpty) Path./ else tail, - output = - if (output.startsWithSegment) - if (output.tail.startsWithSlash) output.tail.tail else tail - else output) - case Segment("." | "..", tail) ⇒ process(tail, output) - case Slash(tail) ⇒ process(tail, Slash(output)) - case Segment(string, tail) ⇒ process(tail, string :: output) - } - } - if (hasDotOrDotDotSegment(path)) process(path) else path - } - - private[http] def fail(summary: String, detail: String = "") = throw IllegalUriException(summary, detail) - - private[http] def create(scheme: String, userinfo: String, host: Host, port: Int, path: Path, queryString: Option[String], - fragment: Option[String]): Uri = - create(scheme, Authority(host, port, userinfo), path, queryString, fragment) - - private[http] def create(scheme: String, authority: Authority, path: Path, queryString: Option[String], - fragment: Option[String]): Uri = - if (path.isEmpty && scheme.isEmpty && authority.isEmpty && queryString.isEmpty && fragment.isEmpty) Empty - else new Uri(scheme, authority, path, queryString, fragment) { def isEmpty = false } -} - -object UriRendering { - implicit object HostRenderer extends Renderer[Host] { - def render[R <: Rendering](r: R, value: Host): r.type = value match { - case Host.Empty ⇒ r - case IPv4Host(_, address) ⇒ r ~~ address - case IPv6Host(_, address) ⇒ r ~~ '[' ~~ address ~~ ']' - case NamedHost(address) ⇒ encode(r, address, UTF8, `reg-name-char`) - } - } - implicit object AuthorityRenderer extends Renderer[Authority] { - def render[R <: Rendering](r: R, value: Authority): r.type = renderAuthority(r, value, "", UTF8) - } - implicit object PathRenderer extends Renderer[Path] { - def render[R <: Rendering](r: R, value: Path): r.type = renderPath(r, value, UTF8) - } - implicit object QueryRenderer extends Renderer[Query] { - def render[R <: Rendering](r: R, value: Query): r.type = renderQuery(r, value, UTF8) - } - implicit object UriRenderer extends Renderer[Uri] { - def render[R <: Rendering](r: R, value: Uri): r.type = renderUri(r, value, UTF8) - } - - /** - * Renders this Uri into the given Renderer as defined by http://tools.ietf.org/html/rfc3986. - * All Uri components are encoded and joined as required by the spec. The given charset is used to - * produce percent-encoded representations of potentially existing non-ASCII characters in the - * different components. - */ - def renderUri[R <: Rendering](r: R, value: Uri, charset: Charset): r.type = { - renderUriWithoutFragment(r, value, charset) - if (value.fragment.isDefined) encode(r ~~ '#', value.fragment.get, charset, `query-fragment-char`) - r - } - - /** - * Renders this Uri (without the fragment component) into the given Renderer as defined by - * http://tools.ietf.org/html/rfc3986. - * All Uri components are encoded and joined as required by the spec. The given charset is used to - * produce percent-encoded representations of potentially existing non-ASCII characters in the - * different components. - */ - def renderUriWithoutFragment[R <: Rendering](r: R, value: Uri, charset: Charset): r.type = { - import value._ - if (isAbsolute) r ~~ scheme ~~ ':' - renderAuthority(r, authority, path, scheme, charset) - renderPath(r, path, charset, encodeFirstSegmentColons = isRelative) - rawQueryString.foreach(r ~~ '?' ~~ _) - r - } - - def renderAuthority[R <: Rendering](r: R, authority: Authority, scheme: String, charset: Charset): r.type = - renderAuthority(r, authority, Path.Empty, scheme, charset) - - def renderAuthority[R <: Rendering](r: R, authority: Authority, path: Path, scheme: String, charset: Charset): r.type = - if (authority.nonEmpty) { - import authority._ - r ~~ '/' ~~ '/' - if (!userinfo.isEmpty) encode(r, userinfo, charset, `userinfo-char`) ~~ '@' - r ~~ host - if (port != 0) r ~~ ':' ~~ port else r - } else scheme match { - case "" | "mailto" ⇒ r - case _ ⇒ if (path.isEmpty || path.startsWithSlash) r ~~ '/' ~~ '/' else r - } - - def renderPath[R <: Rendering](r: R, path: Path, charset: Charset, encodeFirstSegmentColons: Boolean = false): r.type = - path match { - case Path.Empty ⇒ r - case Path.Slash(tail) ⇒ renderPath(r ~~ '/', tail, charset) - case Path.Segment(head, tail) ⇒ - val keep = if (encodeFirstSegmentColons) `pchar-base-nc` else `pchar-base` - renderPath(encode(r, head, charset, keep), tail, charset) - } - - def renderQuery[R <: Rendering](r: R, query: Query, charset: Charset, - keep: CharPredicate = `strict-query-char-np`): r.type = { - def enc(s: String): Unit = encode(r, s, charset, keep, replaceSpaces = true) - @tailrec def append(q: Query): r.type = - q match { - case Query.Empty ⇒ r - case Query.Cons(key, value, tail) ⇒ - if (q ne query) r ~~ '&' - enc(key) - if (value ne Query.EmptyValue) r ~~ '=' - enc(value) - append(tail) - } - append(query) - } - - private[http] def encode(r: Rendering, string: String, charset: Charset, keep: CharPredicate, - replaceSpaces: Boolean = false): r.type = { - val asciiCompatible = isAsciiCompatible(charset) - @tailrec def rec(ix: Int): r.type = { - def appendEncoded(byte: Byte): Unit = r ~~ '%' ~~ CharUtils.upperHexDigit(byte >>> 4) ~~ CharUtils.upperHexDigit(byte) - if (ix < string.length) { - val charSize = string.charAt(ix) match { - case c if keep(c) ⇒ { r ~~ c; 1 } - case ' ' if replaceSpaces ⇒ { r ~~ '+'; 1 } - case c if c <= 127 && asciiCompatible ⇒ { appendEncoded(c.toByte); 1 } - case c ⇒ - def append(s: String) = s.getBytes(charset).foreach(appendEncoded) - if (Character.isHighSurrogate(c)) { append(new String(Array(string codePointAt ix), 0, 1)); 2 } - else { append(c.toString); 1 } - } - rec(ix + charSize) - } else r - } - rec(0) - } - - private[http] def isAsciiCompatible(cs: Charset) = cs == UTF8 || cs == ISO88591 || cs == ASCII -} - -/** - * INTERNAL API. - */ -abstract class UriJavaAccessor -/** - * INTERNAL API. - */ -object UriJavaAccessor { - import collection.JavaConverters._ - - def hostApply(string: String): Host = Uri.Host(string) - def hostApply(string: String, mode: Uri.ParsingMode): Host = Uri.Host(string, mode = mode) - def hostApply(string: String, charset: Charset): Host = Uri.Host(string, charset = charset) - def emptyHost: Uri.Host = Uri.Host.Empty - - def queryApply(params: Array[akka.japi.Pair[String, String]]): Uri.Query = Uri.Query(params.map(_.toScala): _*) - def queryApply(params: java.util.Map[String, String]): Uri.Query = Uri.Query(params.asScala.toSeq: _*) - def queryApply(string: String, mode: Uri.ParsingMode): Uri.Query = Uri.Query(string, mode = mode) - def queryApply(string: String, charset: Charset): Uri.Query = Uri.Query(string, charset = charset) - def emptyQuery: Uri.Query = Uri.Query.Empty - - def pmStrict: Uri.ParsingMode = Uri.ParsingMode.Strict - def pmRelaxed: Uri.ParsingMode = Uri.ParsingMode.Relaxed -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/WithQValue.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/WithQValue.scala deleted file mode 100644 index a2d5e50827..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/WithQValue.scala +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model - -/** Helper trait for objects that allow creating new instances with a modified qValue. */ -trait WithQValue[T] { - /** truncates Double qValue to float and returns a new instance with this qValue set */ - def withQValue(qValue: Double): T = withQValue(qValue.toFloat) - def withQValue(qValue: Float): T -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/ByteRange.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/ByteRange.scala deleted file mode 100644 index 798c2d8192..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/ByteRange.scala +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model.headers - -import java.util.OptionalLong - -import akka.http.impl.util.{ Rendering, ValueRenderable } -import akka.http.javadsl.{ model ⇒ jm } - -sealed abstract class ByteRange extends jm.headers.ByteRange with ValueRenderable { - /** Java API */ - def getSliceFirst: OptionalLong = OptionalLong.empty - /** Java API */ - def getSliceLast: OptionalLong = OptionalLong.empty - /** Java API */ - def getOffset: OptionalLong = OptionalLong.empty - /** Java API */ - def getSuffixLength: OptionalLong = OptionalLong.empty - - /** Java API */ - def isSlice: Boolean = false - - /** Java API */ - def isFromOffset: Boolean = false - - /** Java API */ - def isSuffix: Boolean = false -} - -object ByteRange { - def apply(first: Long, last: Long) = Slice(first, last) - def fromOffset(offset: Long) = FromOffset(offset) - def suffix(length: Long) = Suffix(length) - - final case class Slice(first: Long, last: Long) extends ByteRange { - require(0 <= first && first <= last, "first must be >= 0 and <= last") - def render[R <: Rendering](r: R): r.type = r ~~ first ~~ '-' ~~ last - - /** Java API */ - override def isSlice: Boolean = true - /** Java API */ - override def getSliceFirst: OptionalLong = OptionalLong.of(first) - /** Java API */ - override def getSliceLast: OptionalLong = OptionalLong.of(last) - } - - final case class FromOffset(offset: Long) extends ByteRange { - require(0 <= offset, "offset must be >= 0") - def render[R <: Rendering](r: R): r.type = r ~~ offset ~~ '-' - - /** Java API */ - override def isFromOffset: Boolean = true - /** Java API */ - override def getOffset: OptionalLong = OptionalLong.of(offset) - } - - final case class Suffix(length: Long) extends ByteRange { - require(0 <= length, "length must be >= 0") - def render[R <: Rendering](r: R): r.type = r ~~ '-' ~~ length - - /** Java API */ - override def isSuffix: Boolean = true - /** Java API */ - override def getSuffixLength: OptionalLong = OptionalLong.of(length) - } -} \ No newline at end of file diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/CacheDirective.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/CacheDirective.scala deleted file mode 100644 index cf2879cdde..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/CacheDirective.scala +++ /dev/null @@ -1,113 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model.headers - -import scala.annotation.{ varargs, tailrec } -import scala.collection.immutable -import akka.http.impl.util._ -import akka.http.javadsl.{ model ⇒ jm } - -sealed trait CacheDirective extends Renderable with jm.headers.CacheDirective { - def value: String -} - -object CacheDirective { - sealed trait RequestDirective extends CacheDirective - sealed trait ResponseDirective extends CacheDirective - - final case class CustomCacheDirective(name: String, content: Option[String]) - extends RequestDirective with ResponseDirective with ValueRenderable { - def render[R <: Rendering](r: R): r.type = content match { - case Some(s) ⇒ r ~~ name ~~ '=' ~~# s - case None ⇒ r ~~ name - } - } - - def custom(name: String, content: Option[String]): RequestDirective with ResponseDirective = - CustomCacheDirective(name, content) - - sealed abstract class FieldNamesDirective extends Product with ValueRenderable { - def fieldNames: immutable.Seq[String] - final def render[R <: Rendering](r: R): r.type = - if (fieldNames.nonEmpty) { - r ~~ productPrefix ~~ '=' ~~ '"' - @tailrec def rec(i: Int = 0): r.type = - if (i < fieldNames.length) { - if (i > 0) r ~~ ',' - r.putEscaped(fieldNames(i)) - rec(i + 1) - } else r ~~ '"' - rec() - } else r ~~ productPrefix - } -} - -object CacheDirectives { - import CacheDirective._ - - // http://tools.ietf.org/html/rfc7234#section-5.2.1.1 - // http://tools.ietf.org/html/rfc7234#section-5.2.2.8 - final case class `max-age`(deltaSeconds: Long) extends RequestDirective with ResponseDirective with ValueRenderable { - def render[R <: Rendering](r: R): r.type = r ~~ productPrefix ~~ '=' ~~ deltaSeconds - } - - // http://tools.ietf.org/html/rfc7234#section-5.2.1.2 - final case class `max-stale`(deltaSeconds: Option[Long]) extends RequestDirective with ValueRenderable { - def render[R <: Rendering](r: R): r.type = deltaSeconds match { - case Some(s) ⇒ r ~~ productPrefix ~~ '=' ~~ s - case None ⇒ r ~~ productPrefix - } - } - - // http://tools.ietf.org/html/rfc7234#section-5.2.1.3 - final case class `min-fresh`(deltaSeconds: Long) extends RequestDirective with ValueRenderable { - def render[R <: Rendering](r: R): r.type = r ~~ productPrefix ~~ '=' ~~ deltaSeconds - } - - // http://tools.ietf.org/html/rfc7234#section-5.2.1.4 - case object `no-cache` extends SingletonValueRenderable with RequestDirective with ResponseDirective { - def apply(fieldNames: String*): `no-cache` = new `no-cache`(immutable.Seq(fieldNames: _*)) - } - - // http://tools.ietf.org/html/rfc7234#section-5.2.1.5 - // http://tools.ietf.org/html/rfc7234#section-5.2.2.3 - case object `no-store` extends SingletonValueRenderable with RequestDirective with ResponseDirective - - // http://tools.ietf.org/html/rfc7234#section-5.2.1.6 - // http://tools.ietf.org/html/rfc7234#section-5.2.2.4 - case object `no-transform` extends SingletonValueRenderable with RequestDirective with ResponseDirective - - // http://tools.ietf.org/html/rfc7234#section-5.2.1.7 - case object `only-if-cached` extends SingletonValueRenderable with RequestDirective - - // http://tools.ietf.org/html/rfc7234#section-5.2.2.1 - case object `must-revalidate` extends SingletonValueRenderable with ResponseDirective - - // http://tools.ietf.org/html/rfc7234#section-5.2.2.2 - final case class `no-cache`(fieldNames: immutable.Seq[String]) extends FieldNamesDirective with ResponseDirective - - // http://tools.ietf.org/html/rfc7234#section-5.2.2.5 - case object `public` extends SingletonValueRenderable with ResponseDirective - - /** Java API */ - def getPublic: ResponseDirective = `public` - - // http://tools.ietf.org/html/rfc7234#section-5.2.2.6 - final case class `private`(fieldNames: immutable.Seq[String]) extends FieldNamesDirective with ResponseDirective - object `private` { - def apply(fieldNames: String*): `private` = new `private`(immutable.Seq(fieldNames: _*)) - } - - /** Java API */ - @varargs def createPrivate(fieldNames: String*): ResponseDirective = new `private`(immutable.Seq(fieldNames: _*)) - - // http://tools.ietf.org/html/rfc7234#section-5.2.2.7 - case object `proxy-revalidate` extends SingletonValueRenderable with ResponseDirective - - // http://tools.ietf.org/html/rfc7234#section-5.2.2.9 - final case class `s-maxage`(deltaSeconds: Long) extends ResponseDirective with ValueRenderable { - def render[R <: Rendering](r: R): r.type = r ~~ productPrefix ~~ '=' ~~ deltaSeconds - } -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/ContentDispositionType.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/ContentDispositionType.scala deleted file mode 100644 index fa95a8f21d..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/ContentDispositionType.scala +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model.headers - -import akka.http.impl.util.{ Rendering, SingletonValueRenderable, Renderable } -import akka.http.javadsl.{ model ⇒ jm } - -sealed trait ContentDispositionType extends Renderable with jm.headers.ContentDispositionType - -object ContentDispositionTypes { - protected abstract class Predefined extends ContentDispositionType with SingletonValueRenderable { - def name: String = value - } - - case object inline extends Predefined - case object attachment extends Predefined - case object `form-data` extends Predefined - final case class Ext(name: String) extends ContentDispositionType { - def render[R <: Rendering](r: R): r.type = r ~~ name - } -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/EntityTag.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/EntityTag.scala deleted file mode 100644 index bbcca0889e..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/EntityTag.scala +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model.headers - -import akka.http.impl.model.JavaInitialization -import akka.util.Unsafe - -import scala.collection.immutable -import akka.http.impl.util.{ Renderer, Rendering, ValueRenderable } -import akka.http.javadsl.{ model ⇒ jm } - -final case class EntityTag(tag: String, weak: Boolean = false) extends jm.headers.EntityTag with ValueRenderable { - def render[R <: Rendering](r: R): r.type = if (weak) r ~~ "W/" ~~#! tag else r ~~#! tag -} - -object EntityTag { - def matchesRange(eTag: EntityTag, entityTagRange: EntityTagRange, weakComparison: Boolean) = - entityTagRange match { - case EntityTagRange.`*` ⇒ weakComparison || !eTag.weak - case EntityTagRange.Default(tags) ⇒ tags.exists(matches(eTag, _, weakComparison)) - } - def matches(eTag: EntityTag, other: EntityTag, weakComparison: Boolean) = - other.tag == eTag.tag && (weakComparison || !other.weak && !eTag.weak) -} - -sealed abstract class EntityTagRange extends jm.headers.EntityTagRange with ValueRenderable - -object EntityTagRange { - def apply(tags: EntityTag*) = Default(immutable.Seq(tags: _*)) - - implicit val tagsRenderer = Renderer.defaultSeqRenderer[EntityTag] // cache - - case object `*` extends EntityTagRange { - def render[R <: Rendering](r: R): r.type = r ~~ '*' - } - - final case class Default(tags: immutable.Seq[EntityTag]) extends EntityTagRange { - require(tags.nonEmpty, "tags must not be empty") - def render[R <: Rendering](r: R): r.type = r ~~ tags - } - - JavaInitialization.initializeStaticFieldWith( - `*`, classOf[jm.headers.EntityTagRange].getField("ALL")) - -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/HttpChallenge.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/HttpChallenge.scala deleted file mode 100644 index f4692f1339..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/HttpChallenge.scala +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model.headers - -import java.util -import akka.http.javadsl.{ model ⇒ jm } -import akka.http.impl.util._ -import akka.http.impl.util.JavaMapping.Implicits._ - -final case class HttpChallenge(scheme: String, realm: String, - params: Map[String, String] = Map.empty) extends jm.headers.HttpChallenge with ValueRenderable { - - def render[R <: Rendering](r: R): r.type = { - r ~~ scheme - if (realm != null) r ~~ " realm=" ~~#! realm - if (params.nonEmpty) params.foreach { case (k, v) ⇒ r ~~ ',' ~~ k ~~ '=' ~~# v } - r - } - - /** Java API */ - def getParams: util.Map[String, String] = params.asJava -} - -// FIXME: AbstractFunction3 required for bin compat. remove in Akka 3.0 and change realm in case class to option #20786 -object HttpChallenge extends scala.runtime.AbstractFunction3[String, String, Map[String, String], HttpChallenge] { - - def apply(scheme: String, realm: Option[String]): HttpChallenge = - HttpChallenge(scheme, realm.orNull, Map.empty[String, String]) - - def apply(scheme: String, realm: Option[String], params: Map[String, String]): HttpChallenge = - HttpChallenge(scheme, realm.orNull, params) -} - -object HttpChallenges { - - def basic(realm: String): HttpChallenge = HttpChallenge("Basic", realm) - - def oAuth2(realm: String): HttpChallenge = HttpChallenge("Bearer", realm) -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/HttpCookie.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/HttpCookie.scala deleted file mode 100644 index 5c27f3ead6..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/HttpCookie.scala +++ /dev/null @@ -1,134 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model.headers - -import akka.http.impl.model.parser.CharacterClasses -import akka.http.javadsl.model.headers -import akka.parboiled2.CharPredicate -import java.util.{ Optional, OptionalLong } -import akka.http.scaladsl.model.DateTime -import akka.http.impl.util._ -import akka.http.javadsl.{ model ⇒ jm } -import akka.http.impl.util.JavaMapping.Implicits._ -import scala.compat.java8.OptionConverters._ - -// see http://tools.ietf.org/html/rfc6265 -// sealed abstract to prevent generation of default apply method in companion -sealed abstract case class HttpCookiePair private ( - name: String, - value: String) extends jm.headers.HttpCookiePair with ToStringRenderable { - - def render[R <: Rendering](r: R): r.type = r ~~ name ~~ '=' ~~ value - def toCookie: HttpCookie = HttpCookie.fromPair(this) -} -object HttpCookiePair { - def apply(pair: (String, String)): HttpCookiePair = apply(pair._1, pair._2) - def apply(name: String, value: String): HttpCookiePair = { - HttpCookiePair.validate(name, value) - new HttpCookiePair(name, value) {} - } - - def raw(pair: (String, String)): HttpCookiePair = raw(pair._1, pair._2) - def raw(name: String, value: String): HttpCookiePair = { - HttpCookiePair.validateRaw(name, value) - new HttpCookiePair(name, value) {} - } - - private[http] def validate(name: String, value: String): Unit = { - import HttpCookie._ - require(nameChars.matchesAll(name), s"'${nameChars.firstMismatch(name).get}' not allowed in cookie name ('$name')") - require(valueChars.matchesAll(value), s"'${valueChars.firstMismatch(value).get}' not allowed in cookie content ('$value')") - } - private[http] def validateRaw(name: String, value: String): Unit = { - import HttpCookie._ - require(nameChars.matchesAll(name), s"'${nameChars.firstMismatch(name).get}' not allowed in cookie name ('$name')") - require(rawValueChars.matchesAll(value), s"'${rawValueChars.firstMismatch(value).get}' not allowed in cookie content ('$value')") - } -} - -// see http://tools.ietf.org/html/rfc6265 -final case class HttpCookie( - name: String, - value: String, - expires: Option[DateTime] = None, - maxAge: Option[Long] = None, - domain: Option[String] = None, - path: Option[String] = None, - secure: Boolean = false, - httpOnly: Boolean = false, - extension: Option[String] = None) extends jm.headers.HttpCookie with ToStringRenderable { - - /** Returns the name/value pair for this cookie, to be used in [[Cookie]] headers. */ - def pair: HttpCookiePair = HttpCookiePair(name, value) - - // TODO: suppress running these requires for cookies created from our header parser - - import HttpCookie._ - - HttpCookiePair.validate(name, value) - require(domain.forall(domainChars.matchesAll), s"'${domainChars.firstMismatch(domain.get).get}' not allowed in cookie domain ('${domain.get}')") - require(path.forall(pathOrExtChars.matchesAll), s"'${pathOrExtChars.firstMismatch(path.get).get}' not allowed in cookie path ('${path.get}')") - require(extension.forall(pathOrExtChars.matchesAll), s"'${pathOrExtChars.firstMismatch(extension.get).get}' not allowed in cookie extension ('${extension.get}')") - - def render[R <: Rendering](r: R): r.type = { - r ~~ name ~~ '=' ~~ value - if (expires.isDefined) expires.get.renderRfc1123DateTimeString(r ~~ "; Expires=") - if (maxAge.isDefined) r ~~ "; Max-Age=" ~~ maxAge.get - if (domain.isDefined) r ~~ "; Domain=" ~~ domain.get - if (path.isDefined) r ~~ "; Path=" ~~ path.get - if (secure) r ~~ "; Secure" - if (httpOnly) r ~~ "; HttpOnly" - if (extension.isDefined) r ~~ ';' ~~ ' ' ~~ extension.get - r - } - - /** Java API */ - def getExtension: Optional[String] = extension.asJava - /** Java API */ - def getPath: Optional[String] = path.asJava - /** Java API */ - def getDomain: Optional[String] = domain.asJava - /** Java API */ - def getMaxAge: OptionalLong = maxAge.asPrimitive - /** Java API */ - def getExpires: Optional[jm.DateTime] = expires.map(_.asJava).asJava - /** Java API */ - def withExpires(dateTime: jm.DateTime): headers.HttpCookie = copy(expires = Some(dateTime.asScala)) - /** Java API */ - def withDomain(domain: String): headers.HttpCookie = copy(domain = Some(domain)) - /** Java API */ - def withPath(path: String): headers.HttpCookie = copy(path = Some(path)) - /** Java API */ - def withMaxAge(maxAge: Long): headers.HttpCookie = copy(maxAge = Some(maxAge)) - /** Java API */ - def withSecure(secure: Boolean): headers.HttpCookie = copy(secure = secure) - /** Java API */ - def withHttpOnly(httpOnly: Boolean): headers.HttpCookie = copy(httpOnly = httpOnly) - /** Java API */ - def withExtension(extension: String): headers.HttpCookie = copy(extension = Some(extension)) -} - -object HttpCookie { - def fromPair( - pair: HttpCookiePair, - expires: Option[DateTime] = None, - maxAge: Option[Long] = None, - domain: Option[String] = None, - path: Option[String] = None, - secure: Boolean = false, - httpOnly: Boolean = false, - extension: Option[String] = None): HttpCookie = - HttpCookie(pair.name, pair.value, expires, maxAge, domain, path, secure, httpOnly, extension) - - import akka.http.impl.model.parser.CharacterClasses._ - - private[http] def nameChars = tchar - // http://tools.ietf.org/html/rfc6265#section-4.1.1 - // ; US-ASCII characters excluding CTLs, whitespace DQUOTE, comma, semicolon, and backslash - private[http] val valueChars = CharPredicate('\u0021', '\u0023' to '\u002B', '\u002D' to '\u003A', '\u003C' to '\u005B', '\u005D' to '\u007E') - private[http] val rawValueChars = CharacterClasses.`cookie-octet-raw` - private[http] val domainChars = ALPHANUM ++ ".-" - private[http] val pathOrExtChars = VCHAR ++ ' ' -- ';' -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/HttpCredentials.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/HttpCredentials.scala deleted file mode 100644 index b2d4b1f356..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/HttpCredentials.scala +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model.headers - -import akka.parboiled2.util.Base64 -import akka.http.scaladsl.model.HttpCharsets._ -import akka.http.impl.util.{ Rendering, ValueRenderable } -import akka.http.javadsl.{ model ⇒ jm } -import akka.http.impl.util.JavaMapping.Implicits._ - -sealed abstract class HttpCredentials extends jm.headers.HttpCredentials with ValueRenderable { - def scheme: String - def token: String - def params: Map[String, String] - - /** Java API */ - def getParams: java.util.Map[String, String] = params.asJava -} - -final case class BasicHttpCredentials(username: String, password: String) extends jm.headers.BasicHttpCredentials { - val cookie = { - val userPass = username + ':' + password - val bytes = userPass.getBytes(`UTF-8`.nioCharset) - Base64.rfc2045.encodeToChar(bytes, false) - } - def render[R <: Rendering](r: R): r.type = r ~~ "Basic " ~~ cookie - - override def scheme: String = "Basic" - override def token: String = String.valueOf(cookie) - override def params: Map[String, String] = Map.empty -} - -object BasicHttpCredentials { - def apply(credentials: String): BasicHttpCredentials = { - val bytes = Base64.rfc2045.decodeFast(credentials) - val userPass = new String(bytes, `UTF-8`.nioCharset) - userPass.indexOf(':') match { - case -1 ⇒ apply(userPass, "") - case ix ⇒ apply(userPass.substring(0, ix), userPass.substring(ix + 1)) - } - } -} - -final case class OAuth2BearerToken(token: String) extends jm.headers.OAuth2BearerToken { - def render[R <: Rendering](r: R): r.type = r ~~ "Bearer " ~~ token - - override def scheme: String = "Bearer" - override def params: Map[String, String] = Map.empty -} - -final case class GenericHttpCredentials(scheme: String, token: String, - params: Map[String, String] = Map.empty) extends HttpCredentials { - def render[R <: Rendering](r: R): r.type = { - r ~~ scheme - if (!token.isEmpty) r ~~ ' ' ~~ token - if (params.nonEmpty) - params foreach new (((String, String)) ⇒ Unit) { - var first = true - def apply(kvp: (String, String)): Unit = { - val (k, v) = kvp - if (first) { r ~~ ' '; first = false } else r ~~ ',' - if (!k.isEmpty) r ~~ k ~~ '=' - r ~~# v - } - } - r - } -} - -object GenericHttpCredentials { - def apply(scheme: String, params: Map[String, String]): GenericHttpCredentials = apply(scheme, "", params) -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/HttpEncoding.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/HttpEncoding.scala deleted file mode 100644 index adf5da4809..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/HttpEncoding.scala +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model.headers - -import akka.http.impl.model.JavaInitialization -import akka.util.Unsafe - -import language.implicitConversions -import akka.http.impl.util._ -import akka.http.javadsl.{ model ⇒ jm } -import akka.http.scaladsl.model.WithQValue -import akka.http.impl.util.JavaMapping.Implicits._ - -sealed abstract class HttpEncodingRange extends jm.headers.HttpEncodingRange with ValueRenderable with WithQValue[HttpEncodingRange] { - def qValue: Float - def matches(encoding: HttpEncoding): Boolean - - /** Java API */ - def matches(encoding: jm.headers.HttpEncoding): Boolean = matches(encoding.asScala) -} - -object HttpEncodingRange { - case class `*`(qValue: Float) extends HttpEncodingRange { - require(0.0f <= qValue && qValue <= 1.0f, "qValue must be >= 0 and <= 1.0") - final def render[R <: Rendering](r: R): r.type = if (qValue < 1.0f) r ~~ "*;q=" ~~ qValue else r ~~ '*' - def matches(encoding: HttpEncoding) = true - def withQValue(qValue: Float) = - if (qValue == 1.0f) `*` else if (qValue != this.qValue) `*`(qValue.toFloat) else this - } - object `*` extends `*`(1.0f) - - final case class One(encoding: HttpEncoding, qValue: Float) extends HttpEncodingRange { - require(0.0f <= qValue && qValue <= 1.0f, "qValue must be >= 0 and <= 1.0") - def matches(encoding: HttpEncoding) = this.encoding.value.equalsIgnoreCase(encoding.value) - def withQValue(qValue: Float) = One(encoding, qValue) - def render[R <: Rendering](r: R): r.type = if (qValue < 1.0f) r ~~ encoding ~~ ";q=" ~~ qValue else r ~~ encoding - } - - implicit def apply(encoding: HttpEncoding): HttpEncodingRange = apply(encoding, 1.0f) - def apply(encoding: HttpEncoding, qValue: Float): HttpEncodingRange = One(encoding, qValue) - - JavaInitialization.initializeStaticFieldWith( - `*`, classOf[jm.headers.HttpEncodingRange].getField("ALL")) - -} - -final case class HttpEncoding private[http] (value: String) extends jm.headers.HttpEncoding with LazyValueBytesRenderable with WithQValue[HttpEncodingRange] { - def withQValue(qValue: Float): HttpEncodingRange = HttpEncodingRange(this, qValue.toFloat) -} - -object HttpEncoding { - def custom(value: String): HttpEncoding = apply(value) -} - -// see http://www.iana.org/assignments/http-parameters/http-parameters.xml -object HttpEncodings extends ObjectRegistry[String, HttpEncoding] { - // format: OFF - val compress = register("compress") - val chunked = register("chunked") - val deflate = register("deflate") - val gzip = register("gzip") - val identity = register("identity") - val `x-compress` = register("x-compress") - val `x-zip` = register("x-zip") - // format: ON - - private def register(encoding: HttpEncoding): HttpEncoding = register(encoding.value.toRootLowerCase, encoding) - private def register(value: String): HttpEncoding = register(HttpEncoding(value)) -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/HttpOrigin.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/HttpOrigin.scala deleted file mode 100644 index bd32d48471..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/HttpOrigin.scala +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model.headers - -import akka.http.impl.model.JavaInitialization - -import language.implicitConversions -import scala.collection.immutable -import akka.parboiled2.UTF8 -import akka.http.impl.model.parser.UriParser -import akka.http.impl.util._ -import akka.http.javadsl.{ model ⇒ jm } -import akka.http.scaladsl.model.Uri -import akka.http.impl.util.JavaMapping.Implicits._ - -abstract class HttpOriginRange extends jm.headers.HttpOriginRange with ValueRenderable { - def matches(origin: HttpOrigin): Boolean - - /** Java API */ - def matches(origin: jm.headers.HttpOrigin): Boolean = matches(origin.asScala) -} - -object HttpOriginRange { - case object `*` extends HttpOriginRange { - def matches(origin: HttpOrigin) = true - def render[R <: Rendering](r: R): r.type = r ~~ '*' - } - - def apply(origins: HttpOrigin*): Default = Default(immutable.Seq(origins: _*)) - - final case class Default(origins: immutable.Seq[HttpOrigin]) extends HttpOriginRange { - def matches(origin: HttpOrigin): Boolean = origins contains origin - def render[R <: Rendering](r: R): r.type = r ~~ origins - } - - JavaInitialization.initializeStaticFieldWith( - `*`, classOf[jm.headers.HttpOriginRange].getField("ALL")) - -} - -final case class HttpOrigin(scheme: String, host: Host) extends jm.headers.HttpOrigin with ValueRenderable { - def render[R <: Rendering](r: R): r.type = host.renderValue(r ~~ scheme ~~ "://") -} - -object HttpOrigin { - implicit val originsRenderer: Renderer[immutable.Seq[HttpOrigin]] = Renderer.seqRenderer(" ", "null") - - implicit def apply(str: String): HttpOrigin = { - val parser = new UriParser(str, UTF8, Uri.ParsingMode.Relaxed) - parser.parseOrigin() - } -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/LanguageRange.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/LanguageRange.scala deleted file mode 100644 index 1da7d6ddad..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/LanguageRange.scala +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model.headers - -import akka.http.impl.model.JavaInitialization -import akka.util.Unsafe - -import scala.language.implicitConversions -import scala.collection.immutable -import akka.http.impl.util._ -import akka.http.scaladsl.model.WithQValue -import akka.http.javadsl.{ model ⇒ jm } -import akka.http.impl.util.JavaMapping.Implicits._ - -sealed trait LanguageRange extends jm.headers.LanguageRange with ValueRenderable with WithQValue[LanguageRange] { - def qValue: Float - def primaryTag: String - def subTags: immutable.Seq[String] - def matches(lang: Language): Boolean - final def render[R <: Rendering](r: R): r.type = { - r ~~ primaryTag - if (subTags.nonEmpty) subTags.foreach(r ~~ '-' ~~ _) - if (qValue < 1.0f) r ~~ ";q=" ~~ qValue - r - } - - /** Java API */ - def matches(language: jm.headers.Language) = matches(language.asScala) - def getSubTags: java.lang.Iterable[String] = subTags.asJava -} -object LanguageRange { - case class `*`(qValue: Float) extends LanguageRange { - require(0.0f <= qValue && qValue <= 1.0f, "qValue must be >= 0 and <= 1.0") - def primaryTag = "*" - def subTags = Nil - def matches(lang: Language) = true - def withQValue(qValue: Float) = - if (qValue == 1.0f) `*` else if (qValue != this.qValue) `*`(qValue.toFloat) else this - } - object `*` extends `*`(1.0f) - - final case class One(language: Language, qValue: Float) extends LanguageRange { - require(0.0f <= qValue && qValue <= 1.0f, "qValue must be >= 0 and <= 1.0") - def matches(l: Language) = - (language.primaryTag equalsIgnoreCase l.primaryTag) && - language.subTags.size <= l.subTags.size && - (language.subTags zip l.subTags).forall(t ⇒ t._1 equalsIgnoreCase t._2) - def primaryTag = language.primaryTag - def subTags = language.subTags - def withQValue(qValue: Float) = One(language, qValue) - } - - implicit def apply(language: Language): LanguageRange = apply(language, 1.0f) - def apply(language: Language, qValue: Float): LanguageRange = One(language, qValue) - - JavaInitialization.initializeStaticFieldWith( - `*`, classOf[jm.headers.LanguageRange].getField("ALL")) - -} - -final case class Language(primaryTag: String, subTags: immutable.Seq[String]) - extends jm.headers.Language with ValueRenderable with WithQValue[LanguageRange] { - def withQValue(qValue: Float) = LanguageRange(this, qValue.toFloat) - def render[R <: Rendering](r: R): r.type = { - r ~~ primaryTag - if (subTags.nonEmpty) subTags.foreach(r ~~ '-' ~~ _) - r - } - - /** Java API */ - def getSubTags: java.lang.Iterable[String] = subTags.asJava -} -object Language { - implicit def apply(compoundTag: String): Language = - if (compoundTag.indexOf('-') >= 0) { - val tags = compoundTag.split('-') - new Language(tags.head, immutable.Seq(tags.tail: _*)) - } else new Language(compoundTag, immutable.Seq.empty) - def apply(primaryTag: String, subTags: String*): Language = new Language(primaryTag, immutable.Seq(subTags: _*)) -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/LinkValue.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/LinkValue.scala deleted file mode 100644 index 4a063199b9..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/LinkValue.scala +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model.headers - -import scala.collection.immutable -import akka.parboiled2.CharPredicate -import akka.http.impl.util._ -import akka.http.scaladsl.model._ -import akka.http.javadsl.{ model ⇒ jm } -import akka.http.impl.util.JavaMapping.Implicits._ -import UriRendering.UriRenderer - -final case class LinkValue(uri: Uri, params: immutable.Seq[LinkParam]) extends jm.headers.LinkValue with ValueRenderable { - def render[R <: Rendering](r: R): r.type = { - r ~~ '<' ~~ uri ~~ '>' - if (params.nonEmpty) r ~~ "; " ~~ params - r - } - - def getUri: jm.Uri = uri.asJava - def getParams: java.lang.Iterable[jm.headers.LinkParam] = params.asJava -} - -object LinkValue { - def apply(uri: Uri, params: LinkParam*): LinkValue = apply(uri, immutable.Seq(params: _*)) -} - -sealed abstract class LinkParam extends jm.headers.LinkParam with ToStringRenderable { - val key: String = getClass.getSimpleName - def value: AnyRef -} -object LinkParam { - implicit val paramsRenderer: Renderer[immutable.Seq[LinkParam]] = Renderer.seqRenderer(separator = "; ") -} - -object LinkParams { - private val reserved = CharPredicate(" ,;") - - // A few convenience rels - val next = rel("next") - val prev = rel("prev") - val first = rel("first") - val last = rel("last") - val blockedBy = rel("blocked-by") - - // http://tools.ietf.org/html/rfc5988#section-5.3 - // can be either a bare word, an absolute URI, or a quoted, space-separated string of zero-or-more of either. - final case class rel(value: String) extends LinkParam { - def render[R <: Rendering](r: R): r.type = { - r ~~ "rel=" - if (reserved matchesAny value) r ~~ '"' ~~ value ~~ '"' else r ~~ value - } - } - - // http://tools.ietf.org/html/rfc5988#section-5.2 - final case class anchor(uri: Uri) extends LinkParam { - def value: AnyRef = uri - - def render[R <: Rendering](r: R): r.type = r ~~ "anchor=\"" ~~ uri ~~ '"' - } - - // http://tools.ietf.org/html/rfc5988#section-5.3 - // can be either a bare word, an absolute URI, or a quoted, space-separated string of zero-or-more of either. - final case class rev(value: String) extends LinkParam { - def render[R <: Rendering](r: R): r.type = { - r ~~ "rev=" - if (reserved matchesAny value) r ~~ '"' ~~ value ~~ '"' else r ~~ value - } - } - - // http://tools.ietf.org/html/rfc5988#section-5.4 - final case class hreflang(lang: Language) extends LinkParam { - def value: AnyRef = lang - - def render[R <: Rendering](r: R): r.type = r ~~ "hreflang=" ~~ lang - } - - // http://tools.ietf.org/html/rfc5988#section-5.4 - final case class media(desc: String) extends LinkParam { - def value: AnyRef = desc - - def render[R <: Rendering](r: R): r.type = { - r ~~ "media=" - if (reserved matchesAny desc) r ~~ '"' ~~ desc ~~ '"' else r ~~ desc - } - } - - // http://tools.ietf.org/html/rfc5988#section-5.4 - final case class title(title: String) extends LinkParam { - def value: AnyRef = title - - def render[R <: Rendering](r: R): r.type = r ~~ "title=\"" ~~ title ~~ '"' - } - - // http://tools.ietf.org/html/rfc5988#section-5.4 - final case class `title*`(title: String) extends LinkParam { - def value: AnyRef = title - - def render[R <: Rendering](r: R): r.type = { - r ~~ "title*=" - if (reserved matchesAny title) r ~~ '"' ~~ title ~~ '"' else r ~~ title - } - } - - // http://tools.ietf.org/html/rfc5988#section-5.4 - final case class `type`(mediaType: MediaType) extends LinkParam { - def value: AnyRef = mediaType - - def render[R <: Rendering](r: R): r.type = { - r ~~ "type=" - if (reserved matchesAny mediaType.value) r ~~ '"' ~~ mediaType.value ~~ '"' else r ~~ mediaType.value - } - } -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/ProductVersion.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/ProductVersion.scala deleted file mode 100644 index 0dcb470796..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/ProductVersion.scala +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model.headers - -import scala.collection.immutable -import scala.util.{ Failure, Success } -import akka.parboiled2.ParseError -import akka.http.javadsl.{ model ⇒ jm } -import akka.http.impl.model.parser.HeaderParser -import akka.http.impl.util._ - -final case class ProductVersion(product: String = "", version: String = "", comment: String = "") extends jm.headers.ProductVersion with ValueRenderable { - def render[R <: Rendering](r: R): r.type = { - r ~~ product - if (!version.isEmpty) r ~~ '/' ~~ version - if (!comment.isEmpty) { - if (!product.isEmpty || !version.isEmpty) r ~~ ' ' - r ~~ '(' ~~ comment ~~ ')' - } - r - } -} - -object ProductVersion { - implicit val productsRenderer: Renderer[immutable.Seq[ProductVersion]] = Renderer.seqRenderer[ProductVersion](separator = " ") - - /** parses a string of multiple ProductVersions */ - def parseMultiple(string: String): immutable.Seq[ProductVersion] = { - val parser = new HeaderParser(string) - def fail(msg: String) = throw new IllegalArgumentException(s"'$string' is not a legal sequence of ProductVersions: $msg") - parser.products.run() match { - case Success(x) ⇒ immutable.Seq(x: _*) - case Failure(e: ParseError) ⇒ fail(parser.formatError(e)) - case Failure(e) ⇒ fail(e.getMessage) - } - } -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/RangeUnit.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/RangeUnit.scala deleted file mode 100644 index 4510af4d5e..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/RangeUnit.scala +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model.headers - -import akka.http.impl.util.{ Rendering, ValueRenderable } -import akka.http.javadsl.{ model ⇒ jm } - -sealed abstract class RangeUnit extends jm.headers.RangeUnit with ValueRenderable { - def name: String -} - -object RangeUnits { - case object Bytes extends RangeUnit { - def name = "Bytes" - - def render[R <: Rendering](r: R): r.type = r ~~ "bytes" - } - - final case class Other(name: String) extends RangeUnit { - def render[R <: Rendering](r: R): r.type = r ~~ name - } -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/UpgradeProtocol.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/UpgradeProtocol.scala deleted file mode 100644 index 6e8e1b5469..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/UpgradeProtocol.scala +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model.headers - -import akka.http.impl.util.{ Rendering, ValueRenderable } - -final case class UpgradeProtocol(name: String, version: Option[String] = None) extends ValueRenderable { - def render[R <: Rendering](r: R): r.type = { - r ~~ name - version.foreach(v ⇒ r ~~ '/' ~~ v) - r - } -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/WebSocketExtension.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/WebSocketExtension.scala deleted file mode 100644 index 1c4a0067c3..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/WebSocketExtension.scala +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model.headers - -import scala.collection.immutable -import akka.http.impl.util.{ Rendering, ValueRenderable } - -/** - * A websocket extension as defined in http://tools.ietf.org/html/rfc6455#section-4.3 - */ -final case class WebSocketExtension(name: String, params: immutable.Map[String, String] = Map.empty) extends ValueRenderable { - def render[R <: Rendering](r: R): r.type = { - r ~~ name - if (params.nonEmpty) - params.foreach { - case (k, "") ⇒ r ~~ "; " ~~ k - case (k, v) ⇒ r ~~ "; " ~~ k ~~ '=' ~~# v - } - r - } -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/headers.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/headers.scala deleted file mode 100644 index e70c530f00..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/headers.scala +++ /dev/null @@ -1,954 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model.headers - -import java.lang.Iterable -import java.net.InetSocketAddress -import java.security.MessageDigest -import java.util -import javax.net.ssl.SSLSession -import akka.stream.scaladsl.ScalaSessionAPI - -import scala.reflect.ClassTag -import scala.util.{ Failure, Success, Try } -import scala.annotation.tailrec -import scala.collection.immutable -import akka.parboiled2.util.Base64 -import akka.event.Logging -import akka.http.impl.util._ -import akka.http.javadsl.{ model ⇒ jm } -import akka.http.scaladsl.model._ - -sealed abstract class ModeledCompanion[T: ClassTag] extends Renderable { - val name = ModeledCompanion.nameFromClass(getClass) - val lowercaseName = name.toRootLowerCase - private[this] val nameBytes = name.asciiBytes - final def render[R <: Rendering](r: R): r.type = r ~~ nameBytes ~~ ':' ~~ ' ' - - /** - * Parses the given value into a header of this type. Returns `Right[T]` if parsing - * was successful and `Left(errors)` otherwise. - */ - def parseFromValueString(value: String): Either[List[ErrorInfo], T] = - HttpHeader.parse(name, value) match { - case HttpHeader.ParsingResult.Ok(header: T, Nil) ⇒ Right(header) - case res ⇒ Left(res.errors) - } -} -/** INTERNAL API */ -private[akka] object ModeledCompanion { - def nameFromClass[T](clazz: Class[T]): String = { - val name = { - val n = Logging.simpleName(clazz).replace("$minus", "-") - if (n.last == '$') n.dropRight(1) // drop trailing $ - else n - } - - val dollarIndex = name.indexOf('$') - if (dollarIndex != -1) name.drop(dollarIndex + 1) - else name - } -} - -sealed trait ModeledHeader extends HttpHeader with Serializable { - def renderInRequests: Boolean = false // default implementation - def renderInResponses: Boolean = false // default implementation - def name: String = companion.name - def value: String = renderValue(new StringRendering).get - def lowercaseName: String = companion.lowercaseName - final def render[R <: Rendering](r: R): r.type = renderValue(r ~~ companion) - protected[http] def renderValue[R <: Rendering](r: R): r.type - protected def companion: ModeledCompanion[_] -} - -private[headers] sealed trait RequestHeader extends ModeledHeader { override def renderInRequests = true } -private[headers] sealed trait ResponseHeader extends ModeledHeader { override def renderInResponses = true } -private[headers] sealed trait RequestResponseHeader extends RequestHeader with ResponseHeader -private[headers] sealed trait SyntheticHeader extends ModeledHeader - -/** - * Superclass for user-defined custom headers defined by implementing `name` and `value`. - * - * Prefer to extend [[ModeledCustomHeader]] and [[ModeledCustomHeaderCompanion]] instead if - * planning to use the defined header in match clauses (e.g. in the routing layer of Akka HTTP), - * as they allow the custom header to be matched from [[RawHeader]] and vice-versa. - */ -abstract class CustomHeader extends jm.headers.CustomHeader { - def lowercaseName: String = name.toRootLowerCase - final def render[R <: Rendering](r: R): r.type = r ~~ name ~~ ':' ~~ ' ' ~~ value -} - -/** - * To be extended by companion object of a custom header extending [[ModeledCustomHeader]]. - * Implements necessary apply and unapply methods to make the such defined header feel "native". - */ -abstract class ModeledCustomHeaderCompanion[H <: ModeledCustomHeader[H]] { - def name: String - def lowercaseName: String = name.toRootLowerCase - - def parse(value: String): Try[H] - - def apply(value: String): H = - parse(value) match { - case Success(parsed) ⇒ parsed - case Failure(ex) ⇒ throw new IllegalArgumentException(s"Unable to construct custom header by parsing: '$value'", ex) - } - - def unapply(h: HttpHeader): Option[String] = h match { - case _: RawHeader ⇒ if (h.lowercaseName == lowercaseName) Some(h.value) else None - case _: CustomHeader ⇒ if (h.lowercaseName == lowercaseName) Some(h.value) else None - case _ ⇒ None - } - - final implicit val implicitlyLocatableCompanion: ModeledCustomHeaderCompanion[H] = this -} - -/** - * Support class for building user-defined custom headers defined by implementing `name` and `value`. - * By implementing a [[ModeledCustomHeader]] instead of [[CustomHeader]] directly, all needed unapply - * methods are provided for this class, such that it can be pattern matched on from [[RawHeader]] and - * the other way around as well. - */ -abstract class ModeledCustomHeader[H <: ModeledCustomHeader[H]] extends CustomHeader { this: H ⇒ - def companion: ModeledCustomHeaderCompanion[H] - - final override def name = companion.name - final override def lowercaseName = name.toRootLowerCase -} - -import akka.http.impl.util.JavaMapping.Implicits._ - -// http://tools.ietf.org/html/rfc7231#section-5.3.2 -object Accept extends ModeledCompanion[Accept] { - def apply(mediaRanges: MediaRange*): Accept = apply(immutable.Seq(mediaRanges: _*)) - implicit val mediaRangesRenderer = Renderer.defaultSeqRenderer[MediaRange] // cache -} -final case class Accept(mediaRanges: immutable.Seq[MediaRange]) extends jm.headers.Accept with RequestHeader { - import Accept.mediaRangesRenderer - def renderValue[R <: Rendering](r: R): r.type = r ~~ mediaRanges - protected def companion = Accept - def acceptsAll = mediaRanges.exists(mr ⇒ mr.isWildcard && mr.qValue > 0f) - - /** Java API */ - def getMediaRanges: Iterable[jm.MediaRange] = mediaRanges.asJava -} - -// http://tools.ietf.org/html/rfc7231#section-5.3.3 -object `Accept-Charset` extends ModeledCompanion[`Accept-Charset`] { - def apply(first: HttpCharsetRange, more: HttpCharsetRange*): `Accept-Charset` = apply(immutable.Seq(first +: more: _*)) - implicit val charsetRangesRenderer = Renderer.defaultSeqRenderer[HttpCharsetRange] // cache -} -final case class `Accept-Charset`(charsetRanges: immutable.Seq[HttpCharsetRange]) extends jm.headers.AcceptCharset - with RequestHeader { - require(charsetRanges.nonEmpty, "charsetRanges must not be empty") - import `Accept-Charset`.charsetRangesRenderer - def renderValue[R <: Rendering](r: R): r.type = r ~~ charsetRanges - protected def companion = `Accept-Charset` - - /** Java API */ - def getCharsetRanges: Iterable[jm.HttpCharsetRange] = charsetRanges.asJava -} - -// http://tools.ietf.org/html/rfc7231#section-5.3.4 -object `Accept-Encoding` extends ModeledCompanion[`Accept-Encoding`] { - def apply(encodings: HttpEncodingRange*): `Accept-Encoding` = apply(immutable.Seq(encodings: _*)) - implicit val encodingsRenderer = Renderer.defaultSeqRenderer[HttpEncodingRange] // cache -} -final case class `Accept-Encoding`(encodings: immutable.Seq[HttpEncodingRange]) extends jm.headers.AcceptEncoding - with RequestHeader { - import `Accept-Encoding`.encodingsRenderer - def renderValue[R <: Rendering](r: R): r.type = r ~~ encodings - protected def companion = `Accept-Encoding` - - /** Java API */ - def getEncodings: Iterable[jm.headers.HttpEncodingRange] = encodings.asJava -} - -// http://tools.ietf.org/html/rfc7231#section-5.3.5 -object `Accept-Language` extends ModeledCompanion[`Accept-Language`] { - def apply(first: LanguageRange, more: LanguageRange*): `Accept-Language` = apply(immutable.Seq(first +: more: _*)) - implicit val languagesRenderer = Renderer.defaultSeqRenderer[LanguageRange] // cache -} -final case class `Accept-Language`(languages: immutable.Seq[LanguageRange]) extends jm.headers.AcceptLanguage - with RequestHeader { - require(languages.nonEmpty, "languages must not be empty") - import `Accept-Language`.languagesRenderer - def renderValue[R <: Rendering](r: R): r.type = r ~~ languages - protected def companion = `Accept-Language` - - /** Java API */ - def getLanguages: Iterable[jm.headers.LanguageRange] = languages.asJava -} - -// http://tools.ietf.org/html/rfc7233#section-2.3 -object `Accept-Ranges` extends ModeledCompanion[`Accept-Ranges`] { - def apply(rangeUnits: RangeUnit*): `Accept-Ranges` = apply(immutable.Seq(rangeUnits: _*)) - implicit val rangeUnitsRenderer = Renderer.defaultSeqRenderer[RangeUnit] // cache -} -final case class `Accept-Ranges`(rangeUnits: immutable.Seq[RangeUnit]) extends jm.headers.AcceptRanges - with ResponseHeader { - import `Accept-Ranges`.rangeUnitsRenderer - def renderValue[R <: Rendering](r: R): r.type = if (rangeUnits.isEmpty) r ~~ "none" else r ~~ rangeUnits - protected def companion = `Accept-Ranges` - - /** Java API */ - def getRangeUnits: Iterable[jm.headers.RangeUnit] = rangeUnits.asJava -} - -// http://www.w3.org/TR/cors/#access-control-allow-credentials-response-header -object `Access-Control-Allow-Credentials` extends ModeledCompanion[`Access-Control-Allow-Credentials`] -final case class `Access-Control-Allow-Credentials`(allow: Boolean) - extends jm.headers.AccessControlAllowCredentials with ResponseHeader { - def renderValue[R <: Rendering](r: R): r.type = r ~~ allow.toString - protected def companion = `Access-Control-Allow-Credentials` -} - -// http://www.w3.org/TR/cors/#access-control-allow-headers-response-header -object `Access-Control-Allow-Headers` extends ModeledCompanion[`Access-Control-Allow-Headers`] { - def apply(headers: String*): `Access-Control-Allow-Headers` = apply(immutable.Seq(headers: _*)) - implicit val headersRenderer = Renderer.defaultSeqRenderer[String] // cache -} -final case class `Access-Control-Allow-Headers`(headers: immutable.Seq[String]) - extends jm.headers.AccessControlAllowHeaders with ResponseHeader { - import `Access-Control-Allow-Headers`.headersRenderer - def renderValue[R <: Rendering](r: R): r.type = r ~~ headers - protected def companion = `Access-Control-Allow-Headers` - - /** Java API */ - def getHeaders: Iterable[String] = headers.asJava -} - -// http://www.w3.org/TR/cors/#access-control-allow-methods-response-header -object `Access-Control-Allow-Methods` extends ModeledCompanion[`Access-Control-Allow-Methods`] { - def apply(methods: HttpMethod*): `Access-Control-Allow-Methods` = apply(immutable.Seq(methods: _*)) - implicit val methodsRenderer = Renderer.defaultSeqRenderer[HttpMethod] // cache -} -final case class `Access-Control-Allow-Methods`(methods: immutable.Seq[HttpMethod]) - extends jm.headers.AccessControlAllowMethods with ResponseHeader { - import `Access-Control-Allow-Methods`.methodsRenderer - def renderValue[R <: Rendering](r: R): r.type = r ~~ methods - protected def companion = `Access-Control-Allow-Methods` - - /** Java API */ - def getMethods: Iterable[jm.HttpMethod] = methods.asJava -} - -// http://www.w3.org/TR/cors/#access-control-allow-origin-response-header -object `Access-Control-Allow-Origin` extends ModeledCompanion[`Access-Control-Allow-Origin`] { - val `*` = forRange(HttpOriginRange.`*`) - val `null` = forRange(HttpOriginRange()) - def apply(origin: HttpOrigin) = forRange(HttpOriginRange(origin)) - - /** - * Creates an `Access-Control-Allow-Origin` header for the given origin range. - * - * CAUTION: Even though allowed by the spec (http://www.w3.org/TR/cors/#access-control-allow-origin-response-header) - * `Access-Control-Allow-Origin` headers with more than a single origin appear to be largely unsupported in the field. - * Make sure to thoroughly test such usages with all expected clients! - */ - def forRange(range: HttpOriginRange) = new `Access-Control-Allow-Origin`(range) -} -final case class `Access-Control-Allow-Origin` private (range: HttpOriginRange) - extends jm.headers.AccessControlAllowOrigin with ResponseHeader { - def renderValue[R <: Rendering](r: R): r.type = r ~~ range - protected def companion = `Access-Control-Allow-Origin` -} - -// http://www.w3.org/TR/cors/#access-control-expose-headers-response-header -object `Access-Control-Expose-Headers` extends ModeledCompanion[`Access-Control-Expose-Headers`] { - def apply(headers: String*): `Access-Control-Expose-Headers` = apply(immutable.Seq(headers: _*)) - implicit val headersRenderer = Renderer.defaultSeqRenderer[String] // cache -} -final case class `Access-Control-Expose-Headers`(headers: immutable.Seq[String]) - extends jm.headers.AccessControlExposeHeaders with ResponseHeader { - import `Access-Control-Expose-Headers`.headersRenderer - def renderValue[R <: Rendering](r: R): r.type = r ~~ headers - protected def companion = `Access-Control-Expose-Headers` - - /** Java API */ - def getHeaders: Iterable[String] = headers.asJava -} - -// http://www.w3.org/TR/cors/#access-control-max-age-response-header -object `Access-Control-Max-Age` extends ModeledCompanion[`Access-Control-Max-Age`] -final case class `Access-Control-Max-Age`(deltaSeconds: Long) extends jm.headers.AccessControlMaxAge - with ResponseHeader { - def renderValue[R <: Rendering](r: R): r.type = r ~~ deltaSeconds - protected def companion = `Access-Control-Max-Age` -} - -// http://www.w3.org/TR/cors/#access-control-request-headers-request-header -object `Access-Control-Request-Headers` extends ModeledCompanion[`Access-Control-Request-Headers`] { - def apply(headers: String*): `Access-Control-Request-Headers` = apply(immutable.Seq(headers: _*)) - implicit val headersRenderer = Renderer.defaultSeqRenderer[String] // cache -} -final case class `Access-Control-Request-Headers`(headers: immutable.Seq[String]) - extends jm.headers.AccessControlRequestHeaders with RequestHeader { - import `Access-Control-Request-Headers`.headersRenderer - def renderValue[R <: Rendering](r: R): r.type = r ~~ headers - protected def companion = `Access-Control-Request-Headers` - - /** Java API */ - def getHeaders: Iterable[String] = headers.asJava -} - -// http://www.w3.org/TR/cors/#access-control-request-method-request-header -object `Access-Control-Request-Method` extends ModeledCompanion[`Access-Control-Request-Method`] -final case class `Access-Control-Request-Method`(method: HttpMethod) extends jm.headers.AccessControlRequestMethod - with RequestHeader { - def renderValue[R <: Rendering](r: R): r.type = r ~~ method - protected def companion = `Access-Control-Request-Method` -} - -// http://tools.ietf.org/html/rfc7234#section-5.1 -object Age extends ModeledCompanion[Age] -final case class Age(deltaSeconds: Long) extends jm.headers.Age with ResponseHeader { - def renderValue[R <: Rendering](r: R): r.type = r ~~ deltaSeconds - protected def companion = Age -} - -// http://tools.ietf.org/html/rfc7231#section-7.4.1 -object Allow extends ModeledCompanion[Allow] { - def apply(methods: HttpMethod*): Allow = apply(immutable.Seq(methods: _*)) - implicit val methodsRenderer = Renderer.defaultSeqRenderer[HttpMethod] // cache -} -final case class Allow(methods: immutable.Seq[HttpMethod]) extends jm.headers.Allow with ResponseHeader { - import Allow.methodsRenderer - def renderValue[R <: Rendering](r: R): r.type = r ~~ methods - protected def companion = Allow - - /** Java API */ - def getMethods: Iterable[jm.HttpMethod] = methods.asJava -} - -// http://tools.ietf.org/html/rfc7235#section-4.2 -object Authorization extends ModeledCompanion[Authorization] -final case class Authorization(credentials: HttpCredentials) extends jm.headers.Authorization with RequestHeader { - def renderValue[R <: Rendering](r: R): r.type = r ~~ credentials - protected def companion = Authorization -} - -// http://tools.ietf.org/html/rfc7234#section-5.2 -object `Cache-Control` extends ModeledCompanion[`Cache-Control`] { - def apply(first: CacheDirective, more: CacheDirective*): `Cache-Control` = apply(immutable.Seq(first +: more: _*)) - implicit val directivesRenderer = Renderer.defaultSeqRenderer[CacheDirective] // cache -} -final case class `Cache-Control`(directives: immutable.Seq[CacheDirective]) extends jm.headers.CacheControl - with RequestResponseHeader { - require(directives.nonEmpty, "directives must not be empty") - import `Cache-Control`.directivesRenderer - def renderValue[R <: Rendering](r: R): r.type = r ~~ directives - protected def companion = `Cache-Control` - - /** Java API */ - def getDirectives: Iterable[jm.headers.CacheDirective] = directives.asJava -} - -// http://tools.ietf.org/html/rfc7230#section-6.1 -object Connection extends ModeledCompanion[Connection] { - def apply(first: String, more: String*): Connection = apply(immutable.Seq(first +: more: _*)) - implicit val tokensRenderer = Renderer.defaultSeqRenderer[String] // cache -} -final case class Connection(tokens: immutable.Seq[String]) extends jm.headers.Connection - with RequestResponseHeader { - require(tokens.nonEmpty, "tokens must not be empty") - import Connection.tokensRenderer - def renderValue[R <: Rendering](r: R): r.type = r ~~ tokens - def hasClose = has("close") - def hasKeepAlive = has("keep-alive") - def hasUpgrade = has("upgrade") - def append(tokens: immutable.Seq[String]) = Connection(this.tokens ++ tokens) - @tailrec private def has(item: String, ix: Int = 0): Boolean = - if (ix < tokens.length) - if (tokens(ix) equalsIgnoreCase item) true - else has(item, ix + 1) - else false - protected def companion = Connection - - /** Java API */ - def getTokens: Iterable[String] = tokens.asJava -} - -// http://tools.ietf.org/html/rfc7230#section-3.3.2 -object `Content-Length` extends ModeledCompanion[`Content-Length`] -/** - * Instances of this class will only be created transiently during header parsing and will never appear - * in HttpMessage.header. To access the Content-Length, see subclasses of HttpEntity. - */ -final case class `Content-Length` private[http] (length: Long) extends jm.headers.ContentLength - with RequestResponseHeader { - def renderValue[R <: Rendering](r: R): r.type = r ~~ length - protected def companion = `Content-Length` -} - -// http://tools.ietf.org/html/rfc6266 -object `Content-Disposition` extends ModeledCompanion[`Content-Disposition`] -final case class `Content-Disposition`(dispositionType: ContentDispositionType, params: Map[String, String] = Map.empty) - extends jm.headers.ContentDisposition with RequestResponseHeader { - def renderValue[R <: Rendering](r: R): r.type = { - r ~~ dispositionType - params foreach { case (k, v) ⇒ r ~~ "; " ~~ k ~~ '=' ~~# v } - r - } - protected def companion = `Content-Disposition` - - /** Java API */ - def getParams: util.Map[String, String] = params.asJava -} - -// http://tools.ietf.org/html/rfc7231#section-3.1.2.2 -object `Content-Encoding` extends ModeledCompanion[`Content-Encoding`] { - def apply(first: HttpEncoding, more: HttpEncoding*): `Content-Encoding` = apply(immutable.Seq(first +: more: _*)) - implicit val encodingsRenderer = Renderer.defaultSeqRenderer[HttpEncoding] // cache -} -final case class `Content-Encoding`(encodings: immutable.Seq[HttpEncoding]) extends jm.headers.ContentEncoding - with RequestResponseHeader { - require(encodings.nonEmpty, "encodings must not be empty") - import `Content-Encoding`.encodingsRenderer - def renderValue[R <: Rendering](r: R): r.type = r ~~ encodings - protected def companion = `Content-Encoding` - - /** Java API */ - def getEncodings: Iterable[jm.headers.HttpEncoding] = encodings.asJava -} - -// http://tools.ietf.org/html/rfc7233#section-4.2 -object `Content-Range` extends ModeledCompanion[`Content-Range`] { - def apply(byteContentRange: ByteContentRange): `Content-Range` = apply(RangeUnits.Bytes, byteContentRange) -} -final case class `Content-Range`(rangeUnit: RangeUnit, contentRange: ContentRange) extends jm.headers.ContentRange - with ResponseHeader { - def renderValue[R <: Rendering](r: R): r.type = r ~~ rangeUnit ~~ ' ' ~~ contentRange - protected def companion = `Content-Range` -} - -// http://tools.ietf.org/html/rfc7231#section-3.1.1.5 -object `Content-Type` extends ModeledCompanion[`Content-Type`] -/** - * Instances of this class will only be created transiently during header parsing and will never appear - * in HttpMessage.header. To access the Content-Type, see subclasses of HttpEntity. - */ -final case class `Content-Type` private[http] (contentType: ContentType) extends jm.headers.ContentType - with RequestResponseHeader { - def renderValue[R <: Rendering](r: R): r.type = r ~~ contentType - protected def companion = `Content-Type` -} - -// https://tools.ietf.org/html/rfc6265#section-4.2 -object Cookie extends ModeledCompanion[Cookie] { - def apply(first: HttpCookiePair, more: HttpCookiePair*): Cookie = apply(immutable.Seq(first +: more: _*)) - def apply(name: String, value: String): Cookie = apply(HttpCookiePair(name, value)) - def apply(values: (String, String)*): Cookie = apply(values.map(HttpCookiePair(_)).toList) - implicit val cookiePairsRenderer = Renderer.seqRenderer[HttpCookiePair](separator = "; ") // cache -} -final case class Cookie(cookies: immutable.Seq[HttpCookiePair]) extends jm.headers.Cookie with RequestHeader { - require(cookies.nonEmpty, "cookies must not be empty") - import Cookie.cookiePairsRenderer - def renderValue[R <: Rendering](r: R): r.type = r ~~ cookies - protected def companion = Cookie - - /** Java API */ - def getCookies: Iterable[jm.headers.HttpCookiePair] = cookies.asJava -} - -// http://tools.ietf.org/html/rfc7231#section-7.1.1.2 -object Date extends ModeledCompanion[Date] -final case class Date(date: DateTime) extends jm.headers.Date with RequestResponseHeader { - def renderValue[R <: Rendering](r: R): r.type = date.renderRfc1123DateTimeString(r) - protected def companion = Date -} - -/** - * INTERNAL API - */ -private[headers] object EmptyCompanion extends ModeledCompanion[EmptyHeader.type] -/** - * INTERNAL API - */ -private[http] object EmptyHeader extends SyntheticHeader { - def renderValue[R <: Rendering](r: R): r.type = r - protected def companion: ModeledCompanion[EmptyHeader.type] = EmptyCompanion -} - -// http://tools.ietf.org/html/rfc7232#section-2.3 -object ETag extends ModeledCompanion[ETag] { - def apply(tag: String, weak: Boolean = false): ETag = ETag(EntityTag(tag, weak)) - -} -final case class ETag(etag: EntityTag) extends jm.headers.ETag with ResponseHeader { - def renderValue[R <: Rendering](r: R): r.type = r ~~ etag - protected def companion = ETag -} - -// http://tools.ietf.org/html/rfc7231#section-5.1.1 -object Expect extends ModeledCompanion[Expect] { - val `100-continue` = new Expect() {} -} -sealed abstract case class Expect private () extends RequestHeader { - final def renderValue[R <: Rendering](r: R): r.type = r ~~ "100-continue" - protected def companion = Expect -} - -// http://tools.ietf.org/html/rfc7234#section-5.3 -object Expires extends ModeledCompanion[Expires] -final case class Expires(date: DateTime) extends jm.headers.Expires with ResponseHeader { - def renderValue[R <: Rendering](r: R): r.type = date.renderRfc1123DateTimeString(r) - protected def companion = Expires -} - -// http://tools.ietf.org/html/rfc7230#section-5.4 -object Host extends ModeledCompanion[Host] { - def apply(authority: Uri.Authority): Host = apply(authority.host, authority.port) - def apply(address: InetSocketAddress): Host = apply(address.getHostString, address.getPort) - def apply(host: String): Host = apply(host, 0) - def apply(host: String, port: Int): Host = apply(Uri.Host(host), port) - val empty = Host("") -} -final case class Host(host: Uri.Host, port: Int = 0) extends jm.headers.Host with RequestHeader { - import UriRendering.HostRenderer - require((port >> 16) == 0, "Illegal port: " + port) - def isEmpty = host.isEmpty - def renderValue[R <: Rendering](r: R): r.type = if (port > 0) r ~~ host ~~ ':' ~~ port else r ~~ host - protected def companion = Host - def equalsIgnoreCase(other: Host): Boolean = host.equalsIgnoreCase(other.host) && port == other.port -} - -// http://tools.ietf.org/html/rfc7232#section-3.1 -object `If-Match` extends ModeledCompanion[`If-Match`] { - val `*` = `If-Match`(EntityTagRange.`*`) - def apply(first: EntityTag, more: EntityTag*): `If-Match` = - `If-Match`(EntityTagRange(first +: more: _*)) -} -final case class `If-Match`(m: EntityTagRange) extends jm.headers.IfMatch with RequestHeader { - def renderValue[R <: Rendering](r: R): r.type = r ~~ m - protected def companion = `If-Match` -} - -// http://tools.ietf.org/html/rfc7232#section-3.3 -object `If-Modified-Since` extends ModeledCompanion[`If-Modified-Since`] -final case class `If-Modified-Since`(date: DateTime) extends jm.headers.IfModifiedSince with RequestHeader { - def renderValue[R <: Rendering](r: R): r.type = date.renderRfc1123DateTimeString(r) - protected def companion = `If-Modified-Since` -} - -// http://tools.ietf.org/html/rfc7232#section-3.2 -object `If-None-Match` extends ModeledCompanion[`If-None-Match`] { - val `*` = `If-None-Match`(EntityTagRange.`*`) - def apply(first: EntityTag, more: EntityTag*): `If-None-Match` = - `If-None-Match`(EntityTagRange(first +: more: _*)) -} -final case class `If-None-Match`(m: EntityTagRange) extends jm.headers.IfNoneMatch with RequestHeader { - def renderValue[R <: Rendering](r: R): r.type = r ~~ m - protected def companion = `If-None-Match` -} - -// http://tools.ietf.org/html/rfc7233#section-3.2 -object `If-Range` extends ModeledCompanion[`If-Range`] { - def apply(tag: EntityTag): `If-Range` = apply(Left(tag)) - def apply(timestamp: DateTime): `If-Range` = apply(Right(timestamp)) -} -final case class `If-Range`(entityTagOrDateTime: Either[EntityTag, DateTime]) extends RequestHeader { - def renderValue[R <: Rendering](r: R): r.type = - entityTagOrDateTime match { - case Left(tag) ⇒ r ~~ tag - case Right(dateTime) ⇒ dateTime.renderRfc1123DateTimeString(r) - } - protected def companion = `If-Range` -} - -// http://tools.ietf.org/html/rfc7232#section-3.4 -object `If-Unmodified-Since` extends ModeledCompanion[`If-Unmodified-Since`] -final case class `If-Unmodified-Since`(date: DateTime) extends jm.headers.IfUnmodifiedSince with RequestHeader { - def renderValue[R <: Rendering](r: R): r.type = date.renderRfc1123DateTimeString(r) - protected def companion = `If-Unmodified-Since` -} - -// http://tools.ietf.org/html/rfc7232#section-2.2 -object `Last-Modified` extends ModeledCompanion[`Last-Modified`] -final case class `Last-Modified`(date: DateTime) extends jm.headers.LastModified with ResponseHeader { - def renderValue[R <: Rendering](r: R): r.type = date.renderRfc1123DateTimeString(r) - protected def companion = `Last-Modified` -} - -// http://tools.ietf.org/html/rfc5988#section-5 -object Link extends ModeledCompanion[Link] { - def apply(uri: Uri, first: LinkParam, more: LinkParam*): Link = apply(immutable.Seq(LinkValue(uri, first +: more: _*))) - def apply(values: LinkValue*): Link = apply(immutable.Seq(values: _*)) - implicit val valuesRenderer = Renderer.defaultSeqRenderer[LinkValue] // cache -} -final case class Link(values: immutable.Seq[LinkValue]) extends jm.headers.Link with RequestResponseHeader { - import Link.valuesRenderer - def renderValue[R <: Rendering](r: R): r.type = r ~~ values - protected def companion = Link - - /** Java API */ - def getValues: Iterable[jm.headers.LinkValue] = values.asJava -} - -// http://tools.ietf.org/html/rfc7231#section-7.1.2 -object Location extends ModeledCompanion[Location] -final case class Location(uri: Uri) extends jm.headers.Location with ResponseHeader { - def renderValue[R <: Rendering](r: R): r.type = { import UriRendering.UriRenderer; r ~~ uri } - protected def companion = Location - - /** Java API */ - def getUri: akka.http.javadsl.model.Uri = uri.asJava -} - -// http://tools.ietf.org/html/rfc6454#section-7 -object Origin extends ModeledCompanion[Origin] { - def apply(origins: HttpOrigin*): Origin = apply(immutable.Seq(origins: _*)) -} -final case class Origin(origins: immutable.Seq[HttpOrigin]) extends jm.headers.Origin with RequestHeader { - def renderValue[R <: Rendering](r: R): r.type = if (origins.isEmpty) r ~~ "null" else r ~~ origins - protected def companion = Origin - - /** Java API */ - def getOrigins: Iterable[jm.headers.HttpOrigin] = origins.asJava -} - -// http://tools.ietf.org/html/rfc7235#section-4.3 -object `Proxy-Authenticate` extends ModeledCompanion[`Proxy-Authenticate`] { - def apply(first: HttpChallenge, more: HttpChallenge*): `Proxy-Authenticate` = apply(immutable.Seq(first +: more: _*)) - implicit val challengesRenderer = Renderer.defaultSeqRenderer[HttpChallenge] // cache -} -final case class `Proxy-Authenticate`(challenges: immutable.Seq[HttpChallenge]) extends jm.headers.ProxyAuthenticate - with ResponseHeader { - require(challenges.nonEmpty, "challenges must not be empty") - import `Proxy-Authenticate`.challengesRenderer - def renderValue[R <: Rendering](r: R): r.type = r ~~ challenges - protected def companion = `Proxy-Authenticate` - - /** Java API */ - def getChallenges: Iterable[jm.headers.HttpChallenge] = challenges.asJava -} - -// http://tools.ietf.org/html/rfc7235#section-4.4 -object `Proxy-Authorization` extends ModeledCompanion[`Proxy-Authorization`] -final case class `Proxy-Authorization`(credentials: HttpCredentials) extends jm.headers.ProxyAuthorization - with RequestHeader { - def renderValue[R <: Rendering](r: R): r.type = r ~~ credentials - protected def companion = `Proxy-Authorization` -} - -// http://tools.ietf.org/html/rfc7233#section-3.1 -object Range extends ModeledCompanion[Range] { - def apply(first: ByteRange, more: ByteRange*): Range = apply(immutable.Seq(first +: more: _*)) - def apply(ranges: immutable.Seq[ByteRange]): Range = Range(RangeUnits.Bytes, ranges) - implicit val rangesRenderer = Renderer.defaultSeqRenderer[ByteRange] // cache -} -final case class Range(rangeUnit: RangeUnit, ranges: immutable.Seq[ByteRange]) extends jm.headers.Range - with RequestHeader { - require(ranges.nonEmpty, "ranges must not be empty") - import Range.rangesRenderer - def renderValue[R <: Rendering](r: R): r.type = r ~~ rangeUnit ~~ '=' ~~ ranges - protected def companion = Range - - /** Java API */ - def getRanges: Iterable[jm.headers.ByteRange] = ranges.asJava -} - -final case class RawHeader(name: String, value: String) extends jm.headers.RawHeader { - def renderInRequests = true - def renderInResponses = true - val lowercaseName = name.toRootLowerCase - def render[R <: Rendering](r: R): r.type = r ~~ name ~~ ':' ~~ ' ' ~~ value -} -object RawHeader { - def unapply[H <: HttpHeader](customHeader: H): Option[(String, String)] = - Some(customHeader.name → customHeader.value) -} - -object `Raw-Request-URI` extends ModeledCompanion[`Raw-Request-URI`] -final case class `Raw-Request-URI`(uri: String) extends jm.headers.RawRequestURI with SyntheticHeader { - def renderValue[R <: Rendering](r: R): r.type = r ~~ uri - protected def companion = `Raw-Request-URI` -} - -object `Remote-Address` extends ModeledCompanion[`Remote-Address`] -final case class `Remote-Address`(address: RemoteAddress) extends jm.headers.RemoteAddress with SyntheticHeader { - def renderValue[R <: Rendering](r: R): r.type = r ~~ address - protected def companion = `Remote-Address` -} - -// http://tools.ietf.org/html/rfc7231#section-5.5.2 -object Referer extends ModeledCompanion[Referer] -final case class Referer(uri: Uri) extends jm.headers.Referer with RequestHeader { - require(uri.fragment.isEmpty, "Referer header URI must not contain a fragment") - require(uri.authority.userinfo.isEmpty, "Referer header URI must not contain a userinfo component") - - def renderValue[R <: Rendering](r: R): r.type = { import UriRendering.UriRenderer; r ~~ uri } - protected def companion = Referer - - /** Java API */ - def getUri: akka.http.javadsl.model.Uri = uri.asJava -} - -/** - * INTERNAL API - */ -// http://tools.ietf.org/html/rfc6455#section-4.3 -private[http] object `Sec-WebSocket-Accept` extends ModeledCompanion[`Sec-WebSocket-Accept`] { - // Defined at http://tools.ietf.org/html/rfc6455#section-4.2.2 - val MagicGuid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" - - /** Generates the matching accept header for this key */ - def forKey(key: `Sec-WebSocket-Key`): `Sec-WebSocket-Accept` = { - val sha1 = MessageDigest.getInstance("sha1") - val salted = key.key + MagicGuid - val hash = sha1.digest(salted.asciiBytes) - val acceptKey = Base64.rfc2045().encodeToString(hash, false) - `Sec-WebSocket-Accept`(acceptKey) - } -} -/** - * INTERNAL API - */ -private[http] final case class `Sec-WebSocket-Accept`(key: String) extends ResponseHeader { - protected[http] def renderValue[R <: Rendering](r: R): r.type = r ~~ key - - protected def companion = `Sec-WebSocket-Accept` -} - -/** - * INTERNAL API - */ -// http://tools.ietf.org/html/rfc6455#section-4.3 -private[http] object `Sec-WebSocket-Extensions` extends ModeledCompanion[`Sec-WebSocket-Extensions`] { - implicit val extensionsRenderer = Renderer.defaultSeqRenderer[WebSocketExtension] -} -/** - * INTERNAL API - */ -private[http] final case class `Sec-WebSocket-Extensions`(extensions: immutable.Seq[WebSocketExtension]) - extends ResponseHeader { - require(extensions.nonEmpty, "Sec-WebSocket-Extensions.extensions must not be empty") - import `Sec-WebSocket-Extensions`.extensionsRenderer - protected[http] def renderValue[R <: Rendering](r: R): r.type = r ~~ extensions - protected def companion = `Sec-WebSocket-Extensions` -} - -// http://tools.ietf.org/html/rfc6455#section-4.3 -/** - * INTERNAL API - */ -private[http] object `Sec-WebSocket-Key` extends ModeledCompanion[`Sec-WebSocket-Key`] { - def apply(keyBytes: Array[Byte]): `Sec-WebSocket-Key` = { - require(keyBytes.length == 16, s"Sec-WebSocket-Key keyBytes must have length 16 but had ${keyBytes.length}") - `Sec-WebSocket-Key`(Base64.rfc2045().encodeToString(keyBytes, false)) - } -} -/** - * INTERNAL API - */ -private[http] final case class `Sec-WebSocket-Key`(key: String) extends RequestHeader { - protected[http] def renderValue[R <: Rendering](r: R): r.type = r ~~ key - - protected def companion = `Sec-WebSocket-Key` - - /** - * Checks if the key value is valid according to the WebSocket specification, i.e. - * if the String is a Base64 representation of 16 bytes. - */ - def isValid: Boolean = Try(Base64.rfc2045().decode(key)).toOption.exists(_.length == 16) -} - -// http://tools.ietf.org/html/rfc6455#section-4.3 -/** - * INTERNAL API - */ -private[http] object `Sec-WebSocket-Protocol` extends ModeledCompanion[`Sec-WebSocket-Protocol`] { - implicit val protocolsRenderer = Renderer.defaultSeqRenderer[String] -} -/** - * INTERNAL API - */ -private[http] final case class `Sec-WebSocket-Protocol`(protocols: immutable.Seq[String]) - extends jm.headers.SecWebSocketProtocol with RequestResponseHeader { - require(protocols.nonEmpty, "Sec-WebSocket-Protocol.protocols must not be empty") - import `Sec-WebSocket-Protocol`.protocolsRenderer - protected[http] def renderValue[R <: Rendering](r: R): r.type = r ~~ protocols - protected def companion = `Sec-WebSocket-Protocol` - - /** Java API */ - override def getProtocols: Iterable[String] = protocols.asJava -} - -// http://tools.ietf.org/html/rfc6455#section-4.3 -/** - * INTERNAL API - */ -private[http] object `Sec-WebSocket-Version` extends ModeledCompanion[`Sec-WebSocket-Version`] { - implicit val versionsRenderer = Renderer.defaultSeqRenderer[Int] -} -/** - * INTERNAL API - */ -private[http] final case class `Sec-WebSocket-Version`(versions: immutable.Seq[Int]) - extends RequestResponseHeader { - require(versions.nonEmpty, "Sec-WebSocket-Version.versions must not be empty") - require(versions.forall(v ⇒ v >= 0 && v <= 255), s"Sec-WebSocket-Version.versions must be in the range 0 <= version <= 255 but were $versions") - import `Sec-WebSocket-Version`.versionsRenderer - protected[http] def renderValue[R <: Rendering](r: R): r.type = r ~~ versions - def hasVersion(versionNumber: Int): Boolean = versions contains versionNumber - protected def companion = `Sec-WebSocket-Version` -} - -// http://tools.ietf.org/html/rfc7231#section-7.4.2 -object Server extends ModeledCompanion[Server] { - def apply(products: String): Server = apply(ProductVersion.parseMultiple(products)) - def apply(first: ProductVersion, more: ProductVersion*): Server = apply(immutable.Seq(first +: more: _*)) - implicit val productsRenderer = Renderer.seqRenderer[ProductVersion](separator = " ") // cache -} -final case class Server(products: immutable.Seq[ProductVersion]) extends jm.headers.Server with ResponseHeader { - require(products.nonEmpty, "products must not be empty") - import Server.productsRenderer - def renderValue[R <: Rendering](r: R): r.type = r ~~ products - protected def companion = Server - - /** Java API */ - def getProducts: Iterable[jm.headers.ProductVersion] = products.asJava -} - -// https://tools.ietf.org/html/rfc6797 -object `Strict-Transport-Security` extends ModeledCompanion[`Strict-Transport-Security`] { - def apply(maxAge: Long, includeSubDomains: Option[Boolean]) = new `Strict-Transport-Security`(maxAge, includeSubDomains.getOrElse(false)) -} -final case class `Strict-Transport-Security`(maxAge: Long, includeSubDomains: Boolean = false) extends jm.headers.StrictTransportSecurity with ResponseHeader { - def renderValue[R <: Rendering](r: R): r.type = { - r ~~ "max-age=" ~~ maxAge - if (includeSubDomains) r ~~ "; includeSubDomains" - r - } - protected def companion = `Strict-Transport-Security` -} - -// https://tools.ietf.org/html/rfc6265 -object `Set-Cookie` extends ModeledCompanion[`Set-Cookie`] -final case class `Set-Cookie`(cookie: HttpCookie) extends jm.headers.SetCookie with ResponseHeader { - def renderValue[R <: Rendering](r: R): r.type = r ~~ cookie - protected def companion = `Set-Cookie` -} - -object `Timeout-Access` extends ModeledCompanion[`Timeout-Access`] -final case class `Timeout-Access`(timeoutAccess: akka.http.scaladsl.TimeoutAccess) - extends jm.headers.TimeoutAccess with SyntheticHeader { - def renderValue[R <: Rendering](r: R): r.type = r ~~ timeoutAccess.toString - protected def companion = `Timeout-Access` -} - -/** - * Model for the synthetic `Tls-Session-Info` header which carries the SSLSession of the connection - * the message carrying this header was received with. - * - * This header will only be added if it enabled in the configuration by setting - * - * ``` - * akka.http.[client|server].parsing.tls-session-info-header = on - * ``` - */ -object `Tls-Session-Info` extends ModeledCompanion[`Tls-Session-Info`] -final case class `Tls-Session-Info`(session: SSLSession) extends jm.headers.TlsSessionInfo with SyntheticHeader - with ScalaSessionAPI { - def renderValue[R <: Rendering](r: R): r.type = r ~~ session.toString - protected def companion = `Tls-Session-Info` - - /** Java API */ - def getSession: SSLSession = session -} - -// http://tools.ietf.org/html/rfc7230#section-3.3.1 -object `Transfer-Encoding` extends ModeledCompanion[`Transfer-Encoding`] { - def apply(first: TransferEncoding, more: TransferEncoding*): `Transfer-Encoding` = apply(immutable.Seq(first +: more: _*)) - implicit val encodingsRenderer = Renderer.defaultSeqRenderer[TransferEncoding] // cache -} -final case class `Transfer-Encoding`(encodings: immutable.Seq[TransferEncoding]) extends jm.headers.TransferEncoding - with RequestResponseHeader { - require(encodings.nonEmpty, "encodings must not be empty") - import `Transfer-Encoding`.encodingsRenderer - def isChunked: Boolean = encodings.last == TransferEncodings.chunked - def withChunked: `Transfer-Encoding` = if (isChunked) this else `Transfer-Encoding`(encodings :+ TransferEncodings.chunked) - def withChunkedPeeled: Option[`Transfer-Encoding`] = - if (isChunked) { - encodings.init match { - case Nil ⇒ None - case remaining ⇒ Some(`Transfer-Encoding`(remaining)) - } - } else Some(this) - def append(encodings: immutable.Seq[TransferEncoding]) = `Transfer-Encoding`(this.encodings ++ encodings) - def renderValue[R <: Rendering](r: R): r.type = r ~~ encodings - protected def companion = `Transfer-Encoding` - - /** Java API */ - def getEncodings: Iterable[jm.TransferEncoding] = encodings.asJava -} - -// http://tools.ietf.org/html/rfc7230#section-6.7 -object Upgrade extends ModeledCompanion[Upgrade] { - implicit val protocolsRenderer = Renderer.defaultSeqRenderer[UpgradeProtocol] -} -final case class Upgrade(protocols: immutable.Seq[UpgradeProtocol]) extends RequestResponseHeader { - import Upgrade.protocolsRenderer - protected[http] def renderValue[R <: Rendering](r: R): r.type = r ~~ protocols - - protected def companion = Upgrade - - def hasWebSocket: Boolean = protocols.exists(_.name equalsIgnoreCase "websocket") -} - -// http://tools.ietf.org/html/rfc7231#section-5.5.3 -object `User-Agent` extends ModeledCompanion[`User-Agent`] { - def apply(products: String): `User-Agent` = apply(ProductVersion.parseMultiple(products)) - def apply(first: ProductVersion, more: ProductVersion*): `User-Agent` = apply(immutable.Seq(first +: more: _*)) - implicit val productsRenderer = Renderer.seqRenderer[ProductVersion](separator = " ") // cache -} -final case class `User-Agent`(products: immutable.Seq[ProductVersion]) extends jm.headers.UserAgent with RequestHeader { - require(products.nonEmpty, "products must not be empty") - import `User-Agent`.productsRenderer - def renderValue[R <: Rendering](r: R): r.type = r ~~ products - protected def companion = `User-Agent` - - /** Java API */ - def getProducts: Iterable[jm.headers.ProductVersion] = products.asJava -} - -// http://tools.ietf.org/html/rfc7235#section-4.1 -object `WWW-Authenticate` extends ModeledCompanion[`WWW-Authenticate`] { - def apply(first: HttpChallenge, more: HttpChallenge*): `WWW-Authenticate` = apply(immutable.Seq(first +: more: _*)) - implicit val challengesRenderer = Renderer.defaultSeqRenderer[HttpChallenge] // cache -} -final case class `WWW-Authenticate`(challenges: immutable.Seq[HttpChallenge]) extends jm.headers.WWWAuthenticate - with ResponseHeader { - require(challenges.nonEmpty, "challenges must not be empty") - import `WWW-Authenticate`.challengesRenderer - def renderValue[R <: Rendering](r: R): r.type = r ~~ challenges - protected def companion = `WWW-Authenticate` - - /** Java API */ - def getChallenges: Iterable[jm.headers.HttpChallenge] = challenges.asJava -} - -// http://en.wikipedia.org/wiki/X-Forwarded-For -object `X-Forwarded-For` extends ModeledCompanion[`X-Forwarded-For`] { - def apply(first: RemoteAddress, more: RemoteAddress*): `X-Forwarded-For` = apply(immutable.Seq(first +: more: _*)) - implicit val addressesRenderer = Renderer.defaultSeqRenderer[RemoteAddress] // cache -} -final case class `X-Forwarded-For`(addresses: immutable.Seq[RemoteAddress]) extends jm.headers.XForwardedFor - with RequestHeader { - require(addresses.nonEmpty, "addresses must not be empty") - import `X-Forwarded-For`.addressesRenderer - def renderValue[R <: Rendering](r: R): r.type = r ~~ addresses - protected def companion = `X-Forwarded-For` - - /** Java API */ - def getAddresses: Iterable[jm.RemoteAddress] = addresses.asJava -} - -object `X-Real-Ip` extends ModeledCompanion[`X-Real-Ip`] -final case class `X-Real-Ip`(address: RemoteAddress) extends jm.headers.XRealIp - with RequestHeader { - def renderValue[R <: Rendering](r: R): r.type = r ~~ address - protected def companion = `X-Real-Ip` -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/package.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/package.scala deleted file mode 100644 index 3b2999d71d..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/package.scala +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl - -package object model { - /** An entity that can be used for every HttpMessage, i.e. for requests and responses. */ - type MessageEntity = RequestEntity -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/ws/Message.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/ws/Message.scala deleted file mode 100644 index c19fbc8adc..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/ws/Message.scala +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model.ws - -import akka.stream.javadsl -import akka.stream.scaladsl.Source -import akka.util.ByteString - -//#message-model -/** - * The ADT for WebSocket messages. A message can either be a binary or a text message. - */ -sealed trait Message extends akka.http.javadsl.model.ws.Message - -/** - * Represents a WebSocket text message. A text message can either be a [[TextMessage.Strict]] in which case - * the complete data is already available or it can be [[TextMessage.Streamed]] in which case `textStream` - * will return a Source streaming the data as it comes in. - */ -sealed trait TextMessage extends akka.http.javadsl.model.ws.TextMessage with Message { - /** - * The contents of this message as a stream. - */ - def textStream: Source[String, _] - - /** Java API */ - override def getStreamedText: javadsl.Source[String, _] = textStream.asJava - override def asScala: TextMessage = this -} -//#message-model -object TextMessage { - def apply(text: String): Strict = Strict(text) - def apply(textStream: Source[String, Any]): TextMessage = - Streamed(textStream) - - /** - * A strict [[TextMessage]] that contains the complete data as a [[String]]. - */ - final case class Strict(text: String) extends TextMessage { - def textStream: Source[String, _] = Source.single(text) - override def toString: String = s"TextMessage.Strict($text)" - - /** Java API */ - override def getStrictText: String = text - override def isStrict: Boolean = true - } - - final case class Streamed(textStream: Source[String, _]) extends TextMessage { - override def toString: String = s"TextMessage.Streamed($textStream)" - - /** Java API */ - override def getStrictText: String = throw new IllegalStateException("Cannot get strict text for streamed message.") - override def isStrict: Boolean = false - } -} - -/** - * Represents a WebSocket binary message. A binary message can either be [[BinaryMessage.Strict]] in which case - * the complete data is already available or it can be [[BinaryMessage.Streamed]] in which case `dataStream` - * will return a Source streaming the data as it comes in. - */ -//#message-model -sealed trait BinaryMessage extends akka.http.javadsl.model.ws.BinaryMessage with Message { - /** - * The contents of this message as a stream. - */ - def dataStream: Source[ByteString, _] - - /** Java API */ - override def getStreamedData: javadsl.Source[ByteString, _] = dataStream.asJava - override def asScala: BinaryMessage = this -} -//#message-model -object BinaryMessage { - def apply(data: ByteString): Strict = Strict(data) - def apply(dataStream: Source[ByteString, Any]): BinaryMessage = - Streamed(dataStream) - - /** - * A strict [[BinaryMessage]] that contains the complete data as a [[akka.util.ByteString]]. - */ - final case class Strict(data: ByteString) extends BinaryMessage { - def dataStream: Source[ByteString, _] = Source.single(data) - override def toString: String = s"BinaryMessage.Strict($data)" - - /** Java API */ - override def getStrictData: ByteString = data - override def isStrict: Boolean = true - } - final case class Streamed(dataStream: Source[ByteString, _]) extends BinaryMessage { - override def toString: String = s"BinaryMessage.Streamed($dataStream)" - - /** Java API */ - override def getStrictData: ByteString = throw new IllegalStateException("Cannot get strict data for streamed message.") - override def isStrict: Boolean = false - } -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/ws/PeerClosedConnectionException.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/ws/PeerClosedConnectionException.scala deleted file mode 100644 index 3d65d8db0e..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/ws/PeerClosedConnectionException.scala +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model.ws - -import akka.http.javadsl - -/** - * A PeerClosedConnectionException will be reported to the WebSocket handler if the peer has closed the connection. - * `closeCode` and `closeReason` contain close messages as reported by the peer. - */ -class PeerClosedConnectionException(val closeCode: Int, val closeReason: String) - extends RuntimeException(s"Peer closed connection with code $closeCode '$closeReason'") with javadsl.model.ws.PeerClosedConnectionException diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/ws/UpgradeToWebSocket.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/ws/UpgradeToWebSocket.scala deleted file mode 100644 index 1ebb912906..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/ws/UpgradeToWebSocket.scala +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model.ws - -import java.lang.Iterable -import scala.collection.immutable -import akka.NotUsed -import akka.stream._ -import akka.http.impl.util.JavaMapping -import akka.http.javadsl.{ model ⇒ jm } -import akka.http.scaladsl.model.HttpResponse - -/** - * A custom header that will be added to an WebSocket upgrade HttpRequest that - * enables a request handler to upgrade this connection to a WebSocket connection and - * registers a WebSocket handler. - */ -trait UpgradeToWebSocket extends jm.ws.UpgradeToWebSocket { - /** - * A sequence of protocols the client accepts. - * - * See http://tools.ietf.org/html/rfc6455#section-1.9 - */ - def requestedProtocols: immutable.Seq[String] - - /** - * The high-level interface to create a WebSocket server based on "messages". - * - * Returns a response to return in a request handler that will signal the - * low-level HTTP implementation to upgrade the connection to WebSocket and - * use the supplied handler to handle incoming WebSocket messages. - * - * Optionally, a subprotocol out of the ones requested by the client can be chosen. - */ - def handleMessages( - handlerFlow: Graph[FlowShape[Message, Message], Any], - subprotocol: Option[String] = None): HttpResponse - - /** - * The high-level interface to create a WebSocket server based on "messages". - * - * Returns a response to return in a request handler that will signal the - * low-level HTTP implementation to upgrade the connection to WebSocket and - * use the supplied inSink to consume messages received from the client and - * the supplied outSource to produce message to sent to the client. - * - * Optionally, a subprotocol out of the ones requested by the client can be chosen. - */ - def handleMessagesWithSinkSource( - inSink: Graph[SinkShape[Message], Any], - outSource: Graph[SourceShape[Message], Any], - subprotocol: Option[String] = None): HttpResponse = - handleMessages(scaladsl.Flow.fromSinkAndSource(inSink, outSource), subprotocol) - - import scala.collection.JavaConverters._ - - /** - * Java API - */ - def getRequestedProtocols(): Iterable[String] = requestedProtocols.asJava - - /** - * Java API - */ - def handleMessagesWith(handlerFlow: Graph[FlowShape[jm.ws.Message, jm.ws.Message], _ <: Any]): HttpResponse = - handleMessages(JavaMapping.toScala(handlerFlow)) - - /** - * Java API - */ - def handleMessagesWith(handlerFlow: Graph[FlowShape[jm.ws.Message, jm.ws.Message], _ <: Any], subprotocol: String): HttpResponse = - handleMessages(JavaMapping.toScala(handlerFlow), subprotocol = Some(subprotocol)) - - /** - * Java API - */ - def handleMessagesWith(inSink: Graph[SinkShape[jm.ws.Message], _ <: Any], outSource: Graph[SourceShape[jm.ws.Message], _ <: Any]): HttpResponse = - handleMessages(createScalaFlow(inSink, outSource)) - - /** - * Java API - */ - def handleMessagesWith( - inSink: Graph[SinkShape[jm.ws.Message], _ <: Any], - outSource: Graph[SourceShape[jm.ws.Message], _ <: Any], - subprotocol: String): HttpResponse = - handleMessages(createScalaFlow(inSink, outSource), subprotocol = Some(subprotocol)) - - private[this] def createScalaFlow(inSink: Graph[SinkShape[jm.ws.Message], _ <: Any], outSource: Graph[SourceShape[jm.ws.Message], _ <: Any]): Graph[FlowShape[Message, Message], NotUsed] = - JavaMapping.toScala(scaladsl.Flow.fromSinkAndSourceMat(inSink, outSource)(scaladsl.Keep.none): Graph[FlowShape[jm.ws.Message, jm.ws.Message], NotUsed]) -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/ws/WebSocketRequest.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/ws/WebSocketRequest.scala deleted file mode 100644 index 9a76e7f952..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/ws/WebSocketRequest.scala +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model.ws - -import scala.language.implicitConversions - -import scala.collection.immutable - -import akka.http.scaladsl.model.{ HttpHeader, Uri } - -/** - * Represents a WebSocket request. - * @param uri The target URI to connect to. - * @param extraHeaders Extra headers to add to the WebSocket request. - * @param subprotocol A WebSocket subprotocol if required. - */ -final case class WebSocketRequest( - uri: Uri, - extraHeaders: immutable.Seq[HttpHeader] = Nil, - subprotocol: Option[String] = None) -object WebSocketRequest { - implicit def fromTargetUri(uri: Uri): WebSocketRequest = WebSocketRequest(uri) - implicit def fromTargetUriString(uriString: String): WebSocketRequest = WebSocketRequest(uriString) -} \ No newline at end of file diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/ws/WebSocketUpgradeResponse.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/ws/WebSocketUpgradeResponse.scala deleted file mode 100644 index 18485e2a6e..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/ws/WebSocketUpgradeResponse.scala +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model.ws - -import akka.http.scaladsl.model.HttpResponse - -/** - * Represents the response to a websocket upgrade request. Can either be [[ValidUpgrade]] or [[InvalidUpgradeResponse]]. - */ -sealed trait WebSocketUpgradeResponse { - def response: HttpResponse -} -final case class ValidUpgrade(response: HttpResponse, chosenSubprotocol: Option[String]) extends WebSocketUpgradeResponse -final case class InvalidUpgradeResponse(response: HttpResponse, cause: String) extends WebSocketUpgradeResponse \ No newline at end of file diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/settings/ClientConnectionSettings.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/settings/ClientConnectionSettings.scala deleted file mode 100644 index 2ec6c8c576..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/settings/ClientConnectionSettings.scala +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright (C) 2016 Lightbend Inc. - */ -package akka.http.scaladsl.settings - -import java.lang.Iterable -import java.util.{ Optional, Random } -import java.util.function.Supplier - -import akka.http.impl.settings.ClientConnectionSettingsImpl -import akka.http.javadsl.model.headers.UserAgent -import akka.http.javadsl.{ settings ⇒ js } -import akka.http.scaladsl.model.headers.`User-Agent` -import akka.io.Inet.SocketOption -import com.typesafe.config.Config - -import scala.collection.immutable -import scala.compat.java8.OptionConverters -import scala.concurrent.duration.{ FiniteDuration, Duration } -import scala.collection.JavaConverters._ - -/** - * Public API but not intended for subclassing - */ -abstract class ClientConnectionSettings private[akka] () extends akka.http.javadsl.settings.ClientConnectionSettings { self: ClientConnectionSettingsImpl ⇒ - def userAgentHeader: Option[`User-Agent`] - def connectingTimeout: FiniteDuration - def idleTimeout: Duration - def requestHeaderSizeHint: Int - def websocketRandomFactory: () ⇒ Random - def socketOptions: immutable.Seq[SocketOption] - def parserSettings: ParserSettings - - /* JAVA APIs */ - - final override def getConnectingTimeout: FiniteDuration = connectingTimeout - final override def getParserSettings: js.ParserSettings = parserSettings - final override def getIdleTimeout: Duration = idleTimeout - final override def getSocketOptions: Iterable[SocketOption] = socketOptions.asJava - final override def getUserAgentHeader: Optional[UserAgent] = OptionConverters.toJava(userAgentHeader) - final override def getRequestHeaderSizeHint: Int = requestHeaderSizeHint - final override def getWebsocketRandomFactory: Supplier[Random] = new Supplier[Random] { - override def get(): Random = websocketRandomFactory() - } - - // --- - - // overrides for more specific return type - override def withConnectingTimeout(newValue: FiniteDuration): ClientConnectionSettings = self.copy(connectingTimeout = newValue) - override def withIdleTimeout(newValue: Duration): ClientConnectionSettings = self.copy(idleTimeout = newValue) - override def withRequestHeaderSizeHint(newValue: Int): ClientConnectionSettings = self.copy(requestHeaderSizeHint = newValue) - - // overloads for idiomatic Scala use - def withWebsocketRandomFactory(newValue: () ⇒ Random): ClientConnectionSettings = self.copy(websocketRandomFactory = newValue) - def withUserAgentHeader(newValue: Option[`User-Agent`]): ClientConnectionSettings = self.copy(userAgentHeader = newValue) - def withSocketOptions(newValue: immutable.Seq[SocketOption]): ClientConnectionSettings = self.copy(socketOptions = newValue) - def withParserSettings(newValue: ParserSettings): ClientConnectionSettings = self.copy(parserSettings = newValue) -} - -object ClientConnectionSettings extends SettingsCompanion[ClientConnectionSettings] { - override def apply(config: Config): ClientConnectionSettings = ClientConnectionSettingsImpl(config) - override def apply(configOverrides: String): ClientConnectionSettings = ClientConnectionSettingsImpl(configOverrides) -} \ No newline at end of file diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/settings/ConnectionPoolSettings.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/settings/ConnectionPoolSettings.scala deleted file mode 100644 index 9607594699..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/settings/ConnectionPoolSettings.scala +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright (C) 2016 Lightbend Inc. - */ -package akka.http.scaladsl.settings - -import akka.http.impl.settings.ConnectionPoolSettingsImpl -import akka.http.javadsl.{ settings ⇒ js } -import com.typesafe.config.Config - -import scala.concurrent.duration.Duration - -/** - * Public API but not intended for subclassing - */ -abstract class ConnectionPoolSettings extends js.ConnectionPoolSettings { self: ConnectionPoolSettingsImpl ⇒ - def maxConnections: Int - def minConnections: Int - def maxRetries: Int - def maxOpenRequests: Int - def pipeliningLimit: Int - def idleTimeout: Duration - def connectionSettings: ClientConnectionSettings - - /* JAVA APIs */ - - final override def getConnectionSettings: js.ClientConnectionSettings = connectionSettings - final override def getPipeliningLimit: Int = pipeliningLimit - final override def getIdleTimeout: Duration = idleTimeout - final override def getMaxConnections: Int = maxConnections - final override def getMinConnections: Int = minConnections - final override def getMaxOpenRequests: Int = maxOpenRequests - final override def getMaxRetries: Int = maxRetries - - // --- - - // overrides for more precise return type - override def withMaxConnections(n: Int): ConnectionPoolSettings = self.copy(maxConnections = n) - override def withMaxRetries(n: Int): ConnectionPoolSettings = self.copy(maxRetries = n) - override def withMaxOpenRequests(newValue: Int): ConnectionPoolSettings = self.copy(maxOpenRequests = newValue) - override def withPipeliningLimit(newValue: Int): ConnectionPoolSettings = self.copy(pipeliningLimit = newValue) - override def withIdleTimeout(newValue: Duration): ConnectionPoolSettings = self.copy(idleTimeout = newValue) - - // overloads for idiomatic Scala use - def withConnectionSettings(newValue: ClientConnectionSettings): ConnectionPoolSettings = self.copy(connectionSettings = newValue) -} - -object ConnectionPoolSettings extends SettingsCompanion[ConnectionPoolSettings] { - override def apply(config: Config) = ConnectionPoolSettingsImpl(config) - override def apply(configOverrides: String) = ConnectionPoolSettingsImpl(configOverrides) -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/settings/ParserSettings.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/settings/ParserSettings.scala deleted file mode 100644 index 97dd767d49..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/settings/ParserSettings.scala +++ /dev/null @@ -1,153 +0,0 @@ -/** - * Copyright (C) 2016 Lightbend Inc. - */ -package akka.http.scaladsl.settings - -import java.util -import java.util.Optional -import java.util.function.Function - -import akka.http.impl.settings.ParserSettingsImpl -import akka.http.impl.util._ -import akka.http.javadsl.model -import akka.http.scaladsl.model._ -import akka.http.scaladsl.{ settings ⇒ js } -import com.typesafe.config.Config - -import scala.collection.JavaConverters._ -import scala.compat.java8.OptionConverters - -/** - * Public API but not intended for subclassing - */ -abstract class ParserSettings private[akka] () extends akka.http.javadsl.settings.ParserSettings { self: ParserSettingsImpl ⇒ - def maxUriLength: Int - def maxMethodLength: Int - def maxResponseReasonLength: Int - def maxHeaderNameLength: Int - def maxHeaderValueLength: Int - def maxHeaderCount: Int - def maxContentLength: Long - def maxChunkExtLength: Int - def maxChunkSize: Int - def uriParsingMode: Uri.ParsingMode - def cookieParsingMode: ParserSettings.CookieParsingMode - def illegalHeaderWarnings: Boolean - def errorLoggingVerbosity: ParserSettings.ErrorLoggingVerbosity - def illegalResponseHeaderValueProcessingMode: ParserSettings.IllegalResponseHeaderValueProcessingMode - def headerValueCacheLimits: Map[String, Int] - def includeTlsSessionInfoHeader: Boolean - def customMethods: String ⇒ Option[HttpMethod] - def customStatusCodes: Int ⇒ Option[StatusCode] - def customMediaTypes: MediaTypes.FindCustom - - /* Java APIs */ - override def getCookieParsingMode: js.ParserSettings.CookieParsingMode = cookieParsingMode - override def getHeaderValueCacheLimits: util.Map[String, Int] = headerValueCacheLimits.asJava - override def getMaxChunkExtLength = maxChunkExtLength - override def getUriParsingMode: akka.http.javadsl.model.Uri.ParsingMode = uriParsingMode - override def getMaxHeaderCount = maxHeaderCount - override def getMaxContentLength = maxContentLength - override def getMaxHeaderValueLength = maxHeaderValueLength - override def getIncludeTlsSessionInfoHeader = includeTlsSessionInfoHeader - override def getIllegalHeaderWarnings = illegalHeaderWarnings - override def getMaxHeaderNameLength = maxHeaderNameLength - override def getMaxChunkSize = maxChunkSize - override def getMaxResponseReasonLength = maxResponseReasonLength - override def getMaxUriLength = maxUriLength - override def getMaxMethodLength = maxMethodLength - override def getErrorLoggingVerbosity: js.ParserSettings.ErrorLoggingVerbosity = errorLoggingVerbosity - override def getIllegalResponseHeaderValueProcessingMode = illegalResponseHeaderValueProcessingMode - - override def getCustomMethods = new Function[String, Optional[akka.http.javadsl.model.HttpMethod]] { - override def apply(t: String) = OptionConverters.toJava(customMethods(t)) - } - override def getCustomStatusCodes = new Function[Int, Optional[akka.http.javadsl.model.StatusCode]] { - override def apply(t: Int) = OptionConverters.toJava(customStatusCodes(t)) - } - override def getCustomMediaTypes = new akka.japi.function.Function2[String, String, Optional[akka.http.javadsl.model.MediaType]] { - override def apply(mainType: String, subType: String): Optional[model.MediaType] = - OptionConverters.toJava(customMediaTypes(mainType, subType)) - } - - // --- - - // override for more specific return type - override def withMaxUriLength(newValue: Int): ParserSettings = self.copy(maxUriLength = newValue) - override def withMaxMethodLength(newValue: Int): ParserSettings = self.copy(maxMethodLength = newValue) - override def withMaxResponseReasonLength(newValue: Int): ParserSettings = self.copy(maxResponseReasonLength = newValue) - override def withMaxHeaderNameLength(newValue: Int): ParserSettings = self.copy(maxHeaderNameLength = newValue) - override def withMaxHeaderValueLength(newValue: Int): ParserSettings = self.copy(maxHeaderValueLength = newValue) - override def withMaxHeaderCount(newValue: Int): ParserSettings = self.copy(maxHeaderCount = newValue) - override def withMaxContentLength(newValue: Long): ParserSettings = self.copy(maxContentLength = newValue) - override def withMaxChunkExtLength(newValue: Int): ParserSettings = self.copy(maxChunkExtLength = newValue) - override def withMaxChunkSize(newValue: Int): ParserSettings = self.copy(maxChunkSize = newValue) - override def withIllegalHeaderWarnings(newValue: Boolean): ParserSettings = self.copy(illegalHeaderWarnings = newValue) - override def withIncludeTlsSessionInfoHeader(newValue: Boolean): ParserSettings = self.copy(includeTlsSessionInfoHeader = newValue) - - // overloads for idiomatic Scala use - def withUriParsingMode(newValue: Uri.ParsingMode): ParserSettings = self.copy(uriParsingMode = newValue) - def withCookieParsingMode(newValue: ParserSettings.CookieParsingMode): ParserSettings = self.copy(cookieParsingMode = newValue) - def withErrorLoggingVerbosity(newValue: ParserSettings.ErrorLoggingVerbosity): ParserSettings = self.copy(errorLoggingVerbosity = newValue) - def withHeaderValueCacheLimits(newValue: Map[String, Int]): ParserSettings = self.copy(headerValueCacheLimits = newValue) - def withCustomMethods(methods: HttpMethod*): ParserSettings = { - val map = methods.map(m ⇒ m.name → m).toMap - self.copy(customMethods = map.get) - } - def withCustomStatusCodes(codes: StatusCode*): ParserSettings = { - val map = codes.map(c ⇒ c.intValue → c).toMap - self.copy(customStatusCodes = map.get) - } - def withCustomMediaTypes(types: MediaType*): ParserSettings = { - val map = types.map(c ⇒ (c.mainType, c.subType) → c).toMap - self.copy(customMediaTypes = (main, sub) ⇒ map.get((main, sub))) - } - def withIllegalResponseHeaderValueProcessingMode(newValue: ParserSettings.IllegalResponseHeaderValueProcessingMode): ParserSettings = - self.copy(illegalResponseHeaderValueProcessingMode = newValue) -} - -object ParserSettings extends SettingsCompanion[ParserSettings] { - sealed trait CookieParsingMode extends akka.http.javadsl.settings.ParserSettings.CookieParsingMode - object CookieParsingMode { - case object RFC6265 extends CookieParsingMode - case object Raw extends CookieParsingMode - - def apply(mode: String): CookieParsingMode = mode.toRootLowerCase match { - case "rfc6265" ⇒ RFC6265 - case "raw" ⇒ Raw - } - } - - sealed trait ErrorLoggingVerbosity extends akka.http.javadsl.settings.ParserSettings.ErrorLoggingVerbosity - object ErrorLoggingVerbosity { - case object Off extends ErrorLoggingVerbosity - case object Simple extends ErrorLoggingVerbosity - case object Full extends ErrorLoggingVerbosity - - def apply(string: String): ErrorLoggingVerbosity = - string.toRootLowerCase match { - case "off" ⇒ Off - case "simple" ⇒ Simple - case "full" ⇒ Full - case x ⇒ throw new IllegalArgumentException(s"[$x] is not a legal `error-logging-verbosity` setting") - } - } - - sealed trait IllegalResponseHeaderValueProcessingMode extends akka.http.javadsl.settings.ParserSettings.IllegalResponseHeaderValueProcessingMode - object IllegalResponseHeaderValueProcessingMode { - case object Error extends IllegalResponseHeaderValueProcessingMode - case object Warn extends IllegalResponseHeaderValueProcessingMode - case object Ignore extends IllegalResponseHeaderValueProcessingMode - - def apply(string: String): IllegalResponseHeaderValueProcessingMode = - string.toRootLowerCase match { - case "error" ⇒ Error - case "warn" ⇒ Warn - case "ignore" ⇒ Ignore - case x ⇒ throw new IllegalArgumentException(s"[$x] is not a legal `illegal-response-header-value-processing-mode` setting") - } - } - - override def apply(config: Config): ParserSettings = ParserSettingsImpl(config) - override def apply(configOverrides: String): ParserSettings = ParserSettingsImpl(configOverrides) -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/settings/RoutingSettings.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/settings/RoutingSettings.scala deleted file mode 100644 index 50a3a576ac..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/settings/RoutingSettings.scala +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2016 Lightbend Inc. - */ -package akka.http.scaladsl.settings - -import akka.http.impl.settings.RoutingSettingsImpl -import com.typesafe.config.Config - -/** - * Public API but not intended for subclassing - */ -abstract class RoutingSettings private[akka] () extends akka.http.javadsl.settings.RoutingSettings { self: RoutingSettingsImpl ⇒ - def verboseErrorMessages: Boolean - def fileGetConditional: Boolean - def renderVanityFooter: Boolean - def rangeCountLimit: Int - def rangeCoalescingThreshold: Long - def decodeMaxBytesPerChunk: Int - def fileIODispatcher: String - - /* Java APIs */ - def getVerboseErrorMessages: Boolean = verboseErrorMessages - def getFileGetConditional: Boolean = fileGetConditional - def getRenderVanityFooter: Boolean = renderVanityFooter - def getRangeCountLimit: Int = rangeCountLimit - def getRangeCoalescingThreshold: Long = rangeCoalescingThreshold - def getDecodeMaxBytesPerChunk: Int = decodeMaxBytesPerChunk - def getFileIODispatcher: String = fileIODispatcher - - override def withVerboseErrorMessages(verboseErrorMessages: Boolean): RoutingSettings = self.copy(verboseErrorMessages = verboseErrorMessages) - override def withFileGetConditional(fileGetConditional: Boolean): RoutingSettings = self.copy(fileGetConditional = fileGetConditional) - override def withRenderVanityFooter(renderVanityFooter: Boolean): RoutingSettings = self.copy(renderVanityFooter = renderVanityFooter) - override def withRangeCountLimit(rangeCountLimit: Int): RoutingSettings = self.copy(rangeCountLimit = rangeCountLimit) - override def withRangeCoalescingThreshold(rangeCoalescingThreshold: Long): RoutingSettings = self.copy(rangeCoalescingThreshold = rangeCoalescingThreshold) - override def withDecodeMaxBytesPerChunk(decodeMaxBytesPerChunk: Int): RoutingSettings = self.copy(decodeMaxBytesPerChunk = decodeMaxBytesPerChunk) - override def withFileIODispatcher(fileIODispatcher: String): RoutingSettings = self.copy(fileIODispatcher = fileIODispatcher) - -} - -object RoutingSettings extends SettingsCompanion[RoutingSettings] { - override def apply(config: Config): RoutingSettings = RoutingSettingsImpl(config) - override def apply(configOverrides: String): RoutingSettings = RoutingSettingsImpl(configOverrides) -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/settings/ServerSettings.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/settings/ServerSettings.scala deleted file mode 100644 index 9ee3a3cff6..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/settings/ServerSettings.scala +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Copyright (C) 2016 Lightbend Inc. - */ -package akka.http.scaladsl.settings - -import java.util.Random -import java.util.function.Supplier - -import akka.http.impl.settings.ServerSettingsImpl -import akka.http.impl.util.JavaMapping.Implicits._ -import akka.http.javadsl.{ settings ⇒ js } -import akka.http.scaladsl.model.headers.Host -import akka.http.scaladsl.model.headers.Server -import akka.io.Inet.SocketOption -import com.typesafe.config.Config - -import scala.collection.JavaConverters._ -import scala.collection.immutable -import scala.compat.java8.OptionConverters -import scala.concurrent.duration.{ FiniteDuration, Duration } -import scala.language.implicitConversions - -/** - * Public API but not intended for subclassing - */ -abstract class ServerSettings private[akka] () extends akka.http.javadsl.settings.ServerSettings { self: ServerSettingsImpl ⇒ - def serverHeader: Option[Server] - def timeouts: ServerSettings.Timeouts - def maxConnections: Int - def pipeliningLimit: Int - def remoteAddressHeader: Boolean - def rawRequestUriHeader: Boolean - def transparentHeadRequests: Boolean - def verboseErrorMessages: Boolean - def responseHeaderSizeHint: Int - def backlog: Int - def socketOptions: immutable.Seq[SocketOption] - def defaultHostHeader: Host - def websocketRandomFactory: () ⇒ Random - def parserSettings: ParserSettings - - /* Java APIs */ - - override def getBacklog = backlog - override def getDefaultHostHeader = defaultHostHeader.asJava - override def getPipeliningLimit = pipeliningLimit - override def getParserSettings: js.ParserSettings = parserSettings - override def getMaxConnections = maxConnections - override def getTransparentHeadRequests = transparentHeadRequests - override def getResponseHeaderSizeHint = responseHeaderSizeHint - override def getVerboseErrorMessages = verboseErrorMessages - override def getSocketOptions = socketOptions.asJava - override def getServerHeader = OptionConverters.toJava(serverHeader.map(_.asJava)) - override def getTimeouts = timeouts - override def getRawRequestUriHeader = rawRequestUriHeader - override def getRemoteAddressHeader = remoteAddressHeader - override def getWebsocketRandomFactory = new Supplier[Random] { - override def get(): Random = websocketRandomFactory() - } - - // --- - - // override for more specific return type - override def withMaxConnections(newValue: Int): ServerSettings = self.copy(maxConnections = newValue) - override def withPipeliningLimit(newValue: Int): ServerSettings = self.copy(pipeliningLimit = newValue) - override def withRemoteAddressHeader(newValue: Boolean): ServerSettings = self.copy(remoteAddressHeader = newValue) - override def withRawRequestUriHeader(newValue: Boolean): ServerSettings = self.copy(rawRequestUriHeader = newValue) - override def withTransparentHeadRequests(newValue: Boolean): ServerSettings = self.copy(transparentHeadRequests = newValue) - override def withVerboseErrorMessages(newValue: Boolean): ServerSettings = self.copy(verboseErrorMessages = newValue) - override def withResponseHeaderSizeHint(newValue: Int): ServerSettings = self.copy(responseHeaderSizeHint = newValue) - override def withBacklog(newValue: Int): ServerSettings = self.copy(backlog = newValue) - override def withSocketOptions(newValue: java.lang.Iterable[SocketOption]): ServerSettings = self.copy(socketOptions = newValue.asScala.toList) - override def withWebsocketRandomFactory(newValue: java.util.function.Supplier[Random]): ServerSettings = self.copy(websocketRandomFactory = () ⇒ newValue.get()) - - // overloads for Scala idiomatic use - def withTimeouts(newValue: ServerSettings.Timeouts): ServerSettings = self.copy(timeouts = newValue) - def withServerHeader(newValue: Option[Server]): ServerSettings = self.copy(serverHeader = newValue) - def withDefaultHostHeader(newValue: Host): ServerSettings = self.copy(defaultHostHeader = newValue) - def withParserSettings(newValue: ParserSettings): ServerSettings = self.copy(parserSettings = newValue) - def withWebsocketRandomFactory(newValue: () ⇒ Random): ServerSettings = self.copy(websocketRandomFactory = newValue) - def withSocketOptions(newValue: immutable.Seq[SocketOption]): ServerSettings = self.copy(socketOptions = newValue) - -} - -object ServerSettings extends SettingsCompanion[ServerSettings] { - trait Timeouts extends akka.http.javadsl.settings.ServerSettings.Timeouts { - // --- - // override for more specific return types - override def withIdleTimeout(newValue: Duration): ServerSettings.Timeouts = self.copy(idleTimeout = newValue) - override def withRequestTimeout(newValue: Duration): ServerSettings.Timeouts = self.copy(requestTimeout = newValue) - override def withBindTimeout(newValue: FiniteDuration): ServerSettings.Timeouts = self.copy(bindTimeout = newValue) - } - - implicit def timeoutsShortcut(s: ServerSettings): Timeouts = s.timeouts - - override def apply(config: Config): ServerSettings = ServerSettingsImpl(config) - override def apply(configOverrides: String): ServerSettings = ServerSettingsImpl(configOverrides) -} \ No newline at end of file diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/settings/SettingsCompanion.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/settings/SettingsCompanion.scala deleted file mode 100644 index 8157eaeb89..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/settings/SettingsCompanion.scala +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.scaladsl.settings - -import akka.actor.{ ActorRefFactory, ActorSystem } -import com.typesafe.config.Config -import akka.http.impl.util._ - -/** INTERNAL API */ -private[akka] trait SettingsCompanion[T] { - - /** - * Creates an instance of settings using the configuration provided by the given ActorSystem. - */ - final def apply(system: ActorSystem): T = apply(system.settings.config) - implicit def default(implicit system: ActorRefFactory): T = apply(actorSystem) - - /** - * Creates an instance of settings using the given Config. - */ - def apply(config: Config): T - - /** - * Create an instance of settings using the given String of config overrides to override - * settings set in the class loader of this class (i.e. by application.conf or reference.conf files in - * the class loader of this class). - */ - def apply(configOverrides: String): T -} diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/util/FastFuture.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/util/FastFuture.scala deleted file mode 100644 index 2c8abddb16..0000000000 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/util/FastFuture.scala +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.util - -import scala.language.{ higherKinds } -import scala.util.control.NonFatal -import scala.util.{ Failure, Success, Try } -import scala.collection.generic.CanBuildFrom -import scala.concurrent.duration.Duration -import scala.concurrent._ - -/** - * Provides alternative implementations of the basic transformation operations defined on [[scala.concurrent.Future]], - * which try to avoid scheduling to an [[scala.concurrent.ExecutionContext]] if possible, i.e. if the given future - * value is already present. - */ -class FastFuture[A](val future: Future[A]) extends AnyVal { - import FastFuture._ - - def map[B](f: A ⇒ B)(implicit ec: ExecutionContext): Future[B] = - transformWith(a ⇒ FastFuture.successful(f(a)), FastFuture.failed) - - def flatMap[B](f: A ⇒ Future[B])(implicit ec: ExecutionContext): Future[B] = - transformWith(f, FastFuture.failed) - - def filter(pred: A ⇒ Boolean)(implicit executor: ExecutionContext): Future[A] = - flatMap { r ⇒ - if (pred(r)) future - else throw new NoSuchElementException("Future.filter predicate is not satisfied") // FIXME: avoid stack trace generation - } - - def foreach(f: A ⇒ Unit)(implicit ec: ExecutionContext): Unit = map(f) - - def transformWith[B](f: Try[A] ⇒ Future[B])(implicit executor: ExecutionContext): Future[B] = - transformWith(a ⇒ f(Success(a)), e ⇒ f(Failure(e))) - - def transformWith[B](s: A ⇒ Future[B], f: Throwable ⇒ Future[B])(implicit executor: ExecutionContext): Future[B] = { - def strictTransform[T](x: T, f: T ⇒ Future[B]) = - try f(x) - catch { case NonFatal(e) ⇒ ErrorFuture(e) } - - future match { - case FulfilledFuture(a) ⇒ strictTransform(a, s) - case ErrorFuture(e) ⇒ strictTransform(e, f) - case _ ⇒ future.value match { - case None ⇒ - val p = Promise[B]() - future.onComplete { - case Success(a) ⇒ p completeWith strictTransform(a, s) - case Failure(e) ⇒ p completeWith strictTransform(e, f) - } - p.future - case Some(Success(a)) ⇒ strictTransform(a, s) - case Some(Failure(e)) ⇒ strictTransform(e, f) - } - } - } - - def recover[B >: A](pf: PartialFunction[Throwable, B])(implicit ec: ExecutionContext): Future[B] = - transformWith(FastFuture.successful, t ⇒ if (pf isDefinedAt t) FastFuture.successful(pf(t)) else future) - - def recoverWith[B >: A](pf: PartialFunction[Throwable, Future[B]])(implicit ec: ExecutionContext): Future[B] = - transformWith(FastFuture.successful, t ⇒ pf.applyOrElse(t, (_: Throwable) ⇒ future)) -} - -object FastFuture { - def apply[T](value: Try[T]): Future[T] = value match { - case Success(t) ⇒ FulfilledFuture(t) - case Failure(e) ⇒ ErrorFuture(e) - } - private[this] val _successful: Any ⇒ Future[Any] = FulfilledFuture.apply - def successful[T]: T ⇒ Future[T] = _successful.asInstanceOf[T ⇒ Future[T]] - val failed: Throwable ⇒ Future[Nothing] = ErrorFuture.apply - - private case class FulfilledFuture[+A](a: A) extends Future[A] { - def value = Some(Success(a)) - def onComplete[U](f: Try[A] ⇒ U)(implicit executor: ExecutionContext) = Future.successful(a).onComplete(f) - def isCompleted = true - def result(atMost: Duration)(implicit permit: CanAwait) = a - def ready(atMost: Duration)(implicit permit: CanAwait) = this - def transform[S](f: scala.util.Try[A] ⇒ scala.util.Try[S])(implicit executor: scala.concurrent.ExecutionContext): scala.concurrent.Future[S] = - FastFuture(f(Success(a))) - def transformWith[S](f: scala.util.Try[A] ⇒ scala.concurrent.Future[S])(implicit executor: scala.concurrent.ExecutionContext): scala.concurrent.Future[S] = - new FastFuture(this).transformWith(f) - } - private case class ErrorFuture(error: Throwable) extends Future[Nothing] { - def value = Some(Failure(error)) - def onComplete[U](f: Try[Nothing] ⇒ U)(implicit executor: ExecutionContext) = Future.failed(error).onComplete(f) - def isCompleted = true - def result(atMost: Duration)(implicit permit: CanAwait) = throw error - def ready(atMost: Duration)(implicit permit: CanAwait) = this - def transform[S](f: scala.util.Try[Nothing] ⇒ scala.util.Try[S])(implicit executor: scala.concurrent.ExecutionContext): scala.concurrent.Future[S] = - FastFuture(f(Failure(error))) - def transformWith[S](f: scala.util.Try[Nothing] ⇒ scala.concurrent.Future[S])(implicit executor: scala.concurrent.ExecutionContext): scala.concurrent.Future[S] = - new FastFuture(this).transformWith(f) - } - - implicit class EnhancedFuture[T](val future: Future[T]) extends AnyVal { - def fast: FastFuture[T] = new FastFuture[T](future) - } - - def sequence[T, M[_] <: TraversableOnce[_]](in: M[Future[T]])(implicit cbf: CanBuildFrom[M[Future[T]], T, M[T]], executor: ExecutionContext): Future[M[T]] = - in.foldLeft(successful(cbf(in))) { - (fr, fa) ⇒ for (r ← fr.fast; a ← fa.asInstanceOf[Future[T]].fast) yield r += a - }.fast.map(_.result()) - - def fold[T, R](futures: TraversableOnce[Future[T]])(zero: R)(f: (R, T) ⇒ R)(implicit executor: ExecutionContext): Future[R] = - if (futures.isEmpty) successful(zero) - else sequence(futures).fast.map(_.foldLeft(zero)(f)) - - def reduce[T, R >: T](futures: TraversableOnce[Future[T]])(op: (R, T) ⇒ R)(implicit executor: ExecutionContext): Future[R] = - if (futures.isEmpty) failed(new NoSuchElementException("reduce attempted on empty collection")) - else sequence(futures).fast.map(_ reduceLeft op) - - def traverse[A, B, M[_] <: TraversableOnce[_]](in: M[A])(fn: A ⇒ Future[B])(implicit cbf: CanBuildFrom[M[A], B, M[B]], executor: ExecutionContext): Future[M[B]] = - in.foldLeft(successful(cbf(in))) { (fr, a) ⇒ - val fb = fn(a.asInstanceOf[A]) - for (r ← fr.fast; b ← fb.fast) yield r += b - }.fast.map(_.result()) -} diff --git a/akka-http-core/src/test/java/akka/http/JavaTestServer.java b/akka-http-core/src/test/java/akka/http/JavaTestServer.java deleted file mode 100644 index 4b32285684..0000000000 --- a/akka-http-core/src/test/java/akka/http/JavaTestServer.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http; - -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.JavaApiTestCases; -import akka.http.javadsl.model.ws.Message; -import akka.http.javadsl.model.ws.TextMessage; -import akka.http.javadsl.model.ws.WebSocket; -import akka.japi.JavaPartialFunction; -import akka.stream.ActorMaterializer; -import akka.stream.Materializer; -import akka.stream.javadsl.Flow; -import akka.stream.javadsl.Source; - -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.util.concurrent.CompletionStage; -import java.util.concurrent.TimeUnit; - -public class JavaTestServer { - public static void main(String[] args) throws Exception { - ActorSystem system = ActorSystem.create(); - - try { - final Materializer materializer = ActorMaterializer.create(system); - - CompletionStage serverBindingFuture = - Http.get(system).bindAndHandleSync( - request -> { - System.out.println("Handling request to " + request.getUri()); - - if (request.getUri().path().equals("/")) - return WebSocket.handleWebSocketRequestWith(request, echoMessages()); - else if (request.getUri().path().equals("/greeter")) - return WebSocket.handleWebSocketRequestWith(request, greeter()); - else - return JavaApiTestCases.handleRequest(request); - }, ConnectHttp.toHost("localhost", 8080), materializer); - - 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 Flow echoMessages() { - return Flow.create(); // the identity operation - } - - public static Flow greeter() { - return - Flow.create() - .collect(new JavaPartialFunction() { - @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()) - return TextMessage.create("Hello "+msg.getStrictText()); - else - return TextMessage.create(Source.single("Hello ").concat(msg.getStreamedText())); - } -} diff --git a/akka-http-core/src/test/java/akka/http/javadsl/WSEchoTestClientApp.java b/akka-http-core/src/test/java/akka/http/javadsl/WSEchoTestClientApp.java deleted file mode 100644 index 0b3969059a..0000000000 --- a/akka-http-core/src/test/java/akka/http/javadsl/WSEchoTestClientApp.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl; - -import akka.NotUsed; -import akka.actor.ActorSystem; -import akka.dispatch.Futures; -import akka.http.javadsl.model.ws.Message; -import akka.http.javadsl.model.ws.TextMessage; -import akka.http.javadsl.model.ws.WebSocketRequest; -import akka.japi.function.Function; -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 scala.concurrent.Future; -import scala.concurrent.duration.FiniteDuration; - -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.CompletionStage; -import java.util.concurrent.TimeUnit; - -public class WSEchoTestClientApp { - private static final Function messageStringifier = new Function() { - private static final long serialVersionUID = 1L; - @Override - public String apply(Message msg) throws Exception { - if (msg.isText() && msg.asTextMessage().isStrict()) - return msg.asTextMessage().getStrictText(); - else - throw new IllegalArgumentException("Unexpected message "+msg); - } - }; - - public static void main(String[] args) throws Exception { - ActorSystem system = ActorSystem.create(); - - try { - final Materializer materializer = ActorMaterializer.create(system); - - final Future ignoredMessage = Futures.successful((Message) TextMessage.create("blub")); - final Future delayedCompletion = - akka.pattern.Patterns.after( - FiniteDuration.apply(1, "second"), - system.scheduler(), - system.dispatcher(), - ignoredMessage); - - Source echoSource = - Source.from(Arrays.asList( - TextMessage.create("abc"), - TextMessage.create("def"), - TextMessage.create("ghi") - )).concat(Source.fromFuture(delayedCompletion).drop(1)); - - Sink>> echoSink = - Flow.of(Message.class) - .map(messageStringifier) - .grouped(1000) - .toMat(Sink.>head(), Keep.right()); - - Flow>> echoClient = - Flow.fromSinkAndSourceMat(echoSink, echoSource, Keep.left()); - - CompletionStage> result = - Http.get(system).singleWebSocketRequest( - WebSocketRequest.create("ws://echo.websocket.org"), - echoClient, - materializer - ).second(); - - List messages = result.toCompletableFuture().get(10, TimeUnit.SECONDS); - System.out.println("Collected " + messages.size() + " messages:"); - for (String msg: messages) - System.out.println(msg); - } finally { - system.terminate(); - } - } -} diff --git a/akka-http-core/src/test/java/akka/http/javadsl/model/EntityDiscardingTest.java b/akka-http-core/src/test/java/akka/http/javadsl/model/EntityDiscardingTest.java deleted file mode 100644 index fbdf6add6a..0000000000 --- a/akka-http-core/src/test/java/akka/http/javadsl/model/EntityDiscardingTest.java +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -import akka.Done; -import akka.actor.ActorSystem; -import akka.japi.function.Procedure; -import akka.stream.ActorMaterializer; -import akka.stream.javadsl.Sink; -import akka.stream.javadsl.Source; -import akka.util.ByteString; -import org.junit.Test; -import org.scalatest.junit.JUnitSuite; - -import scala.util.Try; - -import java.util.Arrays; -import java.util.concurrent.CompletableFuture; - -import static org.junit.Assert.assertEquals; - -public class EntityDiscardingTest extends JUnitSuite { - - private ActorSystem sys = ActorSystem.create("test"); - private ActorMaterializer mat = ActorMaterializer.create(sys); - private Iterable testData = Arrays.asList(ByteString.fromString("abc"), ByteString.fromString("def")); - - @Test - public void testHttpRequestDiscardEntity() { - - CompletableFuture f = new CompletableFuture<>(); - Source s = Source.from(testData).alsoTo(Sink.onComplete(completeDone(f))); - - RequestEntity reqEntity = HttpEntities.create(ContentTypes.TEXT_PLAIN_UTF8, s); - HttpRequest req = HttpRequest.create().withEntity(reqEntity); - - HttpMessage.DiscardedEntity de = req.discardEntityBytes(mat); - - assertEquals(Done.getInstance(), f.join()); - assertEquals(Done.getInstance(), de.completionStage().toCompletableFuture().join()); - } - - @Test - public void testHttpResponseDiscardEntity() { - - CompletableFuture f = new CompletableFuture<>(); - Source s = Source.from(testData).alsoTo(Sink.onComplete(completeDone(f))); - - ResponseEntity respEntity = HttpEntities.create(ContentTypes.TEXT_PLAIN_UTF8, s); - HttpResponse resp = HttpResponse.create().withEntity(respEntity); - - HttpMessage.DiscardedEntity de = resp.discardEntityBytes(mat); - - assertEquals(Done.getInstance(), f.join()); - assertEquals(Done.getInstance(), de.completionStage().toCompletableFuture().join()); - } - - private Procedure> completeDone(CompletableFuture p) { - return new Procedure>() { - @Override - public void apply(Try t) throws Exception { - if(t.isSuccess()) - p.complete(Done.getInstance()); - else - p.completeExceptionally(t.failed().get()); - } - }; - } -} diff --git a/akka-http-core/src/test/java/akka/http/javadsl/model/JavaApiTestCases.java b/akka-http-core/src/test/java/akka/http/javadsl/model/JavaApiTestCases.java deleted file mode 100644 index bc90653ba1..0000000000 --- a/akka-http-core/src/test/java/akka/http/javadsl/model/JavaApiTestCases.java +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model; - -import akka.http.javadsl.model.headers.*; -import akka.japi.Pair; -import akka.stream.javadsl.Source; -import akka.util.ByteString; - -public class JavaApiTestCases { - /** - * Builds a request for use on the client side - */ - public static HttpRequest buildRequest() { - return - HttpRequest.create() - .withMethod(HttpMethods.POST) - .withUri("/send"); - } - - /** - * A simple handler for an Http server - */ - public static HttpResponse handleRequest(HttpRequest request) { - if (request.method() == HttpMethods.GET) { - Uri uri = request.getUri(); - if (uri.path().equals("/hello")) { - String name = uri.query().get("name").orElse("Mister X"); - - return - HttpResponse.create() - .withEntity("Hello " + name + "!"); - } else { - return - HttpResponse.create() - .withStatus(404) - .withEntity("Not found"); - } - } else { - return - HttpResponse.create() - .withStatus(StatusCodes.METHOD_NOT_ALLOWED) - .withEntity("Unsupported method"); - } - } - - /** - * Adds authentication to an existing request - */ - public static HttpRequest addAuthentication(HttpRequest request) { - // unused here but just to show the shortcut - request.addHeader(Authorization.basic("username", "password")); - - return request - .addHeader(Authorization.create(HttpCredentials.createBasicHttpCredentials("username", "password"))); - - } - - /** - * Removes cookies from an existing request - */ - public static HttpRequest removeCookies(HttpRequest request) { - return request.removeHeader("Cookie"); - } - - /** - * Build a uri to send a form - */ - public static Uri createUriForOrder(String orderId, String price, String amount) { - return Uri.create("/order").query( - Query.create( - Pair.create("orderId", orderId), - Pair.create("price", price), - Pair.create("amount", amount))); - } - - public static Query addSessionId(Query query) { - return query.withParam("session", "abcdefghijkl"); - } - - public static Object accessScalaDefinedJavadslContentTypeAndMediaType(ContentType type) { - Object anything = null; - - akka.http.javadsl.model.MediaType mediaType = type.mediaType(); - - // just for the sake of explicitly touching the interfaces - if (mediaType.binary()) anything = (akka.http.javadsl.model.MediaType.Binary) mediaType; - anything = (akka.http.javadsl.model.MediaType.Multipart) mediaType; - anything = (akka.http.javadsl.model.MediaType.WithOpenCharset) mediaType; - anything = (akka.http.javadsl.model.MediaType.WithFixedCharset) mediaType; - - if (type.binary()) anything = (akka.http.javadsl.model.ContentType.Binary) type; - anything = (akka.http.javadsl.model.ContentType.NonBinary) type; - anything = (akka.http.javadsl.model.ContentType.WithCharset) type; - anything = (akka.http.javadsl.model.ContentType.WithFixedCharset) type; - - return anything; - } - - public static void HttpEntity_should_not_care_about_materialized_value_of_its_source() { - Source src = Source.single(ByteString.fromString("hello, world")).mapMaterializedValue(m -> 42); - HttpEntity entity = HttpEntities.create(ContentTypes.TEXT_PLAIN_UTF8, src); // this needs to accept Source - } -} diff --git a/akka-http-core/src/test/java/akka/http/javadsl/settings/ClientConnectionSettingsTest.java b/akka-http-core/src/test/java/akka/http/javadsl/settings/ClientConnectionSettingsTest.java deleted file mode 100644 index cb99227476..0000000000 --- a/akka-http-core/src/test/java/akka/http/javadsl/settings/ClientConnectionSettingsTest.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.settings; - -import akka.actor.ActorSystem; -import org.junit.Test; -import org.scalatest.junit.JUnitSuite; - -public class ClientConnectionSettingsTest extends JUnitSuite { - - @Test - public void testCreateWithActorSystem() { - ActorSystem sys = ActorSystem.create("test"); - ClientConnectionSettings settings = ClientConnectionSettings.create(sys); - } -} diff --git a/akka-http-core/src/test/java/akka/http/javadsl/settings/ConnectionPoolSettingsTest.java b/akka-http-core/src/test/java/akka/http/javadsl/settings/ConnectionPoolSettingsTest.java deleted file mode 100644 index 62f0da1b07..0000000000 --- a/akka-http-core/src/test/java/akka/http/javadsl/settings/ConnectionPoolSettingsTest.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.settings; - -import akka.actor.ActorSystem; -import org.junit.Test; -import org.scalatest.junit.JUnitSuite; - -public class ConnectionPoolSettingsTest extends JUnitSuite { - - @Test - public void testCreateWithActorSystem() { - ActorSystem sys = ActorSystem.create("test"); - ConnectionPoolSettings settings = ConnectionPoolSettings.create(sys); - } -} diff --git a/akka-http-core/src/test/java/akka/http/javadsl/settings/ParserSettingsTest.java b/akka-http-core/src/test/java/akka/http/javadsl/settings/ParserSettingsTest.java deleted file mode 100644 index 1ade595117..0000000000 --- a/akka-http-core/src/test/java/akka/http/javadsl/settings/ParserSettingsTest.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.settings; - -import akka.actor.ActorSystem; -import org.junit.Test; -import org.scalatest.junit.JUnitSuite; - -public class ParserSettingsTest extends JUnitSuite { - - @Test - public void testCreateWithActorSystem() { - ActorSystem sys = ActorSystem.create("test"); - ParserSettings settings = ParserSettings.create(sys); - } -} diff --git a/akka-http-core/src/test/java/akka/http/javadsl/settings/RoutingSettingsTest.java b/akka-http-core/src/test/java/akka/http/javadsl/settings/RoutingSettingsTest.java deleted file mode 100644 index 902a321cb4..0000000000 --- a/akka-http-core/src/test/java/akka/http/javadsl/settings/RoutingSettingsTest.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.settings; - -import akka.actor.ActorSystem; -import com.typesafe.config.Config; -import com.typesafe.config.ConfigFactory; -import org.junit.Test; -import org.scalatest.junit.JUnitSuite; - -public class RoutingSettingsTest extends JUnitSuite { - - @Test - public void testCreateWithActorSystem() { - String testConfig = - "akka.http.routing {\n" + - " verbose-error-messages = off\n" + - " file-get-conditional = on\n" + - " render-vanity-footer = yes\n" + - " range-coalescing-threshold = 80\n" + - " range-count-limit = 16\n" + - " decode-max-bytes-per-chunk = 1m\n" + - " file-io-dispatcher = \"test-only\"\n" + - "}"; - Config config = ConfigFactory.parseString(testConfig); - ActorSystem sys = ActorSystem.create("test", config); - RoutingSettings settings = RoutingSettings.create(sys); - } -} diff --git a/akka-http-core/src/test/java/akka/http/javadsl/settings/ServerSettingsTest.java b/akka-http-core/src/test/java/akka/http/javadsl/settings/ServerSettingsTest.java deleted file mode 100644 index b4ae596f06..0000000000 --- a/akka-http-core/src/test/java/akka/http/javadsl/settings/ServerSettingsTest.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.settings; - -import akka.actor.ActorSystem; -import org.junit.Test; -import org.scalatest.junit.JUnitSuite; - -public class ServerSettingsTest extends JUnitSuite { - - @Test - public void testCreateWithActorSystem() { - ActorSystem sys = ActorSystem.create("test"); - ServerSettings settings = ServerSettings.create(sys); - } -} diff --git a/akka-http-core/src/test/resources/keys/README.md b/akka-http-core/src/test/resources/keys/README.md deleted file mode 100644 index 1353642d4e..0000000000 --- a/akka-http-core/src/test/resources/keys/README.md +++ /dev/null @@ -1,57 +0,0 @@ -Keys for running Tls tests using the `ExampleHttpContexts` ----------------------------------------------------------- - -Instructions adapted from - - * http://datacenteroverlords.com/2012/03/01/creating-your-own-ssl-certificate-authority/ - * http://security.stackexchange.com/questions/9600/how-to-use-openssl-generated-keys-in-java - - -# Create a rootCA key: - -``` -openssl genrsa -out rootCA.key 2048 -``` - -# Self-sign CA: - -``` -openssl req -x509 -new -nodes -key rootCA.key -days 3560 -out rootCA.crt -``` - -# Create server key: - -``` -openssl genrsa -out server.key 2048 -``` - -# Create server CSR (you need to set the common name CN to "akka.example.org"): - -``` -openssl req -new -key server.key -out server.csr -``` - -# Create server certificate: - -``` -openssl x509 -req -in server.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out server.crt -days 3560 -``` - -# Create certificate chain: - -``` -cat server.crt rootCA.crt > chain.pem -``` - -# Convert certificate and key to pkcs12 (you need to provide a password manually, `ExampleHttpContexts` -# expects the password to be "abcdef"): - -``` -openssl pkcs12 -export -name servercrt -in chain.pem -inkey server.key -out server.p12 -``` - -# For investigating remote certs use: - -``` -openssl s_client -showcerts -connect 54.173.126.144:443 -``` diff --git a/akka-http-core/src/test/resources/keys/chain.pem b/akka-http-core/src/test/resources/keys/chain.pem deleted file mode 100644 index 766871eba2..0000000000 --- a/akka-http-core/src/test/resources/keys/chain.pem +++ /dev/null @@ -1,40 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDITCCAgkCCQCo8H6OcPrArzANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB -VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 -cyBQdHkgTHRkMB4XDTE1MDcyMzA5NTEyMloXDTI1MDQyMTA5NTEyMlowYDELMAkG -A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0 -IFdpZGdpdHMgUHR5IEx0ZDEZMBcGA1UEAwwQYWtrYS5leGFtcGxlLm9yZzCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANMy/wgrSYVhVtu9OGbo2rSKauiz -5V56X4uCqtCHF9UeHtnVtFLCBMa+pimOS+UyUAT4mbBsxW22BhoNUBZ15KPxltyD -yEsqNCKwWGxL3r8AXQtze2MEpTl22Lvp/iCTXO1vbML/+9r3uqUjw/AAP9HwF9Wd -j/yOrs6q8WE4sfc48iOj6N60/h2pRfn2WNJmo9W9FLC53NznixfsG5oN6Jmb9RM+ -fMHYXLfL/Vt6NrgVX1uqHt9HvuoxfNKhhXE5VU8bNfFfzPYvIt4aZXGxO15vEqsq -OaZ7YJyKr1oFfJC8LmE5xPa3GHToCqmkdMXQK38mpslMQWlQLYnmkS5Qzv8CAwEA -ATANBgkqhkiG9w0BAQsFAAOCAQEAEPDd1gAF9q2LtoZqTdcwmeBjdbT7n0WDRSuI -BzQ/qKjvymwpFKQ0pZSPUyaw2qfRRiTQ/QTbqYep2mhvl5n+gW3ifTp83zgTGKH/ -3sDlX0HPSCBYCDy2gP/AOIgV/57ADMpEkTlz8yyLMH+pLDAoNFIPwy7blAkq+ULQ -y6TfEBmZXoemSaIh5tRnexCD+pTvL4MRrGlBEoxdejDnIAt4n6BxmF0b4hKg8uta -UvivA85lBKzWUoR/Vam5/SC8jtcyLt9RThRcNSj6zP6s5d+o+8PLznrSEadAtfD9 -0q+t4TYF81tClEEgGruVPNL4WIpDniOfw9AJgQNVJGfy5TKY1Q== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDXTCCAkWgAwIBAgIJANYwx08wP3STMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV -BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX -aWRnaXRzIFB0eSBMdGQwHhcNMTUwNzIzMDk0ODI2WhcNMjUwNDIxMDk0ODI2WjBF -MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 -ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEArk0K/Rn7uND2YGFBks5Sok1WvNdHQccPESEw2hNVF32ExAhbBXCrFaIl -Io0q4eYSbypeauEjDXB/NJXurEefL8ONXK62erJDKKQ0aTTYqsVifoNYA9ORWoGE -XhtAfOx4xvzr6vF1e3kz0PB/A4ftn0vvVygYnf/2E2bQZgaw8dXP5lIGasEzzigB -LX/qTEW/vBOL98Rxp6JvjwvYMbPSZGwNwSz+tI5W2psdE1Mga2Qnsv3j+STWlD9v -+JlgdN8r3PyR1sl3jC7gCj3AaOhv4RbAbqjwnZ9nrckx16PFiMtJiVRea7CQXN7g -191EVujQnlg1LOhiSMKwVsuoXr08ywIDAQABo1AwTjAdBgNVHQ4EFgQU2THI/ilU -M0xds3vZlV4CvhAZ1d8wHwYDVR0jBBgwFoAU2THI/ilUM0xds3vZlV4CvhAZ1d8w -DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAK9LO0HyIi0xbTISsc+A5 -LQyZowgRAGqsNNmni7NKDXauPLZrCfDVhvo/FPP1XSFShXo7ARvro9lul4AJlkNN -VgX0gbWtkiAx0uLqlbMsC6imj2L9boRse7mzI/Ymem5SNTn9GUnlMiZ74rca9UT4 -Dk9YytrT4FSpomiL6z8Xj604W3RuLSdEfpfcn3Jh2tFSZ9hyLwB7ATUTA/yuj1SU -G1gmoPMvlnPzNj2lIqyIdQxGdxt+L3mFO20CxBkeieWqQuNptpjwptliFjkZJJZP -wQlx9qLLvs/eFC2AUWj+hbsl37PuARR9hoeqbKRcUjwGtaXOqikrvX1qzPc2+ij9 -/w== ------END CERTIFICATE----- diff --git a/akka-http-core/src/test/resources/keys/rootCA.crt b/akka-http-core/src/test/resources/keys/rootCA.crt deleted file mode 100644 index 6ba9fb756c..0000000000 --- a/akka-http-core/src/test/resources/keys/rootCA.crt +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDXTCCAkWgAwIBAgIJANYwx08wP3STMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV -BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX -aWRnaXRzIFB0eSBMdGQwHhcNMTUwNzIzMDk0ODI2WhcNMjUwNDIxMDk0ODI2WjBF -MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 -ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEArk0K/Rn7uND2YGFBks5Sok1WvNdHQccPESEw2hNVF32ExAhbBXCrFaIl -Io0q4eYSbypeauEjDXB/NJXurEefL8ONXK62erJDKKQ0aTTYqsVifoNYA9ORWoGE -XhtAfOx4xvzr6vF1e3kz0PB/A4ftn0vvVygYnf/2E2bQZgaw8dXP5lIGasEzzigB -LX/qTEW/vBOL98Rxp6JvjwvYMbPSZGwNwSz+tI5W2psdE1Mga2Qnsv3j+STWlD9v -+JlgdN8r3PyR1sl3jC7gCj3AaOhv4RbAbqjwnZ9nrckx16PFiMtJiVRea7CQXN7g -191EVujQnlg1LOhiSMKwVsuoXr08ywIDAQABo1AwTjAdBgNVHQ4EFgQU2THI/ilU -M0xds3vZlV4CvhAZ1d8wHwYDVR0jBBgwFoAU2THI/ilUM0xds3vZlV4CvhAZ1d8w -DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAK9LO0HyIi0xbTISsc+A5 -LQyZowgRAGqsNNmni7NKDXauPLZrCfDVhvo/FPP1XSFShXo7ARvro9lul4AJlkNN -VgX0gbWtkiAx0uLqlbMsC6imj2L9boRse7mzI/Ymem5SNTn9GUnlMiZ74rca9UT4 -Dk9YytrT4FSpomiL6z8Xj604W3RuLSdEfpfcn3Jh2tFSZ9hyLwB7ATUTA/yuj1SU -G1gmoPMvlnPzNj2lIqyIdQxGdxt+L3mFO20CxBkeieWqQuNptpjwptliFjkZJJZP -wQlx9qLLvs/eFC2AUWj+hbsl37PuARR9hoeqbKRcUjwGtaXOqikrvX1qzPc2+ij9 -/w== ------END CERTIFICATE----- diff --git a/akka-http-core/src/test/resources/keys/rootCA.key b/akka-http-core/src/test/resources/keys/rootCA.key deleted file mode 100644 index 119caf0dd1..0000000000 --- a/akka-http-core/src/test/resources/keys/rootCA.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEArk0K/Rn7uND2YGFBks5Sok1WvNdHQccPESEw2hNVF32ExAhb -BXCrFaIlIo0q4eYSbypeauEjDXB/NJXurEefL8ONXK62erJDKKQ0aTTYqsVifoNY -A9ORWoGEXhtAfOx4xvzr6vF1e3kz0PB/A4ftn0vvVygYnf/2E2bQZgaw8dXP5lIG -asEzzigBLX/qTEW/vBOL98Rxp6JvjwvYMbPSZGwNwSz+tI5W2psdE1Mga2Qnsv3j -+STWlD9v+JlgdN8r3PyR1sl3jC7gCj3AaOhv4RbAbqjwnZ9nrckx16PFiMtJiVRe -a7CQXN7g191EVujQnlg1LOhiSMKwVsuoXr08ywIDAQABAoIBAQCSXAEpLMNRmq33 -mlMMqhF7VcPKyF5+Xl9Je/xgcjFWi0CLt5Ruyf/vJ3tVOwLSM3YxQHuN9cSQSXGX -P3rt0SpbWjJ+q/pwpvV7z/5uhUCWjS46m6GxfNsmC3GR8AJDo/F67fBQFTcYWlrn -TLrqxR4EUCgGoJWjPsZr3j6KHX5BYmzyTuJFBzxxipK42hnJQ7tMB8l6/5r4nRka -d6SGFpJDkyhO+Wl0sBXjxHu1E4g8asI061jEOhcROV1Dk4hp1CYhd8TBj//6FSBC -ttsIe2gxT0fk8bnNC78FuO0CUTCj4hFOWP7apr/NhLlxypu+4hj17NMhlptRvGxz -6pPlMVDJAoGBANPVTS5nkJpMyczA5vaHsyTF/nwunogwHVeVYsQQ3Bed28Ldp7gr -Dr4hgYFvGkEmlLvWOleHvGISuD3lHLd112LcPyLFMRrs8wX9vWTueZGYj5KDLS3C -i3GaYMqqYbuiFY1QYprF36zRQkLMKUiOomE2+baCasbhluAqqx32KEKvAoGBANKk -cG0X0svJ/TTQIE5nfDtKePDUA7wEPYGrQOO4vKKZUlytVhf+gEcYr575bPjkTl1h -5jrrhr4OWpFDmRyBpi7wB95Fe93Df+0o4KmiNtsioZsi/MA5Tga2rAZPBBuZ9+5l -alYl0fTo5PR3fOXJJoJ+w7+QI4N/9TGuBJoiEl6lAoGBAM8XapsBOIcApxB7TdCa -HXLH9eDlmqq9jxH+w022xdR4yU2acMtFnOYXz4oAWgRzeVihOOw1kN+4OVKZWBer -JuRJOZf+e+E84OFsjOnNkh/arBGqGFLyLGzlZdb79wv+i19ZxOxWojNLaKHxAjMi -7nBn1Hyux0CjbmK8lAl4iyeVAoGAT6r4BprTFFaiGN56yYykVPx2v4dAnlTwOmHe -GgLd/ZWFrB23CT4toDY6/iKST5Rx+ymy3SgFf06IfJaXi0uR4gDQyQV4sshlUvp5 -9k6u9rSjcLyL4dwKoclnSL+L6zCRsC3VSR3myf1n0vp6V6J7mTF+sa4/cFXuE8sg -XHd0gS0CgYAXNDcF+zYoSmbfdG7uM7qOPQwNRbr0pHvAg0NmtM9JOj8gZPoaeAy3 -3jEk9AMQrK0MNsRynAoMkhy+7WOU6TNLvyxXAKGZffOmABzSB9LEFgHkVPutl5/i -wL2pE1SoG2QwSqFYGv+rHgIpREJzDTNwbmSbl/Za50JrIZ3OFfTMDQ== ------END RSA PRIVATE KEY----- diff --git a/akka-http-core/src/test/resources/keys/server.crt b/akka-http-core/src/test/resources/keys/server.crt deleted file mode 100644 index 4395b589d5..0000000000 --- a/akka-http-core/src/test/resources/keys/server.crt +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDITCCAgkCCQCo8H6OcPrArzANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB -VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 -cyBQdHkgTHRkMB4XDTE1MDcyMzA5NTEyMloXDTI1MDQyMTA5NTEyMlowYDELMAkG -A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0 -IFdpZGdpdHMgUHR5IEx0ZDEZMBcGA1UEAwwQYWtrYS5leGFtcGxlLm9yZzCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANMy/wgrSYVhVtu9OGbo2rSKauiz -5V56X4uCqtCHF9UeHtnVtFLCBMa+pimOS+UyUAT4mbBsxW22BhoNUBZ15KPxltyD -yEsqNCKwWGxL3r8AXQtze2MEpTl22Lvp/iCTXO1vbML/+9r3uqUjw/AAP9HwF9Wd -j/yOrs6q8WE4sfc48iOj6N60/h2pRfn2WNJmo9W9FLC53NznixfsG5oN6Jmb9RM+ -fMHYXLfL/Vt6NrgVX1uqHt9HvuoxfNKhhXE5VU8bNfFfzPYvIt4aZXGxO15vEqsq -OaZ7YJyKr1oFfJC8LmE5xPa3GHToCqmkdMXQK38mpslMQWlQLYnmkS5Qzv8CAwEA -ATANBgkqhkiG9w0BAQsFAAOCAQEAEPDd1gAF9q2LtoZqTdcwmeBjdbT7n0WDRSuI -BzQ/qKjvymwpFKQ0pZSPUyaw2qfRRiTQ/QTbqYep2mhvl5n+gW3ifTp83zgTGKH/ -3sDlX0HPSCBYCDy2gP/AOIgV/57ADMpEkTlz8yyLMH+pLDAoNFIPwy7blAkq+ULQ -y6TfEBmZXoemSaIh5tRnexCD+pTvL4MRrGlBEoxdejDnIAt4n6BxmF0b4hKg8uta -UvivA85lBKzWUoR/Vam5/SC8jtcyLt9RThRcNSj6zP6s5d+o+8PLznrSEadAtfD9 -0q+t4TYF81tClEEgGruVPNL4WIpDniOfw9AJgQNVJGfy5TKY1Q== ------END CERTIFICATE----- diff --git a/akka-http-core/src/test/resources/keys/server.key b/akka-http-core/src/test/resources/keys/server.key deleted file mode 100644 index 117cb40355..0000000000 --- a/akka-http-core/src/test/resources/keys/server.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEogIBAAKCAQEA0zL/CCtJhWFW2704ZujatIpq6LPlXnpfi4Kq0IcX1R4e2dW0 -UsIExr6mKY5L5TJQBPiZsGzFbbYGGg1QFnXko/GW3IPISyo0IrBYbEvevwBdC3N7 -YwSlOXbYu+n+IJNc7W9swv/72ve6pSPD8AA/0fAX1Z2P/I6uzqrxYTix9zjyI6Po -3rT+HalF+fZY0maj1b0UsLnc3OeLF+wbmg3omZv1Ez58wdhct8v9W3o2uBVfW6oe -30e+6jF80qGFcTlVTxs18V/M9i8i3hplcbE7Xm8Sqyo5pntgnIqvWgV8kLwuYTnE -9rcYdOgKqaR0xdArfyamyUxBaVAtieaRLlDO/wIDAQABAoIBADfqTXkVNM7aWYut -yiv8xEJ+TxWy4ywjS/58psq0qYukANj9alNqyKbxvL5NzSwuKN9YDiCWe6KzSWRG -WAjKR7Fb+ewB+9pinxD8DT0GzT9WUkwA1A8AINpY68K8jaqEOVsnX+00prJvWfv0 -vyBggIUNgtHseD2ObRuMSIHL59oivxoBKmeRqFl26PCq+m6Dp1SsMwL8NE02rfUu -uVW0zSz0/A5ZK90l8St3N78Puw/qicvfrI4PrGi4kLKW9UKJKP5FzfPF7Kf9itVA -1VB3gd8Gs98vRnzHwZlwgjyAQkePzS/iEQid9uRA/Xys5ozcT1arYM00t3I7ZEUg -GJTKHBECgYEA+K/M6smzPrTAi0BEuI1NCb3zfxkjbBhC0cco9U4VIuhYVU+7Ukre -zi5yI+BQR8MPbftSeeosXV6eQaq04pKCrHWF+ql+3Io9Hojghd/EnNCOtGxjTGmI -Px8G7byeIr4+QyP+JSEdsVBfIEEQ9BJ8Up84RibsMfWcKe6ntzAMEmkCgYEA2Wj6 -DqPisPp4WwGi8bSvSRZsF3h3xu0saml+ug28j+b3kOa99Uz49kCi99sacJArYOWv -Dn+DPl2K2/lwYO0bfyXwWaLp8pd/MAmwhKZ2+qvoUnkZJFRU3yrUoPp7CURZSbcG -aD7IKotFH7wutqj8pZ50y8VGqKVACenhRSAH2ScCgYAuX7IJslUfg1tIXFK0S30r -LOXENK7bUGbdcZMcs1PTr5oRRo362YVU02prcD/oMeKlsrD9lQJy4tsGCcwzV/jQ -KhYy2PqUK58cG5AqxsCGMYn68R9PN3q1spZ7LKocdndr08FnsRY1Y3Rpslhz+yJ9 -0b0Pr+BprJBTbXKPAYGuyQKBgAJFu59djSgGZi2lVburBM4Bwv13z+CvZ/Bwy9dL -/3WNl3bXQpMGy+9e+5UVoDAfAaUQoYTIRmnndmUYNVl+APSSQ/Hb5xAXD0hEQakR -SFsUYuhBxcaAbyap/vDzzUdqhHhlxlZemZ8AN6e+Qsq793APuO7MUBHBMGsqG6Wq -UQqvAoGAINEINXhFXp2qVRDBUY57rRtpjQHajeNTMChgWTg30owfVNBY4evjRj8f -9XDuUkTumYcDcnOKmX3L6n9rg4noHlfNvxmn9pmG9vP0mG0MEOOxSxXFHVIuBw10 -wdTb0WE/i3FhyufdaRHLGhPAMQjaCeFSV3sMxMHuNePvCxnKD3E= ------END RSA PRIVATE KEY----- diff --git a/akka-http-core/src/test/resources/keys/server.p12 b/akka-http-core/src/test/resources/keys/server.p12 deleted file mode 100644 index d72cc9f3d4..0000000000 Binary files a/akka-http-core/src/test/resources/keys/server.p12 and /dev/null differ diff --git a/akka-http-core/src/test/resources/reference.conf b/akka-http-core/src/test/resources/reference.conf deleted file mode 100644 index 4dc03e5ed7..0000000000 --- a/akka-http-core/src/test/resources/reference.conf +++ /dev/null @@ -1,9 +0,0 @@ -akka { - loggers = ["akka.testkit.TestEventListener"] - actor { - serialize-creators = on - serialize-messages = on - default-dispatcher.throughput = 1 - } - stream.materializer.debug.fuzzing-mode=off -} \ No newline at end of file diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/client/ClientCancellationSpec.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/client/ClientCancellationSpec.scala deleted file mode 100644 index 839ecbc26d..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/client/ClientCancellationSpec.scala +++ /dev/null @@ -1,74 +0,0 @@ -package akka.http.impl.engine.client - -import javax.net.ssl.SSLContext -import akka.http.scaladsl.{ ConnectionContext, Http } -import akka.http.scaladsl.model.{ HttpResponse, HttpRequest } -import akka.stream.ActorMaterializer -import akka.stream.scaladsl.{ Flow, Sink, Source } -import akka.stream.testkit.{ TestSubscriber, TestPublisher, TestUtils, Utils } -import akka.http.scaladsl.model.headers -import akka.testkit.AkkaSpec - -class ClientCancellationSpec extends AkkaSpec(""" - akka.loglevel = DEBUG - akka.io.tcp.trace-logging = off""") { - - implicit val materializer = ActorMaterializer() - val noncheckedMaterializer = ActorMaterializer() - - "Http client connections" must { - val address = TestUtils.temporaryServerAddress() - Http().bindAndHandleSync( - { req ⇒ HttpResponse(headers = headers.Connection("close") :: Nil) }, - address.getHostName, - address.getPort)(noncheckedMaterializer) - - val addressTls = TestUtils.temporaryServerAddress() - Http().bindAndHandleSync( - { req ⇒ HttpResponse() }, // TLS client does full-close, no need for the connection:close header - addressTls.getHostName, - addressTls.getPort, - connectionContext = ConnectionContext.https(SSLContext.getDefault))(noncheckedMaterializer) - - def testCase(connection: Flow[HttpRequest, HttpResponse, Any]): Unit = Utils.assertAllStagesStopped { - val requests = TestPublisher.probe[HttpRequest]() - val responses = TestSubscriber.probe[HttpResponse]() - Source.fromPublisher(requests).via(connection).runWith(Sink.fromSubscriber(responses)) - responses.request(1) - requests.sendNext(HttpRequest()) - responses.expectNext().entity.dataBytes.runWith(Sink.cancelled) - responses.cancel() - requests.expectCancellation() - } - - "support cancellation in simple outgoing connection" in { - testCase( - Http().outgoingConnection(address.getHostName, address.getPort)) - } - - "support cancellation in pooled outgoing connection" in { - testCase( - Flow[HttpRequest] - .map((_, ())) - .via(Http().cachedHostConnectionPool(address.getHostName, address.getPort)(noncheckedMaterializer)) - .map(_._1.get)) - } - - "support cancellation in simple outgoing connection with TLS" in { - pending - testCase( - Http().outgoingConnectionHttps(addressTls.getHostName, addressTls.getPort)) - } - - "support cancellation in pooled outgoing connection with TLS" in { - pending - testCase( - Flow[HttpRequest] - .map((_, ())) - .via(Http().cachedHostConnectionPoolHttps(addressTls.getHostName, addressTls.getPort)(noncheckedMaterializer)) - .map(_._1.get)) - } - - } - -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/client/ConnectionPoolSpec.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/client/ConnectionPoolSpec.scala deleted file mode 100644 index ccb658bc79..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/client/ConnectionPoolSpec.scala +++ /dev/null @@ -1,509 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.client - -import java.net.InetSocketAddress -import java.nio.ByteBuffer -import java.nio.channels.{ ServerSocketChannel, SocketChannel } -import java.util.concurrent.atomic.AtomicInteger - -import akka.http.impl.engine.client.PoolMasterActor.PoolInterfaceRunning -import akka.http.impl.settings.ConnectionPoolSettingsImpl -import akka.http.impl.util.SingletonException -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers._ -import akka.http.scaladsl.settings.{ ClientConnectionSettings, ConnectionPoolSettings, ServerSettings } -import akka.http.scaladsl.{ Http, TestUtils } -import akka.stream.ActorMaterializer -import akka.stream.TLSProtocol._ -import akka.stream.scaladsl._ -import akka.stream.testkit.{ TestPublisher, TestSubscriber } -import akka.testkit.AkkaSpec -import akka.util.ByteString - -import scala.collection.immutable -import scala.concurrent.Await -import scala.concurrent.duration._ -import scala.util.control.NonFatal -import scala.util.{ Failure, Success, Try } - -class ConnectionPoolSpec extends AkkaSpec(""" - akka.loggers = [] - akka.loglevel = OFF - akka.io.tcp.windows-connection-abort-workaround-enabled = auto - akka.io.tcp.trace-logging = off""") { - implicit val materializer = ActorMaterializer() - - // FIXME: Extract into proper util class to be reusable - lazy val ConnectionResetByPeerMessage: String = { - val serverSocket = ServerSocketChannel.open() - serverSocket.socket.bind(new InetSocketAddress("127.0.0.1", 0)) - try { - val clientSocket = SocketChannel.open(new InetSocketAddress("127.0.0.1", serverSocket.socket().getLocalPort)) - @volatile var serverSideChannel: SocketChannel = null - awaitCond { - serverSideChannel = serverSocket.accept() - serverSideChannel != null - } - serverSideChannel.socket.setSoLinger(true, 0) - serverSideChannel.close() - clientSocket.read(ByteBuffer.allocate(1)) - null - } catch { - case NonFatal(e) ⇒ e.getMessage - } - } - - "The host-level client infrastructure" should { - - "properly complete a simple request/response cycle" in new TestSetup { - val (requestIn, responseOut, responseOutSub, hcp) = cachedHostConnectionPool[Int]() - - requestIn.sendNext(HttpRequest(uri = "/") → 42) - - responseOutSub.request(1) - acceptIncomingConnection() - val (Success(response), 42) = responseOut.expectNext() - response.headers should contain(RawHeader("Req-Host", s"$serverHostName:$serverPort")) - } - - "open a second connection if the first one is loaded" in new TestSetup { - val (requestIn, responseOut, responseOutSub, hcp) = cachedHostConnectionPool[Int]() - - requestIn.sendNext(HttpRequest(uri = "/a") → 42) - requestIn.sendNext(HttpRequest(uri = "/b") → 43) - - responseOutSub.request(2) - acceptIncomingConnection() - val r1 = responseOut.expectNext() - acceptIncomingConnection() - val r2 = responseOut.expectNext() - - Seq(r1, r2) foreach { - case (Success(x), 42) ⇒ requestUri(x) should endWith("/a") - case (Success(x), 43) ⇒ requestUri(x) should endWith("/b") - case x ⇒ fail(x.toString) - } - Seq(r1, r2).map(t ⇒ connNr(t._1.get)) should contain allOf (1, 2) - } - - "open a second connection if the request on the first one is dispatch but not yet completed" in new TestSetup { - val (requestIn, responseOut, responseOutSub, hcp) = cachedHostConnectionPool[Int]() - - val responseEntityPub = TestPublisher.probe[ByteString]() - - override def testServerHandler(connNr: Int): HttpRequest ⇒ HttpResponse = { - case request @ HttpRequest(_, Uri.Path("/a"), _, _, _) ⇒ - val entity = HttpEntity.Chunked.fromData(ContentTypes.`text/plain(UTF-8)`, Source.fromPublisher(responseEntityPub)) - super.testServerHandler(connNr)(request) withEntity entity - case x ⇒ super.testServerHandler(connNr)(x) - } - - requestIn.sendNext(HttpRequest(uri = "/a") → 42) - responseOutSub.request(1) - acceptIncomingConnection() - val (Success(r1), 42) = responseOut.expectNext() - val responseEntityProbe = TestSubscriber.probe[ByteString]() - r1.entity.dataBytes.runWith(Sink.fromSubscriber(responseEntityProbe)) - responseEntityProbe.expectSubscription().request(2) - responseEntityPub.sendNext(ByteString("YEAH")) - responseEntityProbe.expectNext(ByteString("YEAH")) - - requestIn.sendNext(HttpRequest(uri = "/b") → 43) - responseOutSub.request(1) - acceptIncomingConnection() - val (Success(r2), 43) = responseOut.expectNext() - connNr(r2) shouldEqual 2 - } - - "not open a second connection if there is an idle one available" in new TestSetup { - val (requestIn, responseOut, responseOutSub, hcp) = cachedHostConnectionPool[Int]() - - requestIn.sendNext(HttpRequest(uri = "/a") → 42) - responseOutSub.request(1) - acceptIncomingConnection() - val (Success(response1), 42) = responseOut.expectNext() - connNr(response1) shouldEqual 1 - - requestIn.sendNext(HttpRequest(uri = "/b") → 43) - responseOutSub.request(1) - val (Success(response2), 43) = responseOut.expectNext() - connNr(response2) shouldEqual 1 - } - - "be able to handle 500 pipelined requests against the test server" in new TestSetup { - val settings = ConnectionPoolSettings(system).withMaxConnections(4).withPipeliningLimit(2) - val poolFlow = Http().cachedHostConnectionPool[Int](serverHostName, serverPort, settings = settings) - - val N = 500 - val requestIds = Source.fromIterator(() ⇒ Iterator.from(1)).take(N) - val idSum = requestIds.map(id ⇒ HttpRequest(uri = s"/r$id") → id).via(poolFlow).map { - case (Success(response), id) ⇒ - requestUri(response) should endWith(s"/r$id") - id - case x ⇒ fail(x.toString) - }.runFold(0)(_ + _) - - acceptIncomingConnection() - acceptIncomingConnection() - acceptIncomingConnection() - acceptIncomingConnection() - - Await.result(idSum, 10.seconds) shouldEqual N * (N + 1) / 2 - } - - "properly surface connection-level errors" in new TestSetup(autoAccept = true) { - val (requestIn, responseOut, responseOutSub, hcp) = cachedHostConnectionPool[Int](maxRetries = 0) - - requestIn.sendNext(HttpRequest(uri = "/a") → 42) - requestIn.sendNext(HttpRequest(uri = "/crash") → 43) - responseOutSub.request(2) - - override def mapServerSideOutboundRawBytes(bytes: ByteString): ByteString = - if (bytes.utf8String.contains("/crash")) sys.error("CRASH BOOM BANG") else bytes - - val responses = Seq(responseOut.expectNext(), responseOut.expectNext()) - - responses mustContainLike { case (Success(x), 42) ⇒ requestUri(x) should endWith("/a") } - responses mustContainLike { case (Failure(x), 43) ⇒ x.getMessage should include(ConnectionResetByPeerMessage) } - } - - "retry failed requests" in new TestSetup(autoAccept = true) { - val (requestIn, responseOut, responseOutSub, hcp) = cachedHostConnectionPool[Int]() - - requestIn.sendNext(HttpRequest(uri = "/a") → 42) - requestIn.sendNext(HttpRequest(uri = "/crash") → 43) - responseOutSub.request(2) - - val remainingResponsesToKill = new AtomicInteger(1) - override def mapServerSideOutboundRawBytes(bytes: ByteString): ByteString = - if (bytes.utf8String.contains("/crash") && remainingResponsesToKill.decrementAndGet() >= 0) - sys.error("CRASH BOOM BANG") - else bytes - - val responses = Seq(responseOut.expectNext(), responseOut.expectNext()) - - responses mustContainLike { case (Success(x), 42) ⇒ requestUri(x) should endWith("/a") } - responses mustContainLike { case (Success(x), 43) ⇒ requestUri(x) should endWith("/crash") } - } - - "respect the configured `maxRetries` value" in new TestSetup(autoAccept = true) { - val (requestIn, responseOut, responseOutSub, hcp) = cachedHostConnectionPool[Int](maxRetries = 4) - - requestIn.sendNext(HttpRequest(uri = "/a") → 42) - requestIn.sendNext(HttpRequest(uri = "/crash") → 43) - responseOutSub.request(2) - - val remainingResponsesToKill = new AtomicInteger(5) - override def mapServerSideOutboundRawBytes(bytes: ByteString): ByteString = - if (bytes.utf8String.contains("/crash") && remainingResponsesToKill.decrementAndGet() >= 0) - sys.error("CRASH BOOM BANG") - else bytes - - val responses = Seq(responseOut.expectNext(), responseOut.expectNext()) - - responses mustContainLike { case (Success(x), 42) ⇒ requestUri(x) should endWith("/a") } - responses mustContainLike { case (Failure(x), 43) ⇒ x.getMessage should include(ConnectionResetByPeerMessage) } - remainingResponsesToKill.get() shouldEqual 0 - } - - "automatically shutdown after configured timeout periods" in new TestSetup() { - val (_, _, _, hcp) = cachedHostConnectionPool[Int](idleTimeout = 1.second) - val gateway = hcp.gateway - Await.result(gateway.poolStatus(), 1500.millis).get shouldBe a[PoolInterfaceRunning] - awaitCond({ Await.result(gateway.poolStatus(), 1500.millis).isEmpty }, 2000.millis) - } - - "transparently restart after idle shutdown" in new TestSetup() { - val (requestIn, responseOut, responseOutSub, hcp) = cachedHostConnectionPool[Int](idleTimeout = 1.second) - - val gateway = hcp.gateway - Await.result(gateway.poolStatus(), 1500.millis).get shouldBe a[PoolInterfaceRunning] - awaitCond({ Await.result(gateway.poolStatus(), 1500.millis).isEmpty }, 2000.millis) - - requestIn.sendNext(HttpRequest(uri = "/") → 42) - - responseOutSub.request(1) - acceptIncomingConnection() - val (Success(_), 42) = responseOut.expectNext() - } - - "never close hot connections when minConnections key is given and >0 (minConnections = 1)" in new TestSetup() { - val close: HttpHeader = Connection("close") - - // for lower bound of one connection - val minConnection = 1 - val (requestIn, requestOut, responseOutSub, hcpMinConnection) = - cachedHostConnectionPool[Int](idleTimeout = 100.millis, minConnections = minConnection) - val gatewayConnection = hcpMinConnection.gateway - - acceptIncomingConnection() - requestIn.sendNext(HttpRequest(uri = "/minimumslots/1", headers = immutable.Seq(close)) → 42) - responseOutSub.request(1) - requestOut.expectNextN(1) - - condHolds(500.millis) { () ⇒ - Await.result(gatewayConnection.poolStatus(), 100.millis).get shouldBe a[PoolInterfaceRunning] - } - } - - "never close hot connections when minConnections key is given and >0 (minConnections = 5)" in new TestSetup() { - val close: HttpHeader = Connection("close") - - // for lower bound of five connections - val minConnections = 5 - val (requestIn, requestOut, responseOutSub, hcpMinConnection) = cachedHostConnectionPool[Int]( - idleTimeout = 100.millis, - minConnections = minConnections, - maxConnections = minConnections + 10) - - (0 until minConnections) foreach { _ ⇒ acceptIncomingConnection() } - (0 until minConnections) foreach { i ⇒ - requestIn.sendNext(HttpRequest(uri = s"/minimumslots/5/$i", headers = immutable.Seq(close)) → 42) - } - responseOutSub.request(minConnections) - requestOut.expectNextN(minConnections) - - val gatewayConnections = hcpMinConnection.gateway - condHolds(1000.millis) { () ⇒ - val status = gatewayConnections.poolStatus() - Await.result(status, 100.millis).get shouldBe a[PoolInterfaceRunning] - } - } - - "shutdown if idle and min connection has been set to 0" in new TestSetup() { - val (_, _, _, hcp) = cachedHostConnectionPool[Int](idleTimeout = 1.second, minConnections = 0) - val gateway = hcp.gateway - Await.result(gateway.poolStatus(), 1500.millis).get shouldBe a[PoolInterfaceRunning] - awaitCond({ Await.result(gateway.poolStatus(), 1500.millis).isEmpty }, 2000.millis) - } - } - - "The single-request client infrastructure" should { - class LocalTestSetup extends TestSetup(ServerSettings(system).withRawRequestUriHeader(true), autoAccept = true) - - "transform absolute request URIs into relative URIs plus host header" in new LocalTestSetup { - val request = HttpRequest(uri = s"http://$serverHostName:$serverPort/abc?query#fragment") - val responseFuture = Http().singleRequest(request) - val responseHeaders = Await.result(responseFuture, 1.second).headers - responseHeaders should contain(RawHeader("Req-Raw-Request-URI", "/abc?query")) - responseHeaders should contain(RawHeader("Req-Host", s"$serverHostName:$serverPort")) - } - - "support absolute request URIs without path component" in new LocalTestSetup { - val request = HttpRequest(uri = s"http://$serverHostName:$serverPort") - val responseFuture = Http().singleRequest(request) - val responseHeaders = Await.result(responseFuture, 1.second).headers - responseHeaders should contain(RawHeader("Req-Raw-Request-URI", "/")) - } - - "support absolute request URIs with a double slash path component" in new LocalTestSetup { - val request = HttpRequest(uri = s"http://$serverHostName:$serverPort//foo") - val responseFuture = Http().singleRequest(request) - val responseHeaders = Await.result(responseFuture, 1.second).headers - responseHeaders should contain(RawHeader("Req-Uri", s"http://$serverHostName:$serverPort//foo")) - responseHeaders should contain(RawHeader("Req-Raw-Request-URI", "//foo")) - } - - "produce an error if the request does not have an absolute URI" in { - val request = HttpRequest(uri = "/foo") - val responseFuture = Http().singleRequest(request) - val thrown = the[IllegalUriException] thrownBy Await.result(responseFuture, 1.second) - thrown should have message "Cannot determine request scheme and target endpoint as HttpMethod(GET) request to /foo doesn't have an absolute URI" - } - } - - "The superPool client infrastructure" should { - - "route incoming requests to the right cached host connection pool" in new TestSetup(autoAccept = true) { - val (serverEndpoint2, serverHostName2, serverPort2) = TestUtils.temporaryServerHostnameAndPort() - Http().bindAndHandleSync(testServerHandler(0), serverHostName2, serverPort2) - - val (requestIn, responseOut, responseOutSub, hcp) = superPool[Int]() - - requestIn.sendNext(HttpRequest(uri = s"http://$serverHostName:$serverPort/a") → 42) - requestIn.sendNext(HttpRequest(uri = s"http://$serverHostName2:$serverPort2/b") → 43) - - responseOutSub.request(2) - Seq(responseOut.expectNext(), responseOut.expectNext()) foreach { - case (Success(x), 42) ⇒ requestUri(x) shouldEqual s"http://$serverHostName:$serverPort/a" - case (Success(x), 43) ⇒ requestUri(x) shouldEqual s"http://$serverHostName2:$serverPort2/b" - case x ⇒ fail(x.toString) - } - } - } - - "be able to handle 500 `Connection: close` requests against the test server" in new TestSetup { - val settings = ConnectionPoolSettings(system).withMaxConnections(4) - val poolFlow = Http().cachedHostConnectionPool[Int](serverHostName, serverPort, settings = settings) - - val N = 500 - val requestIds = Source.fromIterator(() ⇒ Iterator.from(1)).take(N) - val idSum = requestIds.map(id ⇒ HttpRequest(uri = s"/r$id").withHeaders(Connection("close")) → id).via(poolFlow).map { - case (Success(response), id) ⇒ - requestUri(response) should endWith(s"/r$id") - id - case x ⇒ fail(x.toString) - }.runFold(0)(_ + _) - - (1 to N).foreach(_ ⇒ acceptIncomingConnection()) - - Await.result(idSum, 10.seconds) shouldEqual N * (N + 1) / 2 - } - - "be able to handle 500 pipelined requests with connection termination" in new TestSetup(autoAccept = true) { - def closeHeader(): List[Connection] = - if (util.Random.nextInt(8) == 0) Connection("close") :: Nil - else Nil - - override def testServerHandler(connNr: Int): HttpRequest ⇒ HttpResponse = { r ⇒ - val idx = r.uri.path.tail.head.toString - HttpResponse() - .withHeaders(RawHeader("Req-Idx", idx) +: responseHeaders(r, connNr)) - .withDefaultHeaders(closeHeader()) - } - - for (pipeliningLimit ← Iterator.from(1).map(math.pow(2, _).toInt).take(4)) { - val settings = ConnectionPoolSettings(system).withMaxConnections(4).withPipeliningLimit(pipeliningLimit).withMaxOpenRequests(4 * pipeliningLimit) - val poolFlow = Http().cachedHostConnectionPool[Int](serverHostName, serverPort, settings = settings) - - def method() = - if (util.Random.nextInt(2) == 0) HttpMethods.POST else HttpMethods.GET - - def request(i: Int) = - HttpRequest(method = method(), headers = closeHeader(), uri = s"/$i") → i - - try { - val N = 200 - val (_, idSum) = - Source.fromIterator(() ⇒ Iterator.from(1)).take(N) - .map(request) - .viaMat(poolFlow)(Keep.right) - .map { - case (Success(response), id) ⇒ - requestUri(response) should endWith(s"/$id") - id - case x ⇒ fail(x.toString) - }.toMat(Sink.fold(0)(_ + _))(Keep.both).run() - - Await.result(idSum, 30.seconds) shouldEqual N * (N + 1) / 2 - } catch { - case thr: Throwable ⇒ - throw new RuntimeException(s"Failed at pipeliningLimit=$pipeliningLimit, poolFlow=$poolFlow", thr) - } - } - } - - class TestSetup( - serverSettings: ServerSettings = ServerSettings(system), - autoAccept: Boolean = false) { - val (serverEndpoint, serverHostName, serverPort) = TestUtils.temporaryServerHostnameAndPort() - - def testServerHandler(connNr: Int): HttpRequest ⇒ HttpResponse = { - case r: HttpRequest ⇒ HttpResponse(headers = responseHeaders(r, connNr), entity = r.entity) - } - - def responseHeaders(r: HttpRequest, connNr: Int) = - ConnNrHeader(connNr) +: RawHeader("Req-Uri", r.uri.toString) +: r.headers.map(h ⇒ RawHeader("Req-" + h.name, h.value)) - - def mapServerSideOutboundRawBytes(bytes: ByteString): ByteString = bytes - - val incomingConnectionCounter = new AtomicInteger - val incomingConnections = TestSubscriber.manualProbe[Http.IncomingConnection] - val incomingConnectionsSub = { - val rawBytesInjection = BidiFlow.fromFlows( - Flow[SslTlsOutbound].collect[ByteString] { case SendBytes(x) ⇒ mapServerSideOutboundRawBytes(x) } - .recover({ case NoErrorComplete ⇒ ByteString.empty }), - Flow[ByteString].map(SessionBytes(null, _))) - val sink = if (autoAccept) Sink.foreach[Http.IncomingConnection](handleConnection) else Sink.fromSubscriber(incomingConnections) - Tcp().bind(serverEndpoint.getHostString, serverEndpoint.getPort, idleTimeout = serverSettings.timeouts.idleTimeout) - .map { c ⇒ - val layer = Http().serverLayer(serverSettings, log = log) - Http.IncomingConnection(c.localAddress, c.remoteAddress, layer atop rawBytesInjection join c.flow) - }.runWith(sink) - if (autoAccept) null else incomingConnections.expectSubscription() - } - - def acceptIncomingConnection(): Unit = { - incomingConnectionsSub.request(1) - val conn = incomingConnections.expectNext() - handleConnection(conn) - } - - private def handleConnection(c: Http.IncomingConnection) = - c.handleWithSyncHandler(testServerHandler(incomingConnectionCounter.incrementAndGet())) - - def cachedHostConnectionPool[T]( - maxConnections: Int = 2, - minConnections: Int = 0, - maxRetries: Int = 2, - maxOpenRequests: Int = 8, - pipeliningLimit: Int = 1, - idleTimeout: Duration = 5.seconds, - ccSettings: ClientConnectionSettings = ClientConnectionSettings(system)) = { - - val settings = - new ConnectionPoolSettingsImpl(maxConnections, minConnections, - maxRetries, maxOpenRequests, pipeliningLimit, - idleTimeout, ccSettings) - flowTestBench( - Http().cachedHostConnectionPool[T](serverHostName, serverPort, settings)) - } - - def superPool[T]( - maxConnections: Int = 2, - minConnections: Int = 0, - maxRetries: Int = 2, - maxOpenRequests: Int = 8, - pipeliningLimit: Int = 1, - idleTimeout: Duration = 5.seconds, - ccSettings: ClientConnectionSettings = ClientConnectionSettings(system)) = { - val settings = new ConnectionPoolSettingsImpl(maxConnections, minConnections, maxRetries, maxOpenRequests, pipeliningLimit, - idleTimeout, ClientConnectionSettings(system)) - flowTestBench(Http().superPool[T](settings = settings)) - } - - def flowTestBench[T, Mat](poolFlow: Flow[(HttpRequest, T), (Try[HttpResponse], T), Mat]) = { - val requestIn = TestPublisher.probe[(HttpRequest, T)]() - val responseOut = TestSubscriber.manualProbe[(Try[HttpResponse], T)] - val hcp = Source.fromPublisher(requestIn).viaMat(poolFlow)(Keep.right).to(Sink.fromSubscriber(responseOut)).run() - val responseOutSub = responseOut.expectSubscription() - (requestIn, responseOut, responseOutSub, hcp) - } - - def connNr(r: HttpResponse): Int = r.headers.find(_ is "conn-nr").get.value.toInt - def requestUri(r: HttpResponse): String = r.headers.find(_ is "req-uri").get.value - - /** - * Makes sure the given condition "f" holds in the timer period of "in". - * The given condition function should throw if not met. - * Note: Execution of "condHolds" will take at least "in" time, so for big "in" it might drain the ime budget for tests. - */ - def condHolds[T](in: FiniteDuration)(f: () ⇒ T): T = { - val end = System.nanoTime.nanos + in - - var lastR = f() - while (System.nanoTime.nanos < end) { - lastR = f() - Thread.sleep(50) - } - lastR - } - } - - case class ConnNrHeader(nr: Int) extends CustomHeader { - def renderInRequests = false - def renderInResponses = true - def name = "Conn-Nr" - def value = nr.toString - } - - implicit class MustContain[T](specimen: Seq[T]) { - def mustContainLike(pf: PartialFunction[T, Unit]): Unit = - specimen.collectFirst(pf) getOrElse fail("did not contain") - } - - object NoErrorComplete extends SingletonException -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/client/HighLevelOutgoingConnectionSpec.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/client/HighLevelOutgoingConnectionSpec.scala deleted file mode 100644 index da2ad966f9..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/client/HighLevelOutgoingConnectionSpec.scala +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.client - -import java.util.concurrent.CountDownLatch - -import akka.http.impl.util.One2OneBidiFlow - -import scala.concurrent.{ Await, Future } -import scala.concurrent.duration._ -import akka.stream.{ ActorMaterializer, ActorMaterializerSettings, FlowShape, OverflowStrategy } -import akka.stream.scaladsl._ -import akka.testkit.AkkaSpec -import akka.http.scaladsl.{ Http, TestUtils } -import akka.http.scaladsl.model._ -import akka.stream.testkit.Utils -import org.scalatest.concurrent.PatienceConfiguration.Timeout - -class HighLevelOutgoingConnectionSpec extends AkkaSpec { - implicit val materializer = ActorMaterializer(ActorMaterializerSettings(system).withFuzzing(true)) - - "The connection-level client implementation" should { - - "be able to handle 100 pipelined requests across one connection" in Utils.assertAllStagesStopped { - val (_, serverHostName, serverPort) = TestUtils.temporaryServerHostnameAndPort() - - val binding = Http().bindAndHandleSync( - r ⇒ HttpResponse(entity = r.uri.toString.reverse.takeWhile(Character.isDigit).reverse), - serverHostName, serverPort) - - val N = 100 - val result = Source.fromIterator(() ⇒ Iterator.from(1)) - .take(N) - .map(id ⇒ HttpRequest(uri = s"/r$id")) - .via(Http().outgoingConnection(serverHostName, serverPort)) - .mapAsync(4)(_.entity.toStrict(1.second)) - .map { r ⇒ val s = r.data.utf8String; log.debug(s); s.toInt } - .runFold(0)(_ + _) - result.futureValue(Timeout(10.seconds)) should ===(N * (N + 1) / 2) - binding.futureValue.unbind() - } - - "be able to handle 100 pipelined requests across 4 connections (client-flow is reusable)" in Utils.assertAllStagesStopped { - val (_, serverHostName, serverPort) = TestUtils.temporaryServerHostnameAndPort() - - val binding = Http().bindAndHandleSync( - r ⇒ HttpResponse(entity = r.uri.toString.reverse.takeWhile(Character.isDigit).reverse), - serverHostName, serverPort) - - val connFlow = Http().outgoingConnection(serverHostName, serverPort) - - val C = 4 - val doubleConnection = Flow.fromGraph(GraphDSL.create() { implicit b ⇒ - import GraphDSL.Implicits._ - - val bcast = b.add(Broadcast[HttpRequest](C)) - val merge = b.add(Merge[HttpResponse](C)) - - for (i ← 0 until C) - bcast.out(i) ~> connFlow ~> merge.in(i) - FlowShape(bcast.in, merge.out) - }) - - val N = 100 - val result = Source.fromIterator(() ⇒ Iterator.from(1)) - .take(N) - .map(id ⇒ HttpRequest(uri = s"/r$id")) - .via(doubleConnection) - .mapAsync(4)(_.entity.toStrict(1.second)) - .map { r ⇒ val s = r.data.utf8String; log.debug(s); s.toInt } - .runFold(0)(_ + _) - - result.futureValue(Timeout(10.seconds)) should ===(C * N * (N + 1) / 2) - binding.futureValue.unbind() - } - - } -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/client/HttpConfigurationSpec.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/client/HttpConfigurationSpec.scala deleted file mode 100644 index 599ce124a3..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/client/HttpConfigurationSpec.scala +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.client - -import akka.actor.ActorSystem -import akka.http.scaladsl.settings.{ ClientConnectionSettings, ConnectionPoolSettings, ServerSettings } -import akka.testkit.AkkaSpec -import com.typesafe.config.ConfigFactory - -class HttpConfigurationSpec extends AkkaSpec { - - val On = true - val Off = false - - "Reference configurations" should { - "have default client and server `parsing` settings" in { - ServerSettings(system).parserSettings.toString should ===(ClientConnectionSettings(system).parserSettings.toString) - } - "have default client and pool `parsing` settings" in { - ServerSettings(system).parserSettings.toString should ===(ConnectionPoolSettings(system).connectionSettings.parserSettings.toString) - } - "have default client and pool `client` settings" in { - ClientConnectionSettings(system).toString should ===(ConnectionPoolSettings(system).connectionSettings.toString) - } - - "override value from `akka.http.parsing` by setting `akka.http.client.parsing`" in { - configuredSystem("""akka.http.client.parsing.illegal-header-warnings = off""") { sys ⇒ - val client = ClientConnectionSettings(sys) - client.parserSettings.illegalHeaderWarnings should ===(Off) - - val pool = ConnectionPoolSettings(sys) - pool.connectionSettings.parserSettings.illegalHeaderWarnings should ===(Off) - - val server = ServerSettings(sys) - server.parserSettings.illegalHeaderWarnings should ===(On) - } - } - - "override `akka.http.parsing` by setting `akka.http.host-connection-pool.client.parsing` setting" in { - configuredSystem("""akka.http.host-connection-pool.client.parsing.illegal-header-warnings = off""") { sys ⇒ - val client = ClientConnectionSettings(sys) - client.parserSettings.illegalHeaderWarnings should ===(On) - - val pool = ConnectionPoolSettings(sys) - pool.connectionSettings.parserSettings.illegalHeaderWarnings should ===(Off) - - val server = ServerSettings(sys) - server.parserSettings.illegalHeaderWarnings should ===(On) - } - } - - "set `akka.http.host-connection-pool.client.idle-timeout` only" in { - configuredSystem("""akka.http.host-connection-pool.client.idle-timeout = 1337s""") { sys ⇒ - import scala.concurrent.duration._ - - val client = ClientConnectionSettings(sys) - client.idleTimeout should ===(60.seconds) - - val pool = ConnectionPoolSettings(sys) - pool.connectionSettings.idleTimeout should ===(1337.seconds) - - val server = ServerSettings(sys) - server.idleTimeout should ===(60.seconds) // no change, default akka.http.server.idle-timeout - } - } - "set `akka.http.server.idle-timeout` only" in { - configuredSystem("""akka.http.server.idle-timeout = 1337s""") { sys ⇒ - import scala.concurrent.duration._ - - val client = ClientConnectionSettings(sys) - client.idleTimeout should ===(60.seconds) - - val pool = ConnectionPoolSettings(sys) - pool.connectionSettings.idleTimeout should ===(60.seconds) - - val server = ServerSettings(sys) - server.idleTimeout should ===(1337.seconds) - } - } - - "change parser settings for all by setting `akka.http.parsing`" in { - configuredSystem("""akka.http.parsing.illegal-header-warnings = off""") { sys ⇒ - val client = ClientConnectionSettings(sys) - client.parserSettings.illegalHeaderWarnings should ===(Off) - - val pool = ConnectionPoolSettings(sys) - pool.connectionSettings.parserSettings.illegalHeaderWarnings should ===(Off) - - val server = ServerSettings(sys) - server.parserSettings.illegalHeaderWarnings should ===(Off) - } - } - - "change parser settings for all by setting `akka.http.parsing`, unless client/server override it" in { - configuredSystem(""" - akka.http { - parsing.illegal-header-warnings = off - server.parsing.illegal-header-warnings = on - client.parsing.illegal-header-warnings = on // also affects host-connection-pool.client - }""") { sys ⇒ - val client = ClientConnectionSettings(sys) - client.parserSettings.illegalHeaderWarnings should ===(On) - - val pool = ConnectionPoolSettings(sys) - pool.connectionSettings.parserSettings.illegalHeaderWarnings should ===(On) - - val server = ServerSettings(sys) - server.parserSettings.illegalHeaderWarnings should ===(On) - } - } - - "change parser settings for all by setting `akka.http.parsing`, unless all override it" in { - configuredSystem(""" - akka.http { - parsing.illegal-header-warnings = off - server.parsing.illegal-header-warnings = on - client.parsing.illegal-header-warnings = on - host-connection-pool.client.parsing.illegal-header-warnings = off - }""") { sys ⇒ - val client = ClientConnectionSettings(sys) - client.parserSettings.illegalHeaderWarnings should ===(On) - - val pool = ConnectionPoolSettings(sys) - pool.connectionSettings.parserSettings.illegalHeaderWarnings should ===(Off) - - val server = ServerSettings(sys) - server.parserSettings.illegalHeaderWarnings should ===(On) - } - } - - "set `akka.http.host-connection-pool.min-connections` only" in { - configuredSystem( - """ - akka.http.host-connection-pool.min-connections = 42 - akka.http.host-connection-pool.max-connections = 43 - """.stripMargin) { sys ⇒ - - val pool = ConnectionPoolSettings(sys) - pool.getMinConnections should ===(42) - pool.getMaxConnections should ===(43) - } - - configuredSystem(""" """) { sys ⇒ - - val pool = ConnectionPoolSettings(sys) - pool.minConnections should ===(0) - } - - configuredSystem( - """ - akka.http.host-connection-pool.min-connections = 101 - akka.http.host-connection-pool.max-connections = 1 - """.stripMargin) { sys ⇒ - - intercept[IllegalArgumentException] { ConnectionPoolSettings(sys) } - } - } - } - - def configuredSystem(overrides: String)(block: ActorSystem ⇒ Unit) = { - val config = ConfigFactory.parseString(overrides).withFallback(ConfigFactory.load()) - // we go via ActorSystem in order to hit the settings caching infrastructure - val sys = ActorSystem("config-testing", config) - try block(sys) finally sys.terminate() - } - -} \ No newline at end of file diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/client/LowLevelOutgoingConnectionSpec.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/client/LowLevelOutgoingConnectionSpec.scala deleted file mode 100644 index 14a359351d..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/client/LowLevelOutgoingConnectionSpec.scala +++ /dev/null @@ -1,940 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.client - -import com.typesafe.config.ConfigFactory - -import scala.concurrent.duration._ -import scala.reflect.ClassTag -import org.scalatest.Inside -import akka.http.scaladsl.settings.ClientConnectionSettings -import akka.util.ByteString -import akka.event.NoLogging -import akka.stream.{ ClosedShape, ActorMaterializer } -import akka.stream.TLSProtocol._ -import akka.stream.testkit._ -import akka.stream.scaladsl._ -import akka.http.scaladsl.model.HttpEntity._ -import akka.http.scaladsl.model.HttpMethods._ -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers._ -import akka.http.impl.util._ -import akka.testkit.AkkaSpec - -class LowLevelOutgoingConnectionSpec extends AkkaSpec("akka.loggers = []\n akka.loglevel = OFF") with Inside { - implicit val materializer = ActorMaterializer() - - "The connection-level client implementation" should { - - "handle a request/response round-trip" which { - - "has a request with empty entity" in new TestSetup { - sendStandardRequest() - sendWireData( - """HTTP/1.1 200 OK - |Content-Length: 0 - | - |""") - - expectResponse() shouldEqual HttpResponse() - - requestsSub.sendComplete() - netOut.expectComplete() - netInSub.sendComplete() - responses.expectComplete() - } - - "has a request with default entity" in new TestSetup { - val probe = TestPublisher.manualProbe[ByteString]() - requestsSub.sendNext(HttpRequest(PUT, entity = HttpEntity(ContentTypes.`application/octet-stream`, 8, Source.fromPublisher(probe)))) - expectWireData( - """PUT / HTTP/1.1 - |Host: example.com - |User-Agent: akka-http/test - |Content-Type: application/octet-stream - |Content-Length: 8 - | - |""") - val sub = probe.expectSubscription() - sub.expectRequest() - sub.sendNext(ByteString("ABC")) - expectWireData("ABC") - sub.sendNext(ByteString("DEF")) - expectWireData("DEF") - sub.sendNext(ByteString("XY")) - expectWireData("XY") - sub.sendComplete() - - sendWireData( - """HTTP/1.1 200 OK - |Content-Length: 0 - | - |""") - - expectResponse() shouldEqual HttpResponse() - - requestsSub.sendComplete() - netOut.expectComplete() - netInSub.sendComplete() - responses.expectComplete() - } - - "has a response with a chunked entity" in new TestSetup { - sendStandardRequest() - sendWireData( - """HTTP/1.1 200 OK - |Transfer-Encoding: chunked - | - |""") - - val HttpResponse(_, _, HttpEntity.Chunked(ct, chunks), _) = expectResponse() - ct shouldEqual ContentTypes.`application/octet-stream` - - val probe = TestSubscriber.manualProbe[ChunkStreamPart]() - chunks.runWith(Sink.fromSubscriber(probe)) - val sub = probe.expectSubscription() - - sendWireData("3\nABC\n") - sub.request(1) - probe.expectNext(HttpEntity.Chunk("ABC")) - - sendWireData("4\nDEFX\n") - sub.request(1) - probe.expectNext(HttpEntity.Chunk("DEFX")) - - sendWireData("0\n\n") - sub.request(1) - probe.expectNext(HttpEntity.LastChunk) - sub.request(1) - probe.expectComplete() - - requestsSub.sendComplete() - netOut.expectComplete() - responses.expectComplete() - } - - "has a response with a chunked entity and Connection: close" in new TestSetup { - sendStandardRequest() - - sendWireData( - """HTTP/1.1 200 OK - |Transfer-Encoding: chunked - |Connection: close - | - |""") - sendWireData("3\nABC\n") - sendWireData("4\nDEFX\n") - sendWireData("0\n\n") - - val HttpResponse(_, _, HttpEntity.Chunked(ct, chunks), _) = expectResponse() - ct shouldEqual ContentTypes.`application/octet-stream` - - val probe = TestSubscriber.manualProbe[ChunkStreamPart]() - chunks.runWith(Sink.fromSubscriber(probe)) - val sub = probe.expectSubscription() - sub.request(4) - probe.expectNext(HttpEntity.Chunk("ABC")) - probe.expectNext(HttpEntity.Chunk("DEFX")) - probe.expectNext(HttpEntity.LastChunk) - probe.expectComplete() - - // explicit `requestsSub.sendComplete()` not needed - netOut.expectComplete() - responses.expectComplete() - } - - "has a request with a chunked entity and Connection: close" in new TestSetup { - requestsSub.sendNext(HttpRequest( - entity = HttpEntity(ContentTypes.`text/plain(UTF-8)`, Source(List("ABC", "DEFX").map(ByteString(_)))), - headers = List(Connection("close")) - )) - - expectWireData( - """GET / HTTP/1.1 - |Connection: close - |Host: example.com - |User-Agent: akka-http/test - |Transfer-Encoding: chunked - |Content-Type: text/plain; charset=UTF-8 - | - |""") - expectWireData("3\nABC\n") - expectWireData("4\nDEFX\n") - expectWireData("0\n\n") - - sendWireData( - """HTTP/1.1 200 OK - |Connection: close - |Content-Length: 0 - | - |""") - - expectResponse() - - // explicit `requestsSub.sendComplete()` not needed - responses.expectComplete() - netOut.expectComplete() - } - - "exhibits eager request stream completion" in new TestSetup { - requestsSub.sendNext(HttpRequest()) - requestsSub.sendComplete() - expectWireData( - """GET / HTTP/1.1 - |Host: example.com - |User-Agent: akka-http/test - | - |""") - - sendWireData( - """HTTP/1.1 200 OK - |Content-Length: 0 - | - |""") - - expectResponse() shouldEqual HttpResponse() - - netOut.expectComplete() - netInSub.sendComplete() - responses.expectComplete() - } - } - - "close the connection if response entity stream has been cancelled" in new TestSetup { - // two requests are sent in order to make sure that connection - // isn't immediately closed after the first one by the server - requestsSub.sendNext(HttpRequest()) - requestsSub.sendNext(HttpRequest()) - requestsSub.sendComplete() - - expectWireData( - """GET / HTTP/1.1 - |Host: example.com - |User-Agent: akka-http/test - | - |""") - - // two chunks sent by server - sendWireData( - """HTTP/1.1 200 OK - |Transfer-Encoding: chunked - | - |6 - |abcdef - |6 - |abcdef - |0 - | - |""") - - inside(expectResponse()) { - case HttpResponse(StatusCodes.OK, _, HttpEntity.Chunked(_, data), _) ⇒ - val dataProbe = TestSubscriber.manualProbe[ChunkStreamPart] - // but only one consumed by server - data.take(1).to(Sink.fromSubscriber(dataProbe)).run() - val sub = dataProbe.expectSubscription() - sub.request(1) - dataProbe.expectNext(Chunk(ByteString("abcdef"))) - dataProbe.expectComplete() - // connection is closed once requested elements are consumed - netInSub.expectCancellation() - } - } - - "proceed to next response once previous response's entity has been drained" in new TestSetup { - def twice(action: ⇒ Unit): Unit = { action; action } - - twice { - requestsSub.sendNext(HttpRequest()) - - expectWireData( - """GET / HTTP/1.1 - |Host: example.com - |User-Agent: akka-http/test - | - |""") - - sendWireData( - """HTTP/1.1 200 OK - |Transfer-Encoding: chunked - | - |6 - |abcdef - |0 - | - |""") - - val whenComplete = expectResponse().entity.dataBytes.runWith(Sink.ignore) - whenComplete.futureValue should be(akka.Done) - } - } - - "handle several requests on one persistent connection" which { - "has a first response that was chunked" in new TestSetup { - requestsSub.sendNext(HttpRequest()) - expectWireData( - """GET / HTTP/1.1 - |Host: example.com - |User-Agent: akka-http/test - | - |""") - - sendWireData( - """HTTP/1.1 200 OK - |Transfer-Encoding: chunked - | - |""") - - val HttpResponse(_, _, HttpEntity.Chunked(ct, chunks), _) = expectResponse() - - val probe = TestSubscriber.manualProbe[ChunkStreamPart]() - chunks.runWith(Sink.fromSubscriber(probe)) - val sub = probe.expectSubscription() - - sendWireData("3\nABC\n") - sub.request(1) - probe.expectNext(HttpEntity.Chunk("ABC")) - - sendWireData("0\n\n") - sub.request(1) - probe.expectNext(HttpEntity.LastChunk) - sub.request(1) - probe.expectComplete() - - // simulate that response is received before method bypass reaches response parser - sendWireData( - """HTTP/1.1 200 OK - |Content-Length: 0 - | - |""") - - responsesSub.request(1) - - requestsSub.sendNext(HttpRequest()) - expectWireData( - """GET / HTTP/1.1 - |Host: example.com - |User-Agent: akka-http/test - | - |""") - requestsSub.sendComplete() - responses.expectNext(HttpResponse()) - - netOut.expectComplete() - netInSub.sendComplete() - responses.expectComplete() - } - } - - "process the illegal response header value properly" which { - - val illegalChar = '\u0001' - val escapeChar = "\\u%04x" format illegalChar.toInt - - "catch illegal response header value by default" in new TestSetup { - sendStandardRequest() - sendWireData( - s"""HTTP/1.1 200 OK - |Some-Header: value1$illegalChar - |Other-Header: value2 - | - |""") - - responsesSub.request(1) - val error @ IllegalResponseException(info) = responses.expectError() - info.summary shouldEqual s"""Illegal character '$escapeChar' in header value""" - netOut.expectError(error) - requestsSub.expectCancellation() - netInSub.expectCancellation() - } - - val ignoreConfig = - """ - akka.http.parsing.illegal-response-header-value-processing-mode = ignore - """ - "ignore illegal response header value if setting the config to ignore" in new TestSetup(config = ignoreConfig) { - sendStandardRequest() - sendWireData( - s"""HTTP/1.1 200 OK - |Some-Header: value1$illegalChar - |Other-Header: value2 - | - |""") - - val HttpResponse(_, headers, _, _) = expectResponse() - val headerStr = headers.map(h ⇒ s"${h.name}: ${h.value}").mkString(",") - headerStr shouldEqual "Some-Header: value1,Other-Header: value2" - } - - val warnConfig = - """ - akka.http.parsing.illegal-response-header-value-processing-mode = warn - """ - "ignore illegal response header value and log a warning message if setting the config to warn" in new TestSetup(config = warnConfig) { - sendStandardRequest() - sendWireData( - s"""HTTP/1.1 200 OK - |Some-Header: value1$illegalChar - |Other-Header: value2 - | - |""") - - val HttpResponse(_, headers, _, _) = expectResponse() - val headerStr = headers.map(h ⇒ s"${h.name}: ${h.value}").mkString(",") - headerStr shouldEqual "Some-Header: value1,Other-Header: value2" - } - } - - "produce proper errors" which { - - "catch the request entity stream being shorter than the Content-Length" in new TestSetup { - val probe = TestPublisher.manualProbe[ByteString]() - requestsSub.sendNext(HttpRequest(PUT, entity = HttpEntity(ContentTypes.`application/octet-stream`, 8, Source.fromPublisher(probe)))) - expectWireData( - """PUT / HTTP/1.1 - |Host: example.com - |User-Agent: akka-http/test - |Content-Type: application/octet-stream - |Content-Length: 8 - | - |""") - val sub = probe.expectSubscription() - sub.expectRequest() - sub.sendNext(ByteString("ABC")) - expectWireData("ABC") - sub.sendNext(ByteString("DEF")) - expectWireData("DEF") - sub.sendComplete() - - val InvalidContentLengthException(info) = netOut.expectError() - info.summary shouldEqual "HTTP message had declared Content-Length 8 but entity data stream amounts to 2 bytes less" - netInSub.sendComplete() - responsesSub.request(1) - responses.expectError(One2OneBidiFlow.OutputTruncationException) - } - - "catch the request entity stream being longer than the Content-Length" in new TestSetup { - val probe = TestPublisher.manualProbe[ByteString]() - requestsSub.sendNext(HttpRequest(PUT, entity = HttpEntity(ContentTypes.`application/octet-stream`, 8, Source.fromPublisher(probe)))) - expectWireData( - """PUT / HTTP/1.1 - |Host: example.com - |User-Agent: akka-http/test - |Content-Type: application/octet-stream - |Content-Length: 8 - | - |""") - val sub = probe.expectSubscription() - sub.expectRequest() - sub.sendNext(ByteString("ABC")) - expectWireData("ABC") - sub.sendNext(ByteString("DEF")) - expectWireData("DEF") - sub.sendNext(ByteString("XYZ")) - - val InvalidContentLengthException(info) = netOut.expectError() - info.summary shouldEqual "HTTP message had declared Content-Length 8 but entity data stream amounts to more bytes" - netInSub.sendComplete() - responsesSub.request(1) - responses.expectError(One2OneBidiFlow.OutputTruncationException) - } - - "catch illegal response starts" in new TestSetup { - sendStandardRequest() - sendWireData( - """HTTP/1.2 200 OK - | - |""") - - responsesSub.request(1) - val error @ IllegalResponseException(info) = responses.expectError() - info.summary shouldEqual "The server-side HTTP version is not supported" - netOut.expectError(error) - requestsSub.expectCancellation() - netInSub.expectCancellation() - } - - "catch illegal response chunks" in new TestSetup { - sendStandardRequest() - sendWireData( - """HTTP/1.1 200 OK - |Transfer-Encoding: chunked - | - |""") - - responsesSub.request(1) - val HttpResponse(_, _, HttpEntity.Chunked(ct, chunks), _) = responses.expectNext() - ct shouldEqual ContentTypes.`application/octet-stream` - - val probe = TestSubscriber.manualProbe[ChunkStreamPart]() - chunks.runWith(Sink.fromSubscriber(probe)) - val sub = probe.expectSubscription() - - sendWireData("3\nABC\n") - sub.request(1) - probe.expectNext(HttpEntity.Chunk("ABC")) - - sendWireData("4\nDEFXX") - sub.request(1) - val error @ EntityStreamException(info) = probe.expectError() - info.summary shouldEqual "Illegal chunk termination" - - responses.expectComplete() - netOut.expectComplete() - requestsSub.expectCancellation() - netInSub.expectCancellation() - } - - "catch a response start truncation" in new TestSetup { - sendStandardRequest() - sendWireData("HTTP/1.1 200 OK") - netInSub.sendComplete() - - responsesSub.request(1) - val error @ IllegalResponseException(info) = responses.expectError() - info.summary shouldEqual "Illegal HTTP message start" - netOut.expectError(error) - requestsSub.expectCancellation() - } - } - - def isDefinedVia = afterWord("is defined via") - "support response length verification" which isDefinedVia { - import HttpEntity._ - - class LengthVerificationTest(maxContentLength: Int) extends TestSetup(maxContentLength) { - val entityBase = "0123456789ABCD" - - def sendStrictResponseWithLength(bytes: Int) = - sendWireData( - s"""HTTP/1.1 200 OK - |Content-Length: $bytes - | - |${entityBase take bytes}""") - def sendDefaultResponseWithLength(bytes: Int) = { - sendWireData( - s"""HTTP/1.1 200 OK - |Content-Length: $bytes - | - |${entityBase take 3}""") - sendWireData(entityBase.slice(3, 7)) - sendWireData(entityBase.slice(7, bytes)) - } - def sendChunkedResponseWithLength(bytes: Int) = - sendWireData( - s"""HTTP/1.1 200 OK - |Transfer-Encoding: chunked - | - |3 - |${entityBase take 3} - |4 - |${entityBase.slice(3, 7)} - |${bytes - 7} - |${entityBase.slice(7, bytes)} - |0 - | - |""") - def sendCloseDelimitedResponseWithLength(bytes: Int) = { - sendWireData( - s"""HTTP/1.1 200 OK - | - |${entityBase take 3}""") - sendWireData(entityBase.slice(3, 7)) - sendWireData(entityBase.slice(7, bytes)) - netInSub.sendComplete() - } - - implicit class XResponse(response: HttpResponse) { - def expectStrictEntityWithLength(bytes: Int) = - response shouldEqual HttpResponse( - entity = Strict(ContentTypes.`application/octet-stream`, ByteString(entityBase take bytes))) - - def expectEntity[T <: HttpEntity: ClassTag](bytes: Int) = - inside(response) { - case HttpResponse(_, _, entity: T, _) ⇒ - entity.toStrict(100.millis).awaitResult(100.millis).data.utf8String shouldEqual entityBase.take(bytes) - } - - def expectSizeErrorInEntityOfType[T <: HttpEntity: ClassTag](limit: Int, actualSize: Option[Long] = None) = - inside(response) { - case HttpResponse(_, _, entity: T, _) ⇒ - def gatherBytes = entity.dataBytes.runFold(ByteString.empty)(_ ++ _).awaitResult(100.millis) - (the[Exception] thrownBy gatherBytes).getCause shouldEqual EntityStreamSizeException(limit, actualSize) - } - } - } - - "the config setting (strict entity)" in new LengthVerificationTest(maxContentLength = 10) { - sendStandardRequest() - sendStrictResponseWithLength(10) - expectResponse().expectStrictEntityWithLength(10) - - // entities that would be strict but have a Content-Length > the configured maximum are delivered - // as single element Default entities! - sendStandardRequest() - sendStrictResponseWithLength(11) - expectResponse().expectSizeErrorInEntityOfType[Default](limit = 10, actualSize = Some(11)) - } - - "the config setting (default entity)" in new LengthVerificationTest(maxContentLength = 10) { - sendStandardRequest() - sendDefaultResponseWithLength(10) - expectResponse().expectEntity[Default](10) - - sendStandardRequest() - sendDefaultResponseWithLength(11) - expectResponse().expectSizeErrorInEntityOfType[Default](limit = 10, actualSize = Some(11)) - } - - "the config setting (chunked entity)" in new LengthVerificationTest(maxContentLength = 10) { - sendStandardRequest() - sendChunkedResponseWithLength(10) - expectResponse().expectEntity[Chunked](10) - - sendStandardRequest() - sendChunkedResponseWithLength(11) - expectResponse().expectSizeErrorInEntityOfType[Chunked](limit = 10) - } - - "the config setting (close-delimited entity)" in { - new LengthVerificationTest(maxContentLength = 10) { - sendStandardRequest() - sendCloseDelimitedResponseWithLength(10) - expectResponse().expectEntity[CloseDelimited](10) - } - new LengthVerificationTest(maxContentLength = 10) { - sendStandardRequest() - sendCloseDelimitedResponseWithLength(11) - expectResponse().expectSizeErrorInEntityOfType[CloseDelimited](limit = 10) - } - } - - "a smaller programmatically-set limit (strict entity)" in new LengthVerificationTest(maxContentLength = 12) { - sendStandardRequest() - sendStrictResponseWithLength(10) - expectResponse().mapEntity(_ withSizeLimit 10).expectStrictEntityWithLength(10) - - // entities that would be strict but have a Content-Length > the configured maximum are delivered - // as single element Default entities! - sendStandardRequest() - sendStrictResponseWithLength(11) - expectResponse().mapEntity(_ withSizeLimit 10) - .expectSizeErrorInEntityOfType[Default](limit = 10, actualSize = Some(11)) - } - - "a smaller programmatically-set limit (default entity)" in new LengthVerificationTest(maxContentLength = 12) { - sendStandardRequest() - sendDefaultResponseWithLength(10) - expectResponse().mapEntity(_ withSizeLimit 10).expectEntity[Default](10) - - sendStandardRequest() - sendDefaultResponseWithLength(11) - expectResponse().mapEntity(_ withSizeLimit 10) - .expectSizeErrorInEntityOfType[Default](limit = 10, actualSize = Some(11)) - } - - "a smaller programmatically-set limit (chunked entity)" in new LengthVerificationTest(maxContentLength = 12) { - sendStandardRequest() - sendChunkedResponseWithLength(10) - expectResponse().mapEntity(_ withSizeLimit 10).expectEntity[Chunked](10) - - sendStandardRequest() - sendChunkedResponseWithLength(11) - expectResponse().mapEntity(_ withSizeLimit 10).expectSizeErrorInEntityOfType[Chunked](limit = 10) - } - - "a smaller programmatically-set limit (close-delimited entity)" in { - new LengthVerificationTest(maxContentLength = 12) { - sendStandardRequest() - sendCloseDelimitedResponseWithLength(10) - expectResponse().mapEntity(_ withSizeLimit 10).expectEntity[CloseDelimited](10) - } - new LengthVerificationTest(maxContentLength = 12) { - sendStandardRequest() - sendCloseDelimitedResponseWithLength(11) - expectResponse().mapEntity(_ withSizeLimit 10).expectSizeErrorInEntityOfType[CloseDelimited](limit = 10) - } - } - - "a larger programmatically-set limit (strict entity)" in new LengthVerificationTest(maxContentLength = 8) { - // entities that would be strict but have a Content-Length > the configured maximum are delivered - // as single element Default entities! - sendStandardRequest() - sendStrictResponseWithLength(10) - expectResponse().mapEntity(_ withSizeLimit 10).expectEntity[Default](10) - - sendStandardRequest() - sendStrictResponseWithLength(11) - expectResponse().mapEntity(_ withSizeLimit 10) - .expectSizeErrorInEntityOfType[Default](limit = 10, actualSize = Some(11)) - } - - "a larger programmatically-set limit (default entity)" in new LengthVerificationTest(maxContentLength = 8) { - sendStandardRequest() - sendDefaultResponseWithLength(10) - expectResponse().mapEntity(_ withSizeLimit 10).expectEntity[Default](10) - - sendStandardRequest() - sendDefaultResponseWithLength(11) - expectResponse().mapEntity(_ withSizeLimit 10) - .expectSizeErrorInEntityOfType[Default](limit = 10, actualSize = Some(11)) - } - - "a larger programmatically-set limit (chunked entity)" in new LengthVerificationTest(maxContentLength = 8) { - sendStandardRequest() - sendChunkedResponseWithLength(10) - expectResponse().mapEntity(_ withSizeLimit 10).expectEntity[Chunked](10) - - sendStandardRequest() - sendChunkedResponseWithLength(11) - expectResponse().mapEntity(_ withSizeLimit 10) - .expectSizeErrorInEntityOfType[Chunked](limit = 10) - } - - "a larger programmatically-set limit (close-delimited entity)" in { - new LengthVerificationTest(maxContentLength = 8) { - sendStandardRequest() - sendCloseDelimitedResponseWithLength(10) - expectResponse().mapEntity(_ withSizeLimit 10).expectEntity[CloseDelimited](10) - } - new LengthVerificationTest(maxContentLength = 8) { - sendStandardRequest() - sendCloseDelimitedResponseWithLength(11) - expectResponse().mapEntity(_ withSizeLimit 10).expectSizeErrorInEntityOfType[CloseDelimited](limit = 10) - } - } - } - - "support requests with an `Expect: 100-continue` headers" which { - - "have a strict entity and receive a `100 Continue` response" in new TestSetup { - requestsSub.sendNext(HttpRequest(POST, headers = List(Expect.`100-continue`), entity = "ABCDEF")) - expectWireData( - """POST / HTTP/1.1 - |Expect: 100-continue - |Host: example.com - |User-Agent: akka-http/test - |Content-Type: text/plain; charset=UTF-8 - |Content-Length: 6 - | - |""") - netOutSub.request(1) - netOut.expectNoMsg(50.millis) - - sendWireData( - """HTTP/1.1 100 Continue - | - |""") - - netOut.expectNext().utf8String shouldEqual "ABCDEF" - - sendWireData( - """HTTP/1.1 200 OK - |Content-Length: 0 - | - |""") - - expectResponse() shouldEqual HttpResponse() - - requestsSub.sendComplete() - netOut.expectComplete() - netInSub.sendComplete() - responses.expectComplete() - } - - "have a default entity and receive a `100 Continue` response" in new TestSetup { - val entityParts = List("ABC", "DE", "FGH").map(ByteString(_)) - requestsSub.sendNext(HttpRequest(POST, headers = List(Expect.`100-continue`), - entity = HttpEntity(ContentTypes.`application/octet-stream`, 8, Source(entityParts)))) - expectWireData( - """POST / HTTP/1.1 - |Expect: 100-continue - |Host: example.com - |User-Agent: akka-http/test - |Content-Type: application/octet-stream - |Content-Length: 8 - | - |""") - netOutSub.request(1) - netOut.expectNoMsg(50.millis) - - sendWireData( - """HTTP/1.1 100 Continue - | - |""") - - netOut.expectNext().utf8String shouldEqual "ABC" - expectWireData("DE") - expectWireData("FGH") - - sendWireData( - """HTTP/1.1 200 OK - |Content-Length: 0 - | - |""") - - expectResponse() shouldEqual HttpResponse() - - requestsSub.sendComplete() - netOut.expectComplete() - netInSub.sendComplete() - responses.expectComplete() - } - - "receive a normal response" in new TestSetup { - requestsSub.sendNext(HttpRequest(POST, headers = List(Expect.`100-continue`), entity = "ABCDEF")) - expectWireData( - """POST / HTTP/1.1 - |Expect: 100-continue - |Host: example.com - |User-Agent: akka-http/test - |Content-Type: text/plain; charset=UTF-8 - |Content-Length: 6 - | - |""") - netOutSub.request(1) - netOut.expectNoMsg(50.millis) - - sendWireData( - """HTTP/1.1 200 OK - |Content-Length: 0 - | - |""") - - expectResponse() shouldEqual HttpResponse() - - expectWireData("ABCDEF") - - requestsSub.sendComplete() - netOut.expectComplete() - netInSub.sendComplete() - responses.expectComplete() - } - - "receive an error response" in new TestSetup { - requestsSub.sendNext(HttpRequest(POST, headers = List(Expect.`100-continue`), entity = "ABCDEF")) - requestsSub.sendComplete() - expectWireData( - """POST / HTTP/1.1 - |Expect: 100-continue - |Host: example.com - |User-Agent: akka-http/test - |Content-Type: text/plain; charset=UTF-8 - |Content-Length: 6 - | - |""") - netOutSub.request(1) - netOut.expectNoMsg(50.millis) - - sendWireData( - """HTTP/1.1 400 Bad Request - |Content-Length: 0 - | - |""") - - expectResponse() shouldEqual HttpResponse(400) - - netOut.expectComplete() - netInSub.sendComplete() - responses.expectComplete() - } - } - - "ignore interim 1xx responses" in new TestSetup { - sendStandardRequest() - sendWireData( - """HTTP/1.1 102 Processing - |Content-Length: 0 - | - |""") - sendWireData( - """HTTP/1.1 102 Processing - |Content-Length: 0 - | - |""") - sendWireData( - """HTTP/1.1 200 OK - |Content-Length: 0 - | - |""") - - expectResponse() shouldEqual HttpResponse() - - requestsSub.sendComplete() - netOut.expectComplete() - netInSub.sendComplete() - responses.expectComplete() - } - } - - class TestSetup(maxResponseContentLength: Int = -1, config: String = "") { - val requests = TestPublisher.manualProbe[HttpRequest]() - val responses = TestSubscriber.manualProbe[HttpResponse]() - - def settings = { - val s = ClientConnectionSettings( - ConfigFactory.parseString(config).withFallback(system.settings.config) - ).withUserAgentHeader(Some(`User-Agent`(List(ProductVersion("akka-http", "test"))))) - if (maxResponseContentLength < 0) s - else s.withParserSettings(s.parserSettings.withMaxContentLength(maxResponseContentLength)) - } - - val (netOut, netIn) = { - val netOut = TestSubscriber.manualProbe[ByteString]() - val netIn = TestPublisher.manualProbe[ByteString]() - - RunnableGraph.fromGraph(GraphDSL.create(OutgoingConnectionBlueprint(Host("example.com"), settings, NoLogging)) { implicit b ⇒ client ⇒ - import GraphDSL.Implicits._ - Source.fromPublisher(netIn) ~> Flow[ByteString].map(SessionBytes(null, _)) ~> client.in2 - client.out1 ~> Flow[SslTlsOutbound].collect { case SendBytes(x) ⇒ x } ~> Sink.fromSubscriber(netOut) - Source.fromPublisher(requests) ~> client.in1 - client.out2 ~> Sink.fromSubscriber(responses) - ClosedShape - }).run() - - netOut → netIn - } - - def wipeDate(string: String) = - string.fastSplit('\n').map { - case s if s.startsWith("Date:") ⇒ "Date: XXXX\r" - case s ⇒ s - }.mkString("\n") - - val netInSub = netIn.expectSubscription() - val netOutSub = netOut.expectSubscription() - val requestsSub = requests.expectSubscription() - val responsesSub = responses.expectSubscription() - - requestsSub.expectRequest(16) - netInSub.expectRequest(16) - - def sendWireData(data: String): Unit = sendWireData(ByteString(data.stripMarginWithNewline("\r\n"), "ASCII")) - def sendWireData(data: ByteString): Unit = netInSub.sendNext(data) - - def expectWireData(s: String) = { - netOutSub.request(1) - netOut.expectNext().utf8String shouldEqual s.stripMarginWithNewline("\r\n") - } - - def closeNetworkInput(): Unit = netInSub.sendComplete() - - def sendStandardRequest() = { - requestsSub.sendNext(HttpRequest()) - expectWireData( - """GET / HTTP/1.1 - |Host: example.com - |User-Agent: akka-http/test - | - |""") - } - - def expectResponse() = { - responsesSub.request(1) - responses.expectNext() - } - - } -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/client/PrepareResponseSpec.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/client/PrepareResponseSpec.scala deleted file mode 100644 index b373820064..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/client/PrepareResponseSpec.scala +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright (C) 2016 Lightbend Inc. - */ -package akka.http.impl.engine.client - -import akka.http.impl.engine.client.OutgoingConnectionBlueprint.PrepareResponse -import akka.http.impl.engine.parsing.ParserOutput -import akka.http.impl.engine.parsing.ParserOutput.{ StrictEntityCreator, EntityStreamError, EntityChunk, StreamedEntityCreator } -import akka.http.scaladsl.model._ -import akka.http.scaladsl.settings.ParserSettings -import akka.stream.{ ActorMaterializer, Attributes } -import akka.stream.scaladsl.{ Sink, Source } -import akka.stream.testkit.{ TestSubscriber, TestPublisher } -import akka.util.ByteString -import akka.testkit.AkkaSpec - -class PrepareResponseSpec extends AkkaSpec { - - val parserSettings = ParserSettings(system) - - val chunkedStart = ParserOutput.ResponseStart( - StatusCodes.OK, - HttpProtocols.`HTTP/1.1`, - List(), - StreamedEntityCreator[ParserOutput, ResponseEntity] { entityChunks ⇒ - val chunks = entityChunks.collect { - case EntityChunk(chunk) ⇒ chunk - case EntityStreamError(info) ⇒ throw EntityStreamException(info) - } - HttpEntity.Chunked(ContentTypes.`application/octet-stream`, HttpEntity.limitableChunkSource(chunks)) - }, - closeRequested = false) - - val strictStart = ParserOutput.ResponseStart( - StatusCodes.OK, - HttpProtocols.`HTTP/1.1`, - List(), - StrictEntityCreator(HttpEntity("body")), - closeRequested = false) - - val chunk = ParserOutput.EntityChunk(HttpEntity.ChunkStreamPart("abc")) - - val messageEnd = ParserOutput.MessageEnd - - "The PrepareRequest stage" should { - - "not lose demand that comes in while streaming entity" in { - implicit val mat = ActorMaterializer() - - val inProbe = TestPublisher.manualProbe[ParserOutput.ResponseOutput]() - val responseProbe = TestSubscriber.manualProbe[HttpResponse] - - Source.fromPublisher(inProbe) - .via(new PrepareResponse(parserSettings)) - .to(Sink.fromSubscriber(responseProbe)) - .withAttributes(Attributes.inputBuffer(1, 1)) - .run() - - val inSub = inProbe.expectSubscription() - val responseSub = responseProbe.expectSubscription() - - responseSub.request(1) - inSub.expectRequest(1) - inSub.sendNext(chunkedStart) - val response = responseProbe.expectNext() - - val entityProbe = TestSubscriber.manualProbe[ByteString]() - response.entity.dataBytes.to(Sink.fromSubscriber(entityProbe)).run() - val entitySub = entityProbe.expectSubscription() - - entitySub.request(1) - inSub.expectRequest(1) - inSub.sendNext(chunk) - entityProbe.expectNext() - - // now, before entity stream has completed - // there is upstream demand - responseSub.request(1) - - // then chunk completes - entitySub.request(1) - inSub.expectRequest(1) - inSub.sendNext(messageEnd) - entityProbe.expectComplete() - - // and that demand should go downstream - // since the chunk end was consumed by the stage - inSub.expectRequest(1) - - } - - "not lose demand that comes in while handling strict entity" in { - implicit val mat = ActorMaterializer() - - val inProbe = TestPublisher.manualProbe[ParserOutput.ResponseOutput]() - val responseProbe = TestSubscriber.manualProbe[HttpResponse] - - Source.fromPublisher(inProbe) - .via(new PrepareResponse(parserSettings)) - .to(Sink.fromSubscriber(responseProbe)) - .withAttributes(Attributes.inputBuffer(1, 1)) - .run() - - val inSub = inProbe.expectSubscription() - val responseSub = responseProbe.expectSubscription() - - responseSub.request(1) - inSub.expectRequest(1) - inSub.sendNext(strictStart) - val response = responseProbe.expectNext() - - // now, before the strict message has completed - // there is upstream demand - responseSub.request(1) - - // then chunk completes - inSub.expectRequest(1) - inSub.sendNext(messageEnd) - - // and that demand should go downstream - // since the chunk end was consumed by the stage - inSub.expectRequest(1) - - } - - "complete entity stream then complete stage when downstream cancels" in { - // to make it possible to cancel a big file download for example - // without downloading the entire response first - implicit val mat = ActorMaterializer() - - val inProbe = TestPublisher.manualProbe[ParserOutput.ResponseOutput]() - val responseProbe = TestSubscriber.manualProbe[HttpResponse] - - Source.fromPublisher(inProbe) - .via(new PrepareResponse(parserSettings)) - .to(Sink.fromSubscriber(responseProbe)) - .withAttributes(Attributes.inputBuffer(1, 1)) - .run() - - val inSub = inProbe.expectSubscription() - val responseSub = responseProbe.expectSubscription() - - responseSub.request(1) - inSub.expectRequest(1) - inSub.sendNext(chunkedStart) - val response = responseProbe.expectNext() - - val entityProbe = TestSubscriber.manualProbe[ByteString]() - response.entity.dataBytes.to(Sink.fromSubscriber(entityProbe)).run() - val entitySub = entityProbe.expectSubscription() - - entitySub.request(1) - inSub.expectRequest(1) - inSub.sendNext(chunk) - entityProbe.expectNext() - - // now before entity stream is completed, - // upstream cancels - responseSub.cancel() - - entitySub.request(1) - inSub.expectRequest(1) - inSub.sendNext(messageEnd) - - entityProbe.expectComplete() - inSub.expectCancellation() - } - - "complete stage when downstream cancels before end of strict request has arrived" in { - implicit val mat = ActorMaterializer() - - val inProbe = TestPublisher.manualProbe[ParserOutput.ResponseOutput]() - val responseProbe = TestSubscriber.manualProbe[HttpResponse] - - Source.fromPublisher(inProbe) - .via(new PrepareResponse(parserSettings)) - .to(Sink.fromSubscriber(responseProbe)) - .withAttributes(Attributes.inputBuffer(1, 1)) - .run() - - val inSub = inProbe.expectSubscription() - val responseSub = responseProbe.expectSubscription() - - responseSub.request(1) - inSub.expectRequest(1) - inSub.sendNext(strictStart) - val response = responseProbe.expectNext() - - // now before end of message has arrived - // downstream cancels - responseSub.cancel() - - // which should cancel the stage - inSub.expectCancellation() - } - - "cancel entire stage when the entity stream is canceled" in { - implicit val mat = ActorMaterializer() - - val inProbe = TestPublisher.manualProbe[ParserOutput.ResponseOutput]() - val responseProbe = TestSubscriber.manualProbe[HttpResponse] - - Source.fromPublisher(inProbe) - .via(new PrepareResponse(parserSettings)) - .to(Sink.fromSubscriber(responseProbe)) - .withAttributes(Attributes.inputBuffer(1, 1)) - .run() - - val inSub = inProbe.expectSubscription() - val responseSub = responseProbe.expectSubscription() - - responseSub.request(1) - inSub.expectRequest(1) - inSub.sendNext(chunkedStart) - val response = responseProbe.expectNext() - - val entityProbe = TestSubscriber.manualProbe[ByteString]() - response.entity.dataBytes.to(Sink.fromSubscriber(entityProbe)).run() - val entitySub = entityProbe.expectSubscription() - - entitySub.request(1) - inSub.expectRequest(1) - inSub.sendNext(chunk) - entityProbe.expectNext() - - // now, before entity stream has completed - // it is cancelled - entitySub.cancel() - - // this means that the entire stage should - // cancel - inSub.expectCancellation() - - } - - } - -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/client/TlsEndpointVerificationSpec.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/client/TlsEndpointVerificationSpec.scala deleted file mode 100644 index 41bb35fe97..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/client/TlsEndpointVerificationSpec.scala +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.client - -import akka.NotUsed -import org.scalatest.concurrent.PatienceConfiguration.Timeout -import org.scalatest.concurrent.ScalaFutures -import akka.stream.{ Server, Client, ActorMaterializer } -import akka.stream.scaladsl._ -import akka.testkit.AkkaSpec -import akka.http.impl.util._ -import akka.http.scaladsl.{ ConnectionContext, Http } -import akka.http.scaladsl.model.{ StatusCodes, HttpResponse, HttpRequest } -import akka.http.scaladsl.model.headers.{ Host, `Tls-Session-Info` } -import org.scalatest.time.{ Span, Seconds } -import scala.concurrent.Future - -class TlsEndpointVerificationSpec extends AkkaSpec(""" - akka.loglevel = INFO - akka.io.tcp.trace-logging = off - akka.http.parsing.tls-session-info-header = on - """) { - implicit val materializer = ActorMaterializer() - - /* - * Useful when debugging against "what if we hit a real website" - */ - val includeTestsHittingActualWebsites = false - - val timeout = Timeout(Span(3, Seconds)) - - "The client implementation" should { - "not accept certificates signed by unknown CA" in { - val pipe = pipeline(Http().defaultClientHttpsContext, hostname = "akka.example.org") // default context doesn't include custom CA - - whenReady(pipe(HttpRequest(uri = "https://akka.example.org/")).failed, timeout) { e ⇒ - e shouldBe an[Exception] - } - } - "accept certificates signed by known CA" in { - val pipe = pipeline(ExampleHttpContexts.exampleClientContext, hostname = "akka.example.org") // example context does include custom CA - - whenReady(pipe(HttpRequest(uri = "https://akka.example.org:8080/")), timeout) { response ⇒ - response.status shouldEqual StatusCodes.OK - val tlsInfo = response.header[`Tls-Session-Info`].get - tlsInfo.peerPrincipal.get.getName shouldEqual "CN=akka.example.org,O=Internet Widgits Pty Ltd,ST=Some-State,C=AU" - } - } - "not accept certificates for foreign hosts" in { - val pipe = pipeline(ExampleHttpContexts.exampleClientContext, hostname = "hijack.de") // example context does include custom CA - - whenReady(pipe(HttpRequest(uri = "https://hijack.de/")).failed, timeout) { e ⇒ - e shouldBe an[Exception] - } - } - - if (includeTestsHittingActualWebsites) { - /* - * Requires the following DNS spoof to be running: - * sudo /usr/local/bin/python ./dnschef.py --fakedomains www.howsmyssl.com --fakeip 54.173.126.144 - * - * Read up about it on: https://tersesystems.com/2014/03/31/testing-hostname-verification/ - */ - "fail hostname verification on spoofed https://www.howsmyssl.com/" in { - val req = HttpRequest(uri = "https://www.howsmyssl.com/") - val ex = intercept[Exception] { - Http().singleRequest(req).futureValue - } - // JDK built-in verification - val expectedMsg = "No subject alternative DNS name matching www.howsmyssl.com found" - - var e: Throwable = ex - while (e.getCause != null) e = e.getCause - - info("TLS failure cause: " + e.getMessage) - e.getMessage should include(expectedMsg) - } - - "pass hostname verification on https://www.playframework.com/" in { - val req = HttpRequest(uri = "https://www.playframework.com/") - val res = Http().singleRequest(req).futureValue - res.status should ===(StatusCodes.OK) - } - } - } - - def pipeline(clientContext: ConnectionContext, hostname: String): HttpRequest ⇒ Future[HttpResponse] = req ⇒ - Source.single(req).via(pipelineFlow(clientContext, hostname)).runWith(Sink.head) - - def pipelineFlow(clientContext: ConnectionContext, hostname: String): Flow[HttpRequest, HttpResponse, NotUsed] = { - val handler: HttpRequest ⇒ HttpResponse = { req ⇒ - // verify Tls-Session-Info header information - val name = req.header[`Tls-Session-Info`].flatMap(_.localPrincipal).map(_.getName) - if (name.exists(_ == "CN=akka.example.org,O=Internet Widgits Pty Ltd,ST=Some-State,C=AU")) HttpResponse() - else HttpResponse(StatusCodes.BadRequest, entity = "Tls-Session-Info header verification failed") - } - - val serverSideTls = Http().sslTlsStage(ExampleHttpContexts.exampleServerContext, Server) - val clientSideTls = Http().sslTlsStage(clientContext, Client, Some(hostname → 8080)) - - val server = - Http().serverLayer() - .atop(serverSideTls) - .reversed - .join(Flow[HttpRequest].map(handler)) - - val client = - Http().clientLayer(Host(hostname, 8080)) - .atop(clientSideTls) - - client.join(server) - } -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/parsing/BoyerMooreSpec.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/parsing/BoyerMooreSpec.scala deleted file mode 100644 index 8a8a9a336d..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/parsing/BoyerMooreSpec.scala +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.parsing - -import java.util.regex.Pattern -import scala.annotation.tailrec -import scala.util.Random -import org.scalatest.{ Matchers, WordSpec } -import akka.util.ByteString - -class BoyerMooreSpec extends WordSpec with Matchers { - - "The Boyer Moore implementation" should { - - "correctly find all matches of a few handwritten string-search examples" in { - findString("foo", "the foe in moofoo is foobar") shouldEqual Seq(14, 21) - findString("ana", "bananas") shouldEqual Seq(1, 3) - findString("anna", "bananas") shouldEqual Seq() - } - - "perform identically to a regex search" in { - val random = new Random() - // the alphabet base is a random shuffle of 8 distinct alphanumeric chars - val alphabetBase: IndexedSeq[Byte] = random.shuffle(0 to 255).take(8).map(_.toByte) - - val haystackLen = 1000 - (0 to 9) foreach { run ⇒ - val alphabet = alphabetBase.take(4 + random.nextInt(5)) // 4 to 8 distinct alphanumeric chars - val randomAlphabetChars = Stream.continually(alphabet(random.nextInt(alphabet.length))) - def randomBytes(num: Int): ByteString = ByteString(randomAlphabetChars.take(num): _*) - val haystack = randomBytes(haystackLen) - val needle = randomBytes(run / 3 + 3) // 3 to 6 random alphabet chars - - val bmFinds = find(needle, haystack, skipFindsThatStartInFinds = true) - val reFinds = findWithRegex(needle, haystack) - if (bmFinds != reFinds) { - def showBytes(bs: Seq[Byte]): String = bs.map(b ⇒ (b & 0xff).formatted("%02x")).mkString(" ") - def len(num: Int) = num * 2 + math.max(0, num - 1) - - def showFind(ix: Int): String = { - val startIdx = math.max(ix - 8, 0) - val endIdx = math.min(ix + needle.length + 8, haystack.length) - - s"""...${showBytes(haystack.drop(startIdx).take(endIdx - startIdx))}... - |${" " * (3 + math.min(8, ix) * 3)}${"^" * len(needle.length)} - |""".stripMargin - } - val foundOnlyByBM = bmFinds.filterNot(reFinds.contains).map(showFind).mkString - val foundOnlyByRE = reFinds.filterNot(bmFinds.contains).map(showFind).mkString - fail(s"""alphabet: ${showBytes(alphabet)} - |needle: ${showBytes(needle)} - |found only by boyer moore: - |$foundOnlyByBM - |found only by regex: - |$foundOnlyByRE - """.stripMargin) - } - } - } - } - - def findString(needle: String, haystack: String, skipFindsThatStartInFinds: Boolean = false): Seq[Int] = - find(ByteString(needle), ByteString(haystack), skipFindsThatStartInFinds) - - def find(needle: ByteString, haystack: ByteString, skipFindsThatStartInFinds: Boolean = false): Seq[Int] = { - val boyerMoore = new BoyerMoore(needle.toArray[Byte]) - @tailrec def rec(offset: Int, result: Seq[Int]): Seq[Int] = { - val ix = - try boyerMoore.nextIndex(haystack, offset) - catch { case NotEnoughDataException ⇒ -1 } - if (ix >= 0) rec(if (skipFindsThatStartInFinds) ix + needle.length else ix + 1, result :+ ix) else result - } - rec(0, Seq.empty) - } - - def findWithRegex(needle: ByteString, haystack: ByteString): Seq[Int] = - Pattern.quote(needle.map(_.toChar).mkString).r.findAllMatchIn(haystack.map(_.toChar).mkString).map(_.start).toSeq -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/parsing/ContentLengthHeaderParserSpec.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/parsing/ContentLengthHeaderParserSpec.scala deleted file mode 100644 index 3d24b2fc19..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/parsing/ContentLengthHeaderParserSpec.scala +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.parsing - -import org.scalatest.{ WordSpec, Matchers } -import akka.util.ByteString -import akka.http.scaladsl.model.headers.`Content-Length` -import akka.http.impl.engine.parsing.SpecializedHeaderValueParsers.ContentLengthParser - -class ContentLengthHeaderParserSpec extends WordSpec with Matchers { - - "specialized ContentLength parser" should { - "accept zero" in { - parse("0") shouldEqual 0L - } - "accept positive value" in { - parse("43234398") shouldEqual 43234398L - } - "accept positive value > Int.MaxValue <= Long.MaxValue" in { - parse("274877906944") shouldEqual 274877906944L - parse("9223372036854775807") shouldEqual 9223372036854775807L // Long.MaxValue - } - "don't accept positive value > Long.MaxValue" in { - a[ParsingException] should be thrownBy parse("9223372036854775808") // Long.MaxValue + 1 - a[ParsingException] should be thrownBy parse("92233720368547758070") // Long.MaxValue * 10 which is 0 taken overflow into account - a[ParsingException] should be thrownBy parse("92233720368547758080") // (Long.MaxValue + 1) * 10 which is 0 taken overflow into account - } - } - - def parse(bigint: String): Long = { - val (`Content-Length`(length), _) = ContentLengthParser(null, ByteString(bigint + "\r\n").compact, 0, _ ⇒ ()) - length - } - -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/parsing/HttpHeaderParserSpec.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/parsing/HttpHeaderParserSpec.scala deleted file mode 100644 index c632c74f3c..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/parsing/HttpHeaderParserSpec.scala +++ /dev/null @@ -1,286 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.parsing - -import java.lang.{ StringBuilder ⇒ JStringBuilder } -import akka.http.scaladsl.settings.ParserSettings -import com.typesafe.config.{ ConfigFactory, Config } -import scala.annotation.tailrec -import scala.util.Random -import org.scalatest.{ BeforeAndAfterAll, WordSpec, Matchers } -import akka.util.ByteString -import akka.actor.ActorSystem -import akka.http.scaladsl.model.HttpHeader -import akka.http.scaladsl.model.headers._ -import akka.http.impl.model.parser.CharacterClasses -import akka.http.impl.util._ - -class HttpHeaderParserSpec extends WordSpec with Matchers with BeforeAndAfterAll { - - val testConf: Config = ConfigFactory.parseString(""" - akka.event-handlers = ["akka.testkit.TestEventListener"] - akka.loglevel = ERROR - akka.http.parsing.max-header-name-length = 60 - akka.http.parsing.max-header-value-length = 1000 - akka.http.parsing.header-cache.Host = 300""") - val system = ActorSystem(getClass.getSimpleName, testConf) - - "The HttpHeaderParser" should { - "insert the 1st value" in new TestSetup(primed = false) { - insert("Hello", 'Hello) - check { - """nodes: 0/H, 0/e, 0/l, 0/l, 0/o, 1/Ω - |branchData:\u0020 - |values: 'Hello""" → parser.formatRawTrie - } - check { - """-H-e-l-l-o- 'Hello - |""" → parser.formatTrie - } - } - - "insert a new branch underneath a simple node" in new TestSetup(primed = false) { - insert("Hello", 'Hello) - insert("Hallo", 'Hallo) - check { - """nodes: 0/H, 1/e, 0/l, 0/l, 0/o, 1/Ω, 0/a, 0/l, 0/l, 0/o, 2/Ω - |branchData: 6/2/0 - |values: 'Hello, 'Hallo""" → parser.formatRawTrie - } - check { - """ ┌─a-l-l-o- 'Hallo - |-H-e-l-l-o- 'Hello - |""" → parser.formatTrie - } - } - - "insert a new branch underneath the root" in new TestSetup(primed = false) { - insert("Hello", 'Hello) - insert("Hallo", 'Hallo) - insert("Yeah", 'Yeah) - check { - """nodes: 2/H, 1/e, 0/l, 0/l, 0/o, 1/Ω, 0/a, 0/l, 0/l, 0/o, 2/Ω, 0/Y, 0/e, 0/a, 0/h, 3/Ω - |branchData: 6/2/0, 0/1/11 - |values: 'Hello, 'Hallo, 'Yeah""" → parser.formatRawTrie - } - check { - """ ┌─a-l-l-o- 'Hallo - |-H-e-l-l-o- 'Hello - | └─Y-e-a-h- 'Yeah - |""" → parser.formatTrie - } - } - - "insert a new branch underneath an existing branch node" in new TestSetup(primed = false) { - insert("Hello", 'Hello) - insert("Hallo", 'Hallo) - insert("Yeah", 'Yeah) - insert("Hoo", 'Hoo) - check { - """nodes: 2/H, 1/e, 0/l, 0/l, 0/o, 1/Ω, 0/a, 0/l, 0/l, 0/o, 2/Ω, 0/Y, 0/e, 0/a, 0/h, 3/Ω, 0/o, 0/o, 4/Ω - |branchData: 6/2/16, 0/1/11 - |values: 'Hello, 'Hallo, 'Yeah, 'Hoo""" → parser.formatRawTrie - } - check { - """ ┌─a-l-l-o- 'Hallo - |-H-e-l-l-o- 'Hello - | | └─o-o- 'Hoo - | └─Y-e-a-h- 'Yeah - |""" → parser.formatTrie - } - } - - "support overriding of previously inserted values" in new TestSetup(primed = false) { - insert("Hello", 'Hello) - insert("Hallo", 'Hallo) - insert("Yeah", 'Yeah) - insert("Hoo", 'Hoo) - insert("Hoo", 'Foo) - check { - """ ┌─a-l-l-o- 'Hallo - |-H-e-l-l-o- 'Hello - | | └─o-o- 'Foo - | └─Y-e-a-h- 'Yeah - |""" → parser.formatTrie - } - } - - "retrieve the EmptyHeader" in new TestSetup() { - parseAndCache("\r\n")() shouldEqual EmptyHeader - } - - "retrieve a cached header with an exact header name match" in new TestSetup() { - parseAndCache("Connection: close\r\nx")() shouldEqual Connection("close") - } - - "retrieve a cached header with a case-insensitive header-name match" in new TestSetup() { - parseAndCache("Connection: close\r\nx")("coNNection: close\r\nx") shouldEqual Connection("close") - } - - "parse and cache a modelled header" in new TestSetup() { - parseAndCache("Host: spray.io:123\r\nx")("HOST: spray.io:123\r\nx") shouldEqual Host("spray.io", 123) - } - - "parse and cache an invalid modelled header as RawHeader" in new TestSetup() { - parseAndCache("Content-Type: abc:123\r\nx")() shouldEqual RawHeader("content-type", "abc:123") - parseAndCache("Origin: localhost:8080\r\nx")() shouldEqual RawHeader("origin", "localhost:8080") - } - - "parse and cache an X-Forwarded-For with a hostname in it as a RawHeader" in new TestSetup() { - parseAndCache("X-Forwarded-For: 1.2.3.4, akka.io\r\nx")() shouldEqual RawHeader("x-forwarded-for", "1.2.3.4, akka.io") - } - - "parse and cache an X-Real-Ip with a hostname as it's value as a RawHeader" in new TestSetup() { - parseAndCache("X-Real-Ip: akka.io\r\nx")() shouldEqual RawHeader("x-real-ip", "akka.io") - } - "parse and cache a raw header" in new TestSetup(primed = false) { - insert("hello: bob", 'Hello) - val (ixA, headerA) = parseLine("Fancy-Pants: foo\r\nx") - val (ixB, headerB) = parseLine("Fancy-pants: foo\r\nx") - check { - """ ┌─f-a-n-c-y---p-a-n-t-s-:-(Fancy-Pants)- -f-o-o-\r-\n- *Fancy-Pants: foo - |-h-e-l-l-o-:- -b-o-b- 'Hello - |""" → parser.formatTrie - } - ixA shouldEqual ixB - headerA shouldEqual RawHeader("Fancy-Pants", "foo") - headerA should be theSameInstanceAs headerB - } - - "parse and cache a modelled header with line-folding" in new TestSetup() { - parseAndCache("Connection: foo,\r\n bar\r\nx")("Connection: foo,\r\n bar\r\nx") shouldEqual Connection("foo", "bar") - } - - "parse and cache a header with a tab char in the value" in new TestSetup() { - parseAndCache("Fancy: foo\tbar\r\nx")() shouldEqual RawHeader("Fancy", "foo bar") - } - - "parse and cache a header with UTF8 chars in the value" in new TestSetup() { - parseAndCache("2-UTF8-Bytes: árvíztűrő ütvefúrógép\r\nx")() shouldEqual RawHeader("2-UTF8-Bytes", "árvíztűrő ütvefúrógép") - parseAndCache("3-UTF8-Bytes: The € or the $?\r\nx")() shouldEqual RawHeader("3-UTF8-Bytes", "The € or the $?") - parseAndCache("4-UTF8-Bytes: Surrogate pairs: \uD801\uDC1B\uD801\uDC04\uD801\uDC1B!\r\nx")() shouldEqual - RawHeader("4-UTF8-Bytes", "Surrogate pairs: \uD801\uDC1B\uD801\uDC04\uD801\uDC1B!") - } - - "produce an error message for lines with an illegal header name" in new TestSetup() { - the[ParsingException] thrownBy parseLine(" Connection: close\r\nx") should have message "Illegal character ' ' in header name" - the[ParsingException] thrownBy parseLine("Connection : close\r\nx") should have message "Illegal character ' ' in header name" - the[ParsingException] thrownBy parseLine("Connec/tion: close\r\nx") should have message "Illegal character '/' in header name" - } - - "produce an error message for lines with a too-long header name" in new TestSetup() { - noException should be thrownBy parseLine("123456789012345678901234567890123456789012345678901234567890: foo\r\nx") - the[ParsingException] thrownBy parseLine("1234567890123456789012345678901234567890123456789012345678901: foo\r\nx") should have message - "HTTP header name exceeds the configured limit of 60 characters" - } - - "produce an error message for lines with a too-long header value" in new TestSetup() { - noException should be thrownBy parseLine(s"foo: ${nextRandomString(nextRandomAlphaNumChar, 1000)}\r\nx") - the[ParsingException] thrownBy parseLine(s"foo: ${nextRandomString(nextRandomAlphaNumChar, 1001)}\r\nx") should have message - "HTTP header value exceeds the configured limit of 1000 characters" - } - - "continue parsing raw headers even if the overall cache value capacity is reached" in new TestSetup() { - val randomHeaders = Stream.continually { - val name = nextRandomString(nextRandomAlphaNumChar, nextRandomInt(4, 16)) - val value = nextRandomString(nextRandomPrintableChar, nextRandomInt(4, 16)) - RawHeader(name, value) - } - randomHeaders.take(300).foldLeft(0) { - case (acc, rawHeader) ⇒ acc + parseAndCache(rawHeader.toString + "\r\nx", rawHeader) - } should be < 300 // number of cache hits is smaller headers successfully parsed - } - - "continue parsing modelled headers even if the overall cache value capacity is reached" in new TestSetup() { - val randomHostHeaders = Stream.continually { - Host( - host = nextRandomString(nextRandomAlphaNumChar, nextRandomInt(4, 8)), - port = nextRandomInt(1000, 10000)) - } - randomHostHeaders.take(300).foldLeft(0) { - case (acc, header) ⇒ acc + parseAndCache(header.toString + "\r\nx", header) - } should be < 300 // number of cache hits is smaller headers successfully parsed - } - - "continue parsing headers even if the overall cache node capacity is reached" in new TestSetup() { - val randomHostHeaders = Stream.continually { - RawHeader( - name = nextRandomString(nextRandomAlphaNumChar, 60), - value = nextRandomString(nextRandomAlphaNumChar, 1000)) - } - randomHostHeaders.take(100).foldLeft(0) { - case (acc, header) ⇒ acc + parseAndCache(header.toString + "\r\nx", header) - } should be < 300 // number of cache hits is smaller headers successfully parsed - } - - "continue parsing raw headers even if the header-specific cache capacity is reached" in new TestSetup() { - val randomHeaders = Stream.continually { - val value = nextRandomString(nextRandomPrintableChar, nextRandomInt(4, 16)) - RawHeader("Fancy", value) - } - randomHeaders.take(20).foldLeft(0) { - case (acc, rawHeader) ⇒ acc + parseAndCache(rawHeader.toString + "\r\nx", rawHeader) - } shouldEqual 12 // configured default per-header cache limit - } - - "continue parsing modelled headers even if the header-specific cache capacity is reached" in new TestSetup() { - val randomHeaders = Stream.continually { - `User-Agent`(nextRandomString(nextRandomAlphaNumChar, nextRandomInt(4, 16))) - } - randomHeaders.take(40).foldLeft(0) { - case (acc, header) ⇒ acc + parseAndCache(header.toString + "\r\nx", header) - } shouldEqual 12 // configured default per-header cache limit - } - } - - override def afterAll() = system.terminate() - - def check(pair: (String, String)) = { - val (expected, actual) = pair - actual shouldEqual expected.stripMarginWithNewline("\n") - } - - abstract class TestSetup(primed: Boolean = true) { - val parser = { - val p = HttpHeaderParser.unprimed( - settings = ParserSettings(system), - system.log, - warnOnIllegalHeader = info ⇒ system.log.warning(info.formatPretty)) - if (primed) HttpHeaderParser.prime(p) else p - } - def insert(line: String, value: AnyRef): Unit = - if (parser.isEmpty) HttpHeaderParser.insertRemainingCharsAsNewNodes(parser, ByteString(line), value) - else HttpHeaderParser.insert(parser, ByteString(line), value) - - def parseLine(line: String) = parser.parseHeaderLine(ByteString(line))() → parser.resultHeader - - def parseAndCache(lineA: String)(lineB: String = lineA): HttpHeader = { - val (ixA, headerA) = parseLine(lineA) - val (ixB, headerB) = parseLine(lineB) - ixA shouldEqual ixB - headerA should be theSameInstanceAs headerB - headerA - } - - def parseAndCache(line: String, header: HttpHeader): Int = { - val (ixA, headerA) = parseLine(line) - val (ixB, headerB) = parseLine(line) - headerA shouldEqual header - headerB shouldEqual header - ixA shouldEqual ixB - if (headerA eq headerB) 1 else 0 - } - - private[this] val random = new Random(42) - def nextRandomPrintableChar(): Char = random.nextPrintableChar() - def nextRandomInt(min: Int, max: Int) = random.nextInt(max - min) + min - @tailrec final def nextRandomAlphaNumChar(): Char = { - val c = nextRandomPrintableChar() - if (CharacterClasses.ALPHANUM(c)) c else nextRandomAlphaNumChar() - } - @tailrec final def nextRandomString(charGen: () ⇒ Char, len: Int, sb: JStringBuilder = new JStringBuilder): String = - if (sb.length < len) nextRandomString(charGen, len, sb.append(charGen())) else sb.toString - } -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/parsing/HttpHeaderParserTestBed.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/parsing/HttpHeaderParserTestBed.scala deleted file mode 100644 index 89c554d2fb..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/parsing/HttpHeaderParserTestBed.scala +++ /dev/null @@ -1,34 +0,0 @@ -package akka.http.impl.engine.parsing - -import akka.actor.ActorSystem -import akka.http.scaladsl.settings.ParserSettings -import com.typesafe.config.{ ConfigFactory, Config } - -object HttpHeaderParserTestBed extends App { - - val testConf: Config = ConfigFactory.parseString(""" - akka.event-handlers = ["akka.testkit.TestEventListener"] - akka.loglevel = ERROR - akka.http.parsing.max-header-name-length = 20 - akka.http.parsing.max-header-value-length = 21 - akka.http.parsing.header-cache.Host = 300""") - val system = ActorSystem("HttpHeaderParserTestBed", testConf) - - val parser = HttpHeaderParser.prime { - HttpHeaderParser.unprimed(ParserSettings(system), system.log, warnOnIllegalHeader = info ⇒ system.log.warning(info.formatPretty)) - } - - println { - s""" - |HttpHeaderParser primed Trie - |---------------------------- - | - |%TRIE% - | - |formatSizes: ${parser.formatSizes} - |contentHistogram: ${parser.contentHistogram.mkString("\n ", "\n ", "\n")} - """.stripMargin.replace("%TRIE%", parser.formatTrie) - } - - system.terminate() -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/parsing/RequestParserSpec.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/parsing/RequestParserSpec.scala deleted file mode 100644 index 5dfe4a6ace..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/parsing/RequestParserSpec.scala +++ /dev/null @@ -1,609 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.parsing - -import akka.NotUsed - -import scala.concurrent.Future -import scala.concurrent.duration._ - -import com.typesafe.config.{ Config, ConfigFactory } - -import akka.util.ByteString - -import akka.actor.ActorSystem - -import akka.stream.ActorMaterializer -import akka.stream.scaladsl._ - -import akka.stream.TLSProtocol._ - -import org.scalatest.matchers.Matcher -import org.scalatest.{ BeforeAndAfterAll, FreeSpec, Matchers } - -import akka.http.scaladsl.settings.ParserSettings -import akka.http.impl.engine.parsing.ParserOutput._ -import akka.http.impl.util._ -import akka.http.scaladsl.model.HttpEntity._ -import akka.http.scaladsl.model.HttpMethods._ -import akka.http.scaladsl.model.HttpProtocols._ -import akka.http.scaladsl.model.MediaTypes._ -import akka.http.scaladsl.model.RequestEntityAcceptance.Expected -import akka.http.scaladsl.model.StatusCodes._ -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers._ -import akka.http.scaladsl.util.FastFuture -import akka.http.scaladsl.util.FastFuture._ - -class RequestParserSpec extends FreeSpec with Matchers with BeforeAndAfterAll { - val testConf: Config = ConfigFactory.parseString(""" - akka.event-handlers = ["akka.testkit.TestEventListener"] - akka.loglevel = WARNING - akka.http.parsing.max-header-value-length = 32 - akka.http.parsing.max-uri-length = 40 - akka.http.parsing.max-content-length = 4000000000""") - implicit val system = ActorSystem(getClass.getSimpleName, testConf) - import system.dispatcher - - val BOLT = HttpMethod.custom("BOLT", safe = false, idempotent = true, requestEntityAcceptance = Expected) - implicit val materializer = ActorMaterializer() - - "The request parsing logic should" - { - "properly parse a request" - { - "with no headers and no body" in new Test { - """GET / HTTP/1.0 - | - |""" should parseTo(HttpRequest(protocol = `HTTP/1.0`)) - closeAfterResponseCompletion shouldEqual Seq(true) - } - - "with no headers and no body but remaining content" in new Test { - Seq("""GET / HTTP/1.0 - | - |POST /foo HTTP/1.0 - | - |TRA""") /* beginning of TRACE request */ should generalMultiParseTo( - Right(HttpRequest(GET, "/", protocol = `HTTP/1.0`)), - Right(HttpRequest(POST, "/foo", protocol = `HTTP/1.0`)), - Left(MessageStartError(StatusCodes.BadRequest, ErrorInfo("Illegal HTTP message start")))) - closeAfterResponseCompletion shouldEqual Seq(true, true) - } - - "with one header" in new Test { - """GET / HTTP/1.1 - |Host: example.com - | - |""" should parseTo(HttpRequest(headers = List(Host("example.com")))) - closeAfterResponseCompletion shouldEqual Seq(false) - } - - "with absolute uri in request-target" in new Test { - """GET http://127.0.0.1:8080/hello HTTP/1.1 - |Host: 127.0.0.1:8080 - | - |""" should parseTo(HttpRequest(uri = "http://127.0.0.1:8080/hello", headers = List(Host("127.0.0.1", 8080)))) - closeAfterResponseCompletion shouldEqual Seq(false) - } - - "with 3 headers and a body" in new Test { - """POST /resource/yes HTTP/1.0 - |User-Agent: curl/7.19.7 xyz - |Connection:keep-alive - |Content-Type: text/plain; charset=UTF-8 - |Content-length: 17 - | - |Shake your BOODY!""" should parseTo { - HttpRequest(POST, "/resource/yes", List(`User-Agent`("curl/7.19.7 xyz"), Connection("keep-alive")), - "Shake your BOODY!", `HTTP/1.0`) - } - closeAfterResponseCompletion shouldEqual Seq(false) - } - - "with 3 headers, a body and remaining content" in new Test { - """POST /resource/yes HTTP/1.0 - |User-Agent: curl/7.19.7 xyz - |Connection:keep-alive - |Content-length: 17 - | - |Shake your BOODY!GET / HTTP/1.0 - | - |""" should parseTo( - HttpRequest(POST, "/resource/yes", List(`User-Agent`("curl/7.19.7 xyz"), Connection("keep-alive")), - "Shake your BOODY!".getBytes, `HTTP/1.0`), - HttpRequest(protocol = `HTTP/1.0`)) - closeAfterResponseCompletion shouldEqual Seq(false, true) - } - - "with multi-line headers" in new Test { - """DELETE /abc HTTP/1.0 - |User-Agent: curl/7.19.7 - | abc - | xyz - |Accept: */* - |Connection: close, - | fancy - | - |""" should parseTo { - HttpRequest(DELETE, "/abc", List(`User-Agent`("curl/7.19.7 abc xyz"), Accept(MediaRanges.`*/*`), - Connection("close", "fancy")), protocol = `HTTP/1.0`) - } - closeAfterResponseCompletion shouldEqual Seq(true) - } - - "byte-by-byte" in new Test { - prep { - """PUT /resource/yes HTTP/1.1 - |Content-length: 4 - |Host: x - | - |ABCDPATCH""" - }.toCharArray.map(_.toString).toSeq should generalRawMultiParseTo( - Right(HttpRequest(PUT, "/resource/yes", List(Host("x")), "ABCD".getBytes)), - Left(MessageStartError(400, ErrorInfo("Illegal HTTP message start")))) - closeAfterResponseCompletion shouldEqual Seq(false) - } - - "with a custom HTTP method" in new Test { - override protected def parserSettings: ParserSettings = - super.parserSettings.withCustomMethods(BOLT) - - """BOLT / HTTP/1.0 - | - |""" should parseTo(HttpRequest(BOLT, "/", protocol = `HTTP/1.0`)) - closeAfterResponseCompletion shouldEqual Seq(true) - } - - "with a funky `Transfer-Encoding` header" in new Test { - """PUT / HTTP/1.1 - |Transfer-Encoding: foo, chunked, bar - |Host: x - | - |""" should parseTo(HttpRequest(PUT, "/", List(`Transfer-Encoding`( - TransferEncodings.Extension("foo"), - TransferEncodings.chunked, TransferEncodings.Extension("bar")), Host("x")))) - closeAfterResponseCompletion shouldEqual Seq(false) - } - - "with several identical `Content-Type` headers" in new Test { - """GET /data HTTP/1.1 - |Host: x - |Content-Type: application/pdf - |Content-Type: application/pdf - |Content-Length: 0 - | - |""" should parseTo(HttpRequest(GET, "/data", List(Host("x")), HttpEntity.empty(`application/pdf`))) - closeAfterResponseCompletion shouldEqual Seq(false) - } - - "with a request target starting with a double-slash" in new Test { - """GET //foo HTTP/1.0 - | - |""" should parseTo(HttpRequest(GET, Uri("http://x//foo").toHttpRequestTargetOriginForm, protocol = `HTTP/1.0`)) - closeAfterResponseCompletion shouldEqual Seq(true) - } - - "with additional fields in Strict-Transport-Security header" in new Test { - """GET /hsts HTTP/1.1 - |Host: x - |Strict-Transport-Security: max-age=1; preload; dummy - | - |""" should parseTo(HttpRequest( - GET, - "/hsts", - headers = List(Host("x"), `Strict-Transport-Security`(1, None)), - protocol = `HTTP/1.1`)) - - """GET /hsts HTTP/1.1 - |Host: x - |Strict-Transport-Security: max-age=1; dummy; preload - | - |""" should parseTo(HttpRequest( - GET, - "/hsts", - headers = List(Host("x"), `Strict-Transport-Security`(1, None)), - protocol = `HTTP/1.1`)) - } - } - - "properly parse a chunked request" - { - val start = - """PATCH /data HTTP/1.1 - |Transfer-Encoding: chunked - |Connection: lalelu - |Content-Type: application/pdf - |Host: ping - | - |""" - val baseRequest = HttpRequest(PATCH, "/data", List(Connection("lalelu"), Host("ping"))) - - "request start" in new Test { - Seq(start, "rest") should generalMultiParseTo( - Right(baseRequest.withEntity(HttpEntity.Chunked(`application/pdf`, source()))), - Left(EntityStreamError(ErrorInfo("Illegal character 'r' in chunk start")))) - closeAfterResponseCompletion shouldEqual Seq(false) - } - - "message chunk with and without extension" in new Test { - Seq( - start + - """3 - |abc - |10;some=stuff;bla - |0123456789ABCDEF - |""", - "10;foo=", - """bar - |0123456789ABCDEF - |A - |0123456789""", - """ - |0 - | - |""") should generalMultiParseTo( - Right(baseRequest.withEntity(Chunked(`application/pdf`, source( - Chunk(ByteString("abc")), - Chunk(ByteString("0123456789ABCDEF"), "some=stuff;bla"), - Chunk(ByteString("0123456789ABCDEF"), "foo=bar"), - Chunk(ByteString("0123456789"), ""), - LastChunk))))) - closeAfterResponseCompletion shouldEqual Seq(false) - } - - "message end" in new Test { - Seq( - start, - """0 - | - |""") should generalMultiParseTo( - Right(baseRequest.withEntity(Chunked(`application/pdf`, source(LastChunk))))) - closeAfterResponseCompletion shouldEqual Seq(false) - } - - "message end with extension and trailer" in new Test { - Seq( - start, - """000;nice=true - |Foo: pip - | apo - |Bar: xyz - | - |""") should generalMultiParseTo( - Right(baseRequest.withEntity(Chunked( - `application/pdf`, - source(LastChunk("nice=true", List(RawHeader("Foo", "pip apo"), RawHeader("Bar", "xyz")))))))) - closeAfterResponseCompletion shouldEqual Seq(false) - } - - "don't overflow the stack for large buffers of chunks" in new Test { - override val awaitAtMost = 10000.millis - - val x = NotEnoughDataException - val numChunks = 12000 // failed starting from 4000 with sbt started with `-Xss2m` - val oneChunk = "1\r\nz\n" - val manyChunks = (oneChunk * numChunks) + "0\r\n" - - val parser = newParser - val result = multiParse(newParser)(Seq(prep(start + manyChunks))) - val HttpEntity.Chunked(_, chunks) = result.head.right.get.req.entity - val strictChunks = chunks.limit(100000).runWith(Sink.seq).awaitResult(awaitAtMost) - strictChunks.size shouldEqual numChunks - } - } - - "properly parse a chunked request with additional transfer encodings" in new Test { - """PATCH /data HTTP/1.1 - |Transfer-Encoding: fancy, chunked - |Content-Type: application/pdf - |Host: ping - | - |0 - | - |""" should parseTo(HttpRequest(PATCH, "/data", List( - `Transfer-Encoding`(TransferEncodings.Extension("fancy")), - Host("ping")), HttpEntity.Chunked(`application/pdf`, source(LastChunk)))) - closeAfterResponseCompletion shouldEqual Seq(false) - } - - "support `rawRequestUriHeader` setting" in new Test { - override protected def newParser: HttpRequestParser = - new HttpRequestParser(parserSettings, rawRequestUriHeader = true, headerParser = HttpHeaderParser(parserSettings, system.log)()) - - """GET /f%6f%6fbar?q=b%61z HTTP/1.1 - |Host: ping - |Content-Type: application/pdf - | - |""" should parseTo( - HttpRequest( - GET, - "/foobar?q=b%61z", - List( - `Raw-Request-URI`("/f%6f%6fbar?q=b%61z"), - Host("ping")), - HttpEntity.empty(`application/pdf`))) - } - - "reject a message chunk with" - { - val start = - """PATCH /data HTTP/1.1 - |Transfer-Encoding: chunked - |Connection: lalelu - |Host: ping - | - |""" - val baseRequest = HttpRequest(PATCH, "/data", List(Connection("lalelu"), Host("ping")), - HttpEntity.Chunked(`application/octet-stream`, source())) - - "an illegal char after chunk size" in new Test { - Seq( - start, - """15 ; - |""") should generalMultiParseTo( - Right(baseRequest), - Left(EntityStreamError(ErrorInfo("Illegal character ' ' in chunk start")))) - closeAfterResponseCompletion shouldEqual Seq(false) - } - - "an illegal char in chunk size" in new Test { - Seq(start, "bla") should generalMultiParseTo( - Right(baseRequest), - Left(EntityStreamError(ErrorInfo("Illegal character 'l' in chunk start")))) - closeAfterResponseCompletion shouldEqual Seq(false) - } - - "too-long chunk extension" in new Test { - Seq(start, "3;" + ("x" * 257)) should generalMultiParseTo( - Right(baseRequest), - Left(EntityStreamError(ErrorInfo("HTTP chunk extension length exceeds configured limit of 256 characters")))) - closeAfterResponseCompletion shouldEqual Seq(false) - } - - "too-large chunk size" in new Test { - Seq( - start, - """1a2b3c4d5e - |""") should generalMultiParseTo( - Right(baseRequest), - Left(EntityStreamError(ErrorInfo("HTTP chunk size exceeds the configured limit of 1048576 bytes")))) - closeAfterResponseCompletion shouldEqual Seq(false) - } - - "an illegal chunk termination" in new Test { - Seq( - start, - """3 - |abcde""") should generalMultiParseTo( - Right(baseRequest), - Left(EntityStreamError(ErrorInfo("Illegal chunk termination")))) - closeAfterResponseCompletion shouldEqual Seq(false) - } - - "an illegal header in the trailer" in new Test { - Seq( - start, - """0 - |F@oo: pip""") should generalMultiParseTo( - Right(baseRequest), - Left(EntityStreamError(ErrorInfo("Illegal character '@' in header name")))) - closeAfterResponseCompletion shouldEqual Seq(false) - } - } - - "reject a request with" - { - "an illegal HTTP method" in new Test { - "get " should parseToError(NotImplemented, ErrorInfo("Unsupported HTTP method", "get")) - "GETX " should parseToError(NotImplemented, ErrorInfo("Unsupported HTTP method", "GETX")) - } - - "a too long HTTP method" in new Test { - "ABCDEFGHIJKLMNOPQ " should - parseToError( - BadRequest, - ErrorInfo( - "Unsupported HTTP method", - "HTTP method too long (started with 'ABCDEFGHIJKLMNOP'). Increase `akka.http.server.parsing.max-method-length` to support HTTP methods with more characters.")) - } - - "two Content-Length headers" in new Test { - """GET / HTTP/1.1 - |Content-Length: 3 - |Content-Length: 4 - | - |foo""" should parseToError( - BadRequest, - ErrorInfo("HTTP message must not contain more than one Content-Length header")) - } - - "a too-long URI" in new Test { - "GET /2345678901234567890123456789012345678901 HTTP/1.1" should parseToError( - RequestUriTooLong, - ErrorInfo("URI length exceeds the configured limit of 40 characters")) - } - - "HTTP version 1.2" in new Test { - """GET / HTTP/1.2 - |""" should parseToError( - HTTPVersionNotSupported, - ErrorInfo("The server does not support the HTTP protocol version used in the request.")) - } - - "with an illegal char in a header name" in new Test { - """GET / HTTP/1.1 - |User@Agent: curl/7.19.7""" should parseToError(BadRequest, ErrorInfo("Illegal character '@' in header name")) - } - - "with a too-long header name" in new Test { - """|GET / HTTP/1.1 - |UserxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxAgent: curl/7.19.7""" should parseToError( - BadRequest, ErrorInfo("HTTP header name exceeds the configured limit of 64 characters")) - } - - "with a too-long header-value" in new Test { - """|GET / HTTP/1.1 - |Fancy: 123456789012345678901234567890123""" should parseToError( - BadRequest, - ErrorInfo("HTTP header value exceeds the configured limit of 32 characters")) - } - - "with an invalid Content-Length header value" in new Test { - """GET / HTTP/1.0 - |Content-Length: 1.5 - | - |abc""" should parseToError(BadRequest, ErrorInfo("Illegal `Content-Length` header value")) - } - - "with Content-Length > Long.MaxSize" in new Test { - // content-length = (Long.MaxValue + 1) * 10, which is 0 when calculated overflow - """PUT /resource/yes HTTP/1.1 - |Content-length: 92233720368547758080 - |Host: x - | - |""" should parseToError(400: StatusCode, ErrorInfo("`Content-Length` header value must not exceed 63-bit integer range")) - } - - "with an illegal entity using CONNECT" in new Test { - """CONNECT /resource/yes HTTP/1.1 - |Transfer-Encoding: chunked - |Host: x - | - |""" should parseToError(422: StatusCode, ErrorInfo("CONNECT requests must not have an entity")) - } - "with an illegal entity using HEAD" in new Test { - """HEAD /resource/yes HTTP/1.1 - |Content-length: 3 - |Host: x - | - |foo""" should parseToError(422: StatusCode, ErrorInfo("HEAD requests must not have an entity")) - } - "with an illegal entity using TRACE" in new Test { - """TRACE /resource/yes HTTP/1.1 - |Transfer-Encoding: chunked - |Host: x - | - |""" should parseToError(422: StatusCode, ErrorInfo("TRACE requests must not have an entity")) - } - - "with additional fields in headers" in new Test { - """GET / HTTP/1.1 - |Host: x; dummy - | - |""" should parseToError( - BadRequest, - ErrorInfo("Illegal 'host' header: Invalid input ' ', expected 'EOI', ':', UPPER_ALPHA, lower-reg-name-char or pct-encoded (line 1, column 3)", "x; dummy\n ^")) - - """GET / HTTP/1.1 - |Content-length: 3; dummy - | - |""" should parseToError( - BadRequest, - ErrorInfo("Illegal `Content-Length` header value")) - - """GET / HTTP/1.1 - |Connection:keep-alive; dummy - | - |""" should parseToError( - BadRequest, - ErrorInfo("Illegal 'connection' header: Invalid input ';', expected tchar, OWS, listSep or 'EOI' (line 1, column 11)", "keep-alive; dummy\n ^")) - - """GET / HTTP/1.1 - |Transfer-Encoding: chunked; dummy - | - |""" should parseToError( - BadRequest, - ErrorInfo("Illegal 'transfer-encoding' header: Invalid input ';', expected OWS, listSep or 'EOI' (line 1, column 8)", "chunked; dummy\n ^")) - } - } - } - - override def afterAll() = system.terminate() - - private class Test { - def awaitAtMost: FiniteDuration = 3.seconds - var closeAfterResponseCompletion = Seq.empty[Boolean] - - class StrictEqualHttpRequest(val req: HttpRequest) { - override def equals(other: scala.Any): Boolean = other match { - case other: StrictEqualHttpRequest ⇒ - this.req.copy(entity = HttpEntity.Empty) == other.req.copy(entity = HttpEntity.Empty) && - this.req.entity.toStrict(awaitAtMost).awaitResult(awaitAtMost) == - other.req.entity.toStrict(awaitAtMost).awaitResult(awaitAtMost) - } - - override def toString = req.toString - } - - def strictEqualify[T](x: Either[T, HttpRequest]): Either[T, StrictEqualHttpRequest] = - x.right.map(new StrictEqualHttpRequest(_)) - - def parseTo(expected: HttpRequest*): Matcher[String] = - multiParseTo(expected: _*).compose(_ :: Nil) - - def multiParseTo(expected: HttpRequest*): Matcher[Seq[String]] = multiParseTo(newParser, expected: _*) - def multiParseTo(parser: HttpRequestParser, expected: HttpRequest*): Matcher[Seq[String]] = - rawMultiParseTo(parser, expected: _*).compose(_ map prep) - - def rawMultiParseTo(expected: HttpRequest*): Matcher[Seq[String]] = - rawMultiParseTo(newParser, expected: _*) - def rawMultiParseTo(parser: HttpRequestParser, expected: HttpRequest*): Matcher[Seq[String]] = - generalRawMultiParseTo(parser, expected.map(Right(_)): _*) - - def parseToError(status: StatusCode, info: ErrorInfo): Matcher[String] = - generalMultiParseTo(Left(MessageStartError(status, info))).compose(_ :: Nil) - - def generalMultiParseTo(expected: Either[RequestOutput, HttpRequest]*): Matcher[Seq[String]] = - generalRawMultiParseTo(expected: _*).compose(_ map prep) - - def generalRawMultiParseTo(expected: Either[RequestOutput, HttpRequest]*): Matcher[Seq[String]] = - generalRawMultiParseTo(newParser, expected: _*) - def generalRawMultiParseTo( - parser: HttpRequestParser, - expected: Either[RequestOutput, HttpRequest]*): Matcher[Seq[String]] = - equal(expected.map(strictEqualify)) - .matcher[Seq[Either[RequestOutput, StrictEqualHttpRequest]]] compose multiParse(parser) - - def multiParse(parser: HttpRequestParser)(input: Seq[String]): Seq[Either[RequestOutput, StrictEqualHttpRequest]] = - Source(input.toList) - .map(bytes ⇒ SessionBytes(TLSPlacebo.dummySession, ByteString(bytes))) - .via(parser).named("parser") - .splitWhen(x ⇒ x.isInstanceOf[MessageStart] || x.isInstanceOf[EntityStreamError]) - .prefixAndTail(1) - .collect { - case (Seq(RequestStart(method, uri, protocol, headers, createEntity, _, close)), entityParts) ⇒ - closeAfterResponseCompletion :+= close - Right(HttpRequest(method, uri, headers, createEntity(entityParts), protocol)) - case (Seq(x @ (MessageStartError(_, _) | EntityStreamError(_))), rest) ⇒ - rest.runWith(Sink.cancelled) - Left(x) - } - .concatSubstreams - .flatMapConcat { x ⇒ - Source.fromFuture { - x match { - case Right(request) ⇒ compactEntity(request.entity).fast.map(x ⇒ Right(request.withEntity(x))) - case Left(error) ⇒ FastFuture.successful(Left(error)) - } - } - } - .map(strictEqualify) - .limit(100000).runWith(Sink.seq) - .awaitResult(awaitAtMost) - - protected def parserSettings: ParserSettings = ParserSettings(system) - protected def newParser = new HttpRequestParser(parserSettings, false, HttpHeaderParser(parserSettings, system.log)()) - - private def compactEntity(entity: RequestEntity): Future[RequestEntity] = - entity match { - case x: Chunked ⇒ compactEntityChunks(x.chunks).fast.map(compacted ⇒ x.copy(chunks = source(compacted: _*))) - case _ ⇒ entity.toStrict(awaitAtMost) - } - - private def compactEntityChunks(data: Source[ChunkStreamPart, Any]): Future[Seq[ChunkStreamPart]] = - data.limit(100000).runWith(Sink.seq) - .fast.recover { case _: NoSuchElementException ⇒ Nil } - - def prep(response: String) = response.stripMarginWithNewline("\r\n") - } - - def source[T](elems: T*): Source[T, NotUsed] = Source(elems.toList) -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/parsing/ResponseParserSpec.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/parsing/ResponseParserSpec.scala deleted file mode 100644 index 470d03c3e4..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/parsing/ResponseParserSpec.scala +++ /dev/null @@ -1,343 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.parsing - -import akka.NotUsed -import akka.http.scaladsl.settings.ParserSettings -import akka.http.scaladsl.util.FastFuture -import akka.stream.TLSProtocol._ -import com.typesafe.config.{ ConfigFactory, Config } -import scala.concurrent.{ Future, Await } -import scala.concurrent.duration._ -import org.scalatest.{ BeforeAndAfterAll, FreeSpec, Matchers } -import org.scalatest.matchers.Matcher -import akka.util.ByteString -import akka.actor.ActorSystem -import akka.stream.scaladsl._ -import akka.stream.ActorMaterializer -import akka.http.scaladsl.util.FastFuture._ -import akka.http.impl.util._ -import akka.http.scaladsl.model._ -import headers._ -import MediaTypes._ -import HttpMethods._ -import HttpProtocols._ -import StatusCodes._ -import HttpEntity._ -import ParserOutput._ - -class ResponseParserSpec extends FreeSpec with Matchers with BeforeAndAfterAll { - val testConf: Config = ConfigFactory.parseString(""" - akka.event-handlers = ["akka.testkit.TestEventListener"] - akka.loglevel = WARNING - akka.http.parsing.max-response-reason-length = 21""") - implicit val system = ActorSystem(getClass.getSimpleName, testConf) - import system.dispatcher - - implicit val materializer = ActorMaterializer() - val ServerOnTheMove = StatusCodes.custom(331, "Server on the move") - - "The response parsing logic should" - { - "properly parse" - { - - // http://tools.ietf.org/html/rfc7230#section-3.3.3 - "a 200 response to a HEAD request" in new Test { - """HTTP/1.1 200 OK - | - |""" should parseTo(HEAD, HttpResponse()) - closeAfterResponseCompletion shouldEqual Seq(false) - } - - // http://tools.ietf.org/html/rfc7230#section-3.3.3 - "a 204 response" in new Test { - """HTTP/1.1 204 OK - | - |""" should parseTo(HttpResponse(NoContent)) - closeAfterResponseCompletion shouldEqual Seq(false) - } - - "a response with a simple body" in new Test { - collectBlocking(rawParse( - GET, - prep { - """HTTP/1.1 200 Ok - |Content-Length: 4 - | - |ABCD""" - })) shouldEqual Seq(Right(HttpResponse(entity = "ABCD".getBytes))) - closeAfterResponseCompletion shouldEqual Seq(false) - } - - "a response with a custom status code" in new Test { - override def parserSettings: ParserSettings = - super.parserSettings.withCustomStatusCodes(ServerOnTheMove) - - """HTTP/1.1 331 Server on the move - |Content-Length: 0 - | - |""" should parseTo(HttpResponse(ServerOnTheMove)) - closeAfterResponseCompletion shouldEqual Seq(false) - } - - "a response with a missing reason phrase" in new Test { - "HTTP/1.1 200 \r\nContent-Length: 0\r\n\r\n" should parseTo(HttpResponse(OK)) - closeAfterResponseCompletion shouldEqual Seq(false) - } - - "a response funky `Transfer-Encoding` header" in new Test { - override def parserSettings: ParserSettings = - super.parserSettings.withCustomStatusCodes(ServerOnTheMove) - - """HTTP/1.1 331 Server on the move - |Transfer-Encoding: foo, chunked, bar - |Content-Length: 0 - | - |""" should parseTo(HttpResponse(ServerOnTheMove, List(`Transfer-Encoding`( - TransferEncodings.Extension("foo"), - TransferEncodings.chunked, TransferEncodings.Extension("bar"))))) - closeAfterResponseCompletion shouldEqual Seq(false) - } - - "a response with one header, a body, but no Content-Length header" in new Test { - """HTTP/1.0 404 Not Found - |Host: api.example.com - | - |Foobs""" should parseTo(HttpResponse(NotFound, List(Host("api.example.com")), "Foobs".getBytes, `HTTP/1.0`)) - closeAfterResponseCompletion shouldEqual Seq(true) - } - - "a response with one header, no body, and no Content-Length header" in new Test { - """HTTP/1.0 404 Not Found - |Host: api.example.com - | - |""" should parseTo(HttpResponse(NotFound, List(Host("api.example.com")), - HttpEntity.empty(ContentTypes.`application/octet-stream`), `HTTP/1.0`)) - closeAfterResponseCompletion shouldEqual Seq(true) - } - - "a response with 3 headers, a body and remaining content" in new Test { - Seq("""HTTP/1.1 500 Internal Server Error - |User-Agent: curl/7.19.7 xyz - |Connection:close - |Content-Length: 17 - |Content-Type: text/plain; charset=UTF-8 - | - |Sh""", "ake your BOODY!HTTP/1.") should generalMultiParseTo( - Right(HttpResponse(InternalServerError, List(`User-Agent`("curl/7.19.7 xyz"), Connection("close")), - "Shake your BOODY!"))) - closeAfterResponseCompletion shouldEqual Seq(true) - } - - "a split response (parsed byte-by-byte)" in new Test { - prep { - """HTTP/1.1 200 Ok - |Content-Length: 4 - | - |ABCD""" - }.toCharArray.map(_.toString).toSeq should rawMultiParseTo(HttpResponse(entity = "ABCD".getBytes)) - closeAfterResponseCompletion shouldEqual Seq(false) - } - } - - "properly parse a chunked" - { - val start = - """HTTP/1.1 200 OK - |Transfer-Encoding: chunked - |Connection: lalelu - |Content-Type: application/pdf - |Server: spray-can - | - |""" - val baseResponse = HttpResponse(headers = List(Connection("lalelu"), Server("spray-can"))) - - "response start" in new Test { - Seq(start, "rest") should generalMultiParseTo( - Right(baseResponse.withEntity(Chunked(`application/pdf`, source()))), - Left(EntityStreamError(ErrorInfo("Illegal character 'r' in chunk start")))) - closeAfterResponseCompletion shouldEqual Seq(false) - } - - "message chunk with and without extension" in new Test { - Seq( - start + - """3 - |abc - |10;some=stuff;bla - |0123456789ABCDEF - |""", - "10;foo=", - """bar - |0123456789ABCDEF - |10 - |0123456789""", - """ABCDEF - |0 - | - |""") should generalMultiParseTo( - Right(baseResponse.withEntity(Chunked(`application/pdf`, source( - Chunk(ByteString("abc")), - Chunk(ByteString("0123456789ABCDEF"), "some=stuff;bla"), - Chunk(ByteString("0123456789ABCDEF"), "foo=bar"), - Chunk(ByteString("0123456789ABCDEF")), LastChunk))))) - closeAfterResponseCompletion shouldEqual Seq(false) - } - - "message end" in new Test { - Seq( - start, - """0 - | - |""") should generalMultiParseTo( - Right(baseResponse.withEntity(Chunked(`application/pdf`, source(LastChunk))))) - closeAfterResponseCompletion shouldEqual Seq(false) - } - - "message end with extension, trailer and remaining content" in new Test { - Seq( - start, - """000;nice=true - |Foo: pip - | apo - |Bar: xyz - | - |HT""") should generalMultiParseTo( - Right(baseResponse.withEntity(Chunked( - `application/pdf`, - source(LastChunk("nice=true", List(RawHeader("Foo", "pip apo"), RawHeader("Bar", "xyz"))))))), - Left(MessageStartError(400: StatusCode, ErrorInfo("Illegal HTTP message start")))) - closeAfterResponseCompletion shouldEqual Seq(false) - } - - "response with additional transfer encodings" in new Test { - Seq("""HTTP/1.1 200 OK - |Transfer-Encoding: fancy, chunked - |Cont""", """ent-Type: application/pdf - | - |""") should generalMultiParseTo( - Right(HttpResponse( - headers = List(`Transfer-Encoding`(TransferEncodings.Extension("fancy"))), - entity = HttpEntity.Chunked(`application/pdf`, source()))), - Left(EntityStreamError(ErrorInfo("Entity stream truncation")))) - closeAfterResponseCompletion shouldEqual Seq(false) - } - } - - "reject a response with" - { - "HTTP version 1.2" in new Test { - Seq("HTTP/1.2 200 OK\r\n") should generalMultiParseTo(Left(MessageStartError( - 400: StatusCode, ErrorInfo("The server-side HTTP version is not supported")))) - } - - "an illegal status code" in new Test { - Seq("HTTP/1", ".1 2000 Something") should generalMultiParseTo(Left(MessageStartError( - 400: StatusCode, ErrorInfo("Illegal response status code")))) - } - - "a too-long response status reason" in new Test { - Seq("HTTP/1.1 204 12345678", "90123456789012\r\n") should generalMultiParseTo(Left( - MessageStartError(400: StatusCode, ErrorInfo("Response reason phrase exceeds the configured limit of 21 characters")))) - } - - "with a missing reason phrase and no trailing space" in new Test { - Seq("HTTP/1.1 200\r\nContent-Length: 0\r\n\r\n") should generalMultiParseTo(Left(MessageStartError( - 400: StatusCode, ErrorInfo("Status code misses trailing space")))) - } - } - } - - override def afterAll() = system.terminate() - - private class Test { - def awaitAtMost: FiniteDuration = 3.seconds - var closeAfterResponseCompletion = Seq.empty[Boolean] - - class StrictEqualHttpResponse(val resp: HttpResponse) { - override def equals(other: scala.Any): Boolean = other match { - case other: StrictEqualHttpResponse ⇒ - - this.resp.copy(entity = HttpEntity.Empty) == other.resp.copy(entity = HttpEntity.Empty) && - Await.result(this.resp.entity.toStrict(awaitAtMost), awaitAtMost) == - Await.result(other.resp.entity.toStrict(awaitAtMost), awaitAtMost) - } - - override def toString = resp.toString - } - - def strictEqualify[T](x: Either[T, HttpResponse]): Either[T, StrictEqualHttpResponse] = - x.right.map(new StrictEqualHttpResponse(_)) - - def parseTo(expected: HttpResponse*): Matcher[String] = parseTo(GET, expected: _*) - def parseTo(requestMethod: HttpMethod, expected: HttpResponse*): Matcher[String] = - multiParseTo(requestMethod, expected: _*).compose(_ :: Nil) - - def multiParseTo(expected: HttpResponse*): Matcher[Seq[String]] = multiParseTo(GET, expected: _*) - def multiParseTo(requestMethod: HttpMethod, expected: HttpResponse*): Matcher[Seq[String]] = - rawMultiParseTo(requestMethod, expected: _*).compose(_ map prep) - - def rawMultiParseTo(expected: HttpResponse*): Matcher[Seq[String]] = rawMultiParseTo(GET, expected: _*) - def rawMultiParseTo(requestMethod: HttpMethod, expected: HttpResponse*): Matcher[Seq[String]] = - generalRawMultiParseTo(requestMethod, expected.map(Right(_)): _*) - - def parseToError(error: ResponseOutput): Matcher[String] = generalMultiParseTo(Left(error)).compose(_ :: Nil) - - def generalMultiParseTo(expected: Either[ResponseOutput, HttpResponse]*): Matcher[Seq[String]] = - generalRawMultiParseTo(expected: _*).compose(_ map prep) - - def generalRawMultiParseTo(expected: Either[ResponseOutput, HttpResponse]*): Matcher[Seq[String]] = - generalRawMultiParseTo(GET, expected: _*) - def generalRawMultiParseTo(requestMethod: HttpMethod, expected: Either[ResponseOutput, HttpResponse]*): Matcher[Seq[String]] = - equal(expected.map(strictEqualify)) - .matcher[Seq[Either[ResponseOutput, StrictEqualHttpResponse]]] compose { input: Seq[String] ⇒ - collectBlocking { - rawParse(requestMethod, input: _*) - .mapAsync(1) { - case Right(response) ⇒ compactEntity(response.entity).fast.map(x ⇒ Right(response.withEntity(x))) - case Left(error) ⇒ FastFuture.successful(Left(error)) - } - }.map(strictEqualify) - } - - def rawParse(requestMethod: HttpMethod, input: String*): Source[Either[ResponseOutput, HttpResponse], NotUsed] = - Source(input.toList) - .map(bytes ⇒ SessionBytes(TLSPlacebo.dummySession, ByteString(bytes))) - .via(newParserStage(requestMethod)).named("parser") - .splitWhen(x ⇒ x.isInstanceOf[MessageStart] || x.isInstanceOf[EntityStreamError]) - .prefixAndTail(1) - .collect { - case (Seq(ResponseStart(statusCode, protocol, headers, createEntity, close)), entityParts) ⇒ - closeAfterResponseCompletion :+= close - Right(HttpResponse(statusCode, headers, createEntity(entityParts), protocol)) - case (Seq(x @ (MessageStartError(_, _) | EntityStreamError(_))), tail) ⇒ - tail.runWith(Sink.ignore) - Left(x) - }.concatSubstreams - - def collectBlocking[T](source: Source[T, Any]): Seq[T] = - Await.result(source.limit(100000).runWith(Sink.seq), 500.millis) - - protected def parserSettings: ParserSettings = ParserSettings(system) - - def newParserStage(requestMethod: HttpMethod = GET) = { - val parser = new HttpResponseParser(parserSettings, HttpHeaderParser(parserSettings, system.log)()) - parser.setContextForNextResponse(HttpResponseParser.ResponseContext(requestMethod, None)) - parser.stage - } - - private def compactEntity(entity: ResponseEntity): Future[ResponseEntity] = - entity match { - case x: HttpEntity.Chunked ⇒ compactEntityChunks(x.chunks).fast.map(compacted ⇒ x.copy(chunks = compacted)) - case _ ⇒ entity.toStrict(awaitAtMost) - } - - private def compactEntityChunks(data: Source[ChunkStreamPart, Any]): Future[Source[ChunkStreamPart, Any]] = - data.limit(100000).runWith(Sink.seq) - .fast.map(source(_: _*)) - .fast.recover { case _: NoSuchElementException ⇒ source() } - - def prep(response: String) = response.stripMarginWithNewline("\r\n") - - def source[T](elems: T*): Source[T, NotUsed] = Source(elems.toList) - } -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/rendering/RequestRendererSpec.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/rendering/RequestRendererSpec.scala deleted file mode 100644 index 205481a6c8..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/rendering/RequestRendererSpec.scala +++ /dev/null @@ -1,339 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.rendering - -import com.typesafe.config.{ Config, ConfigFactory } -import java.net.InetSocketAddress -import scala.concurrent.duration._ -import scala.concurrent.Await -import org.scalatest.{ FreeSpec, Matchers, BeforeAndAfterAll } -import org.scalatest.matchers.Matcher -import akka.actor.ActorSystem -import akka.event.NoLogging -import akka.util.ByteString -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers._ -import akka.http.impl.util._ -import akka.stream.scaladsl._ -import akka.stream.ActorMaterializer -import HttpEntity._ -import HttpMethods._ - -class RequestRendererSpec extends FreeSpec with Matchers with BeforeAndAfterAll { - val testConf: Config = ConfigFactory.parseString(""" - akka.event-handlers = ["akka.testkit.TestEventListener"] - akka.loglevel = WARNING""") - implicit val system = ActorSystem(getClass.getSimpleName, testConf) - import system.dispatcher - - implicit val materializer = ActorMaterializer() - - "The request preparation logic should" - { - "properly render an unchunked" - { - - "GET request without headers and without body" in new TestSetup() { - HttpRequest(GET, "/abc") should renderTo { - """GET /abc HTTP/1.1 - |Host: test.com:8080 - |User-Agent: akka-http/1.0.0 - | - |""" - } - } - - "GET request with a URI that requires encoding" in new TestSetup() { - HttpRequest(GET, "/abc; rel=first - |Host: spray.io:9999 - |User-Agent: akka-http/1.0.0 - |Content-Length: 0 - | - |""" - } - } - - "PUT request, a few headers and a body" in new TestSetup() { - HttpRequest(PUT, "/abc/xyz", List( - RawHeader("X-Fancy", "naa"), - RawHeader("Cache-Control", "public"), - Host("spray.io"))).withEntity("The content please!") should renderTo { - """PUT /abc/xyz HTTP/1.1 - |X-Fancy: naa - |Cache-Control: public - |Host: spray.io - |User-Agent: akka-http/1.0.0 - |Content-Type: text/plain; charset=UTF-8 - |Content-Length: 19 - | - |The content please!""" - } - } - - "PUT request, a few headers and a body with suppressed content type" in new TestSetup() { - HttpRequest(PUT, "/abc/xyz", List( - RawHeader("X-Fancy", "naa"), - RawHeader("Cache-Control", "public"), - Host("spray.io")), HttpEntity(ContentTypes.NoContentType, ByteString("The content please!"))) should renderTo { - """PUT /abc/xyz HTTP/1.1 - |X-Fancy: naa - |Cache-Control: public - |Host: spray.io - |User-Agent: akka-http/1.0.0 - |Content-Length: 19 - | - |The content please!""" - } - } - - "PUT request with a custom Transfer-Encoding header" in new TestSetup() { - HttpRequest(PUT, "/abc/xyz", List(`Transfer-Encoding`(TransferEncodings.Extension("fancy")))) - .withEntity("The content please!") should renderTo { - """PUT /abc/xyz HTTP/1.1 - |Transfer-Encoding: fancy - |Host: test.com:8080 - |User-Agent: akka-http/1.0.0 - |Content-Type: text/plain; charset=UTF-8 - |Content-Length: 19 - | - |The content please!""" - } - } - - "DELETE request without headers and without body" in new TestSetup() { - HttpRequest(DELETE, "/abc") should renderTo { - """DELETE /abc HTTP/1.1 - |Host: test.com:8080 - |User-Agent: akka-http/1.0.0 - | - |""" - } - } - } - - "proper render a chunked" - { - - "PUT request with empty chunk stream and custom Content-Type" in new TestSetup() { - HttpRequest(PUT, "/abc/xyz", entity = Chunked(ContentTypes.`text/plain(UTF-8)`, Source.empty)) should renderTo { - """PUT /abc/xyz HTTP/1.1 - |Host: test.com:8080 - |User-Agent: akka-http/1.0.0 - |Content-Type: text/plain; charset=UTF-8 - |Content-Length: 0 - | - |""" - } - } - - "POST request with body" in new TestSetup() { - HttpRequest(POST, "/abc/xyz", entity = Chunked( - ContentTypes.`text/plain(UTF-8)`, - source("XXXX", "ABCDEFGHIJKLMNOPQRSTUVWXYZ"))) should renderTo { - """POST /abc/xyz HTTP/1.1 - |Host: test.com:8080 - |User-Agent: akka-http/1.0.0 - |Transfer-Encoding: chunked - |Content-Type: text/plain; charset=UTF-8 - | - |4 - |XXXX - |1a - |ABCDEFGHIJKLMNOPQRSTUVWXYZ - |0 - | - |""" - } - } - - "POST request with chunked body and explicit LastChunk" in new TestSetup() { - val chunks = - List( - ChunkStreamPart("XXXX"), - ChunkStreamPart("ABCDEFGHIJKLMNOPQRSTUVWXYZ"), - LastChunk) - - HttpRequest(POST, "/abc/xyz", entity = Chunked( - ContentTypes.`text/plain(UTF-8)`, - Source(chunks))) should renderTo { - """POST /abc/xyz HTTP/1.1 - |Host: test.com:8080 - |User-Agent: akka-http/1.0.0 - |Transfer-Encoding: chunked - |Content-Type: text/plain; charset=UTF-8 - | - |4 - |XXXX - |1a - |ABCDEFGHIJKLMNOPQRSTUVWXYZ - |0 - | - |""" - } - } - - "POST request with chunked body and extra LastChunks at the end (which should be ignored)" in new TestSetup() { - val chunks = - List( - ChunkStreamPart("XXXX"), - ChunkStreamPart("ABCDEFGHIJKLMNOPQRSTUVWXYZ"), - LastChunk, - LastChunk) - - HttpRequest(POST, "/abc/xyz", entity = Chunked( - ContentTypes.`text/plain(UTF-8)`, - Source(chunks))) should renderTo { - """POST /abc/xyz HTTP/1.1 - |Host: test.com:8080 - |User-Agent: akka-http/1.0.0 - |Transfer-Encoding: chunked - |Content-Type: text/plain; charset=UTF-8 - | - |4 - |XXXX - |1a - |ABCDEFGHIJKLMNOPQRSTUVWXYZ - |0 - | - |""" - } - } - - "POST request with custom Transfer-Encoding header" in new TestSetup() { - HttpRequest(POST, "/abc/xyz", List(`Transfer-Encoding`(TransferEncodings.Extension("fancy"))), - entity = Chunked(ContentTypes.`text/plain(UTF-8)`, source("XXXX", "ABCDEFGHIJKLMNOPQRSTUVWXYZ"))) should renderTo { - """POST /abc/xyz HTTP/1.1 - |Transfer-Encoding: fancy, chunked - |Host: test.com:8080 - |User-Agent: akka-http/1.0.0 - |Content-Type: text/plain; charset=UTF-8 - | - |4 - |XXXX - |1a - |ABCDEFGHIJKLMNOPQRSTUVWXYZ - |0 - | - |""" - } - } - } - - "properly handle the User-Agent header" - { - "if no default is set and no explicit User-Agent header given" in new TestSetup(None) { - HttpRequest(GET, "/abc") should renderTo { - """GET /abc HTTP/1.1 - |Host: test.com:8080 - | - |""" - } - } - "if a default is set but an explicit User-Agent header given" in new TestSetup() { - HttpRequest(GET, "/abc", List(`User-Agent`("user-ua/1.0"))) should renderTo { - """GET /abc HTTP/1.1 - |User-Agent: user-ua/1.0 - |Host: test.com:8080 - | - |""" - } - } - } - "render a CustomHeader header" - { - "if renderInRequests = true" in new TestSetup(None) { - case class MyHeader(number: Int) extends CustomHeader { - def renderInRequests = true - def renderInResponses = false - def name: String = "X-My-Header" - def value: String = s"No$number" - } - HttpRequest(GET, "/abc", List(MyHeader(5))) should renderTo { - """GET /abc HTTP/1.1 - |X-My-Header: No5 - |Host: test.com:8080 - | - |""" - } - } - "not if renderInRequests = false" in new TestSetup(None) { - case class MyInternalHeader(number: Int) extends CustomHeader { - def renderInRequests = false - def renderInResponses = false - def name: String = "X-My-Internal-Header" - def value: String = s"No$number" - } - HttpRequest(GET, "/abc", List(MyInternalHeader(5))) should renderTo { - """GET /abc HTTP/1.1 - |Host: test.com:8080 - | - |""" - } - } - } - - "properly use URI from Raw-Request-URI header if present" - { - "GET request with Raw-Request-URI" in new TestSetup() { - HttpRequest(GET, "/abc", List(`Raw-Request-URI`("/def"))) should renderTo { - """GET /def HTTP/1.1 - |Host: test.com:8080 - |User-Agent: akka-http/1.0.0 - | - |""" - } - } - - "GET request with Raw-Request-URI sends raw URI even with invalid utf8 characters" in new TestSetup() { - HttpRequest(GET, "/abc", List(`Raw-Request-URI`("/def%80%fe%ff"))) should renderTo { - """GET /def%80%fe%ff HTTP/1.1 - |Host: test.com:8080 - |User-Agent: akka-http/1.0.0 - | - |""" - } - } - } - } - - override def afterAll() = system.terminate() - - class TestSetup( - val userAgent: Option[`User-Agent`] = Some(`User-Agent`("akka-http/1.0.0")), - serverAddress: InetSocketAddress = new InetSocketAddress("test.com", 8080)) - extends HttpRequestRendererFactory(userAgent, requestHeaderSizeHint = 64, NoLogging) { - - def awaitAtMost: FiniteDuration = 3.seconds - - def renderTo(expected: String): Matcher[HttpRequest] = - equal(expected.stripMarginWithNewline("\r\n")).matcher[String] compose { request ⇒ - val byteStringSource = renderToSource(RequestRenderingContext(request, Host(serverAddress))) - val future = byteStringSource.limit(1000).runWith(Sink.seq).map(_.reduceLeft(_ ++ _).utf8String) - Await.result(future, awaitAtMost) - } - } - - def source[T](elems: T*) = Source(elems.toList) -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/rendering/ResponseRendererSpec.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/rendering/ResponseRendererSpec.scala deleted file mode 100644 index f76937a59d..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/rendering/ResponseRendererSpec.scala +++ /dev/null @@ -1,640 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.rendering - -import com.typesafe.config.{ Config, ConfigFactory } -import scala.concurrent.duration._ -import scala.concurrent.Await -import org.scalatest.{ FreeSpec, Matchers, BeforeAndAfterAll } -import org.scalatest.matchers.Matcher -import akka.actor.ActorSystem -import akka.event.NoLogging -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers._ -import akka.http.impl.util._ -import akka.util.ByteString -import akka.stream.scaladsl._ -import akka.stream.ActorMaterializer -import HttpEntity._ - -import scala.util.control.NonFatal - -class ResponseRendererSpec extends FreeSpec with Matchers with BeforeAndAfterAll { - val testConf: Config = ConfigFactory.parseString(""" - akka.event-handlers = ["akka.testkit.TestEventListener"] - akka.loglevel = WARNING""") - implicit val system = ActorSystem(getClass.getSimpleName, testConf) - - val ServerOnTheMove = StatusCodes.custom(330, "Server on the move") - implicit val materializer = ActorMaterializer() - - "The response preparation logic should properly render" - { - "a response with no body," - { - "status 200 and no headers" in new TestSetup() { - HttpResponse(200) should renderTo { - """HTTP/1.1 200 OK - |Server: akka-http/1.0.0 - |Date: Thu, 25 Aug 2011 09:10:29 GMT - |Content-Length: 0 - | - |""" - } - } - - "a custom Date header" in new TestSetup() { - HttpResponse(200, List(Date(DateTime(2011, 8, 26, 10, 11, 59)))) should renderTo { - """HTTP/1.1 200 OK - |Date: Fri, 26 Aug 2011 10:11:59 GMT - |Server: akka-http/1.0.0 - |Content-Length: 0 - | - |""" - } - } - - "status 304 and a few headers" in new TestSetup() { - HttpResponse(304, List(RawHeader("X-Fancy", "of course"), Age(0))) should renderTo { - """HTTP/1.1 304 Not Modified - |X-Fancy: of course - |Age: 0 - |Server: akka-http/1.0.0 - |Date: Thu, 25 Aug 2011 09:10:29 GMT - | - |""" - } - } - "a custom status code and no headers" in new TestSetup() { - HttpResponse(ServerOnTheMove) should renderTo { - """HTTP/1.1 330 Server on the move - |Server: akka-http/1.0.0 - |Date: Thu, 25 Aug 2011 09:10:29 GMT - |Content-Length: 0 - | - |""" - } - } - "a custom status code and no headers and different dates" in new TestSetup() { - val initial = DateTime(2011, 8, 25, 9, 10, 0).clicks - var extraMillis = 0L - (0 until 10000 by 500) foreach { millis ⇒ - extraMillis = millis - HttpResponse(200) should renderTo { - s"""HTTP/1.1 200 OK - |Server: akka-http/1.0.0 - |Date: Thu, 25 Aug 2011 09:10:0${extraMillis / 1000 % 60} GMT - |Content-Length: 0 - | - |""" - } - } - - override def currentTimeMillis() = initial + extraMillis - } - - "to a transparent HEAD request (Strict response entity)" in new TestSetup() { - ResponseRenderingContext( - requestMethod = HttpMethods.HEAD, - response = HttpResponse( - headers = List(Age(30), Connection("Keep-Alive")), - entity = "Small f*ck up overhere!")) should renderTo( - """HTTP/1.1 200 OK - |Age: 30 - |Server: akka-http/1.0.0 - |Date: Thu, 25 Aug 2011 09:10:29 GMT - |Content-Type: text/plain; charset=UTF-8 - |Content-Length: 23 - | - |""", close = false) - } - - "to a transparent HEAD request (CloseDelimited response entity)" in new TestSetup() { - ResponseRenderingContext( - requestMethod = HttpMethods.HEAD, - response = HttpResponse( - headers = List(Age(30), Connection("Keep-Alive")), - entity = HttpEntity.CloseDelimited( - ContentTypes.`text/plain(UTF-8)`, - Source.single(ByteString("Foo"))))) should renderTo( - """HTTP/1.1 200 OK - |Age: 30 - |Server: akka-http/1.0.0 - |Date: Thu, 25 Aug 2011 09:10:29 GMT - |Content-Type: text/plain; charset=UTF-8 - | - |""", close = false) - } - - "to a transparent HEAD request (Chunked response entity)" in new TestSetup() { - ResponseRenderingContext( - requestMethod = HttpMethods.HEAD, - response = HttpResponse( - headers = List(Age(30), Connection("Keep-Alive")), - entity = HttpEntity.Chunked( - ContentTypes.`text/plain(UTF-8)`, - Source.single(HttpEntity.Chunk(ByteString("Foo")))))) should renderTo( - """HTTP/1.1 200 OK - |Age: 30 - |Server: akka-http/1.0.0 - |Date: Thu, 25 Aug 2011 09:10:29 GMT - |Transfer-Encoding: chunked - |Content-Type: text/plain; charset=UTF-8 - | - |""", close = false) - } - - "to a HEAD request setting a custom Content-Type and Content-Length (default response entity)" in new TestSetup() { - ResponseRenderingContext( - requestMethod = HttpMethods.HEAD, - response = HttpResponse( - headers = List(Age(30)), - entity = HttpEntity.Default(ContentTypes.`text/plain(UTF-8)`, 100, Source.empty))) should renderTo( - """HTTP/1.1 200 OK - |Age: 30 - |Server: akka-http/1.0.0 - |Date: Thu, 25 Aug 2011 09:10:29 GMT - |Content-Type: text/plain; charset=UTF-8 - |Content-Length: 100 - | - |""", close = false) - } - } - - "a response with a Strict body," - { - "status 400 and a few headers" in new TestSetup() { - HttpResponse(400, List(Age(30), Connection("Keep-Alive")), "Small f*ck up overhere!") should renderTo { - """HTTP/1.1 400 Bad Request - |Age: 30 - |Server: akka-http/1.0.0 - |Date: Thu, 25 Aug 2011 09:10:29 GMT - |Content-Type: text/plain; charset=UTF-8 - |Content-Length: 23 - | - |Small f*ck up overhere!""" - } - } - - "status 400, a few headers and a body with an explicitly suppressed Content Type header" in new TestSetup() { - HttpResponse(400, List(Age(30), Connection("Keep-Alive")), - HttpEntity(ContentTypes.NoContentType, ByteString("Small f*ck up overhere!"))) should renderTo { - """HTTP/1.1 400 Bad Request - |Age: 30 - |Server: akka-http/1.0.0 - |Date: Thu, 25 Aug 2011 09:10:29 GMT - |Content-Length: 23 - | - |Small f*ck up overhere!""" - } - } - - "status 200 and a custom Transfer-Encoding header" in new TestSetup() { - HttpResponse( - headers = List(`Transfer-Encoding`(TransferEncodings.Extension("fancy"))), - entity = "All good") should renderTo { - """HTTP/1.1 200 OK - |Transfer-Encoding: fancy - |Server: akka-http/1.0.0 - |Date: Thu, 25 Aug 2011 09:10:29 GMT - |Content-Type: text/plain; charset=UTF-8 - |Content-Length: 8 - | - |All good""" - } - } - } - "a response with a Default (streamed with explicit content-length body," - { - "status 400 and a few headers" in new TestSetup() { - HttpResponse(400, List(Age(30), Connection("Keep-Alive")), - entity = Default(ContentTypes.`text/plain(UTF-8)`, 23, source(ByteString("Small f*ck up overhere!")))) should renderTo { - """HTTP/1.1 400 Bad Request - |Age: 30 - |Server: akka-http/1.0.0 - |Date: Thu, 25 Aug 2011 09:10:29 GMT - |Content-Type: text/plain; charset=UTF-8 - |Content-Length: 23 - | - |Small f*ck up overhere!""" - } - } - "one chunk and incorrect (too large) Content-Length" in new TestSetup() { - the[RuntimeException] thrownBy { - HttpResponse(200, entity = Default(ContentTypes.`application/json`, 10, - source(ByteString("body123")))) should renderTo("") - } should have message "HTTP message had declared Content-Length 10 but entity data stream amounts to 3 bytes less" - } - - "one chunk and incorrect (too small) Content-Length" in new TestSetup() { - the[RuntimeException] thrownBy { - HttpResponse(200, entity = Default(ContentTypes.`application/json`, 5, - source(ByteString("body123")))) should renderTo("") - } should have message "HTTP message had declared Content-Length 5 but entity data stream amounts to more bytes" - } - - } - "a response with a CloseDelimited body" - { - "without data" in new TestSetup() { - ResponseRenderingContext( - HttpResponse(200, entity = CloseDelimited( - ContentTypes.`application/json`, - source(ByteString.empty)))) should renderTo( - """HTTP/1.1 200 OK - |Server: akka-http/1.0.0 - |Date: Thu, 25 Aug 2011 09:10:29 GMT - |Connection: close - |Content-Type: application/json - | - |""", close = true) - } - "consisting of two parts" in new TestSetup() { - ResponseRenderingContext( - HttpResponse(200, entity = CloseDelimited( - ContentTypes.`application/json`, - source(ByteString("abc"), ByteString("defg"))))) should renderTo( - """HTTP/1.1 200 OK - |Server: akka-http/1.0.0 - |Date: Thu, 25 Aug 2011 09:10:29 GMT - |Connection: close - |Content-Type: application/json - | - |abcdefg""", close = true) - } - } - - "a chunked response" - { - "with empty entity" in new TestSetup() { - HttpResponse(200, List(Age(30)), - Chunked(ContentTypes.NoContentType, Source.empty)) should renderTo { - """HTTP/1.1 200 OK - |Age: 30 - |Server: akka-http/1.0.0 - |Date: Thu, 25 Aug 2011 09:10:29 GMT - | - |""" - } - } - - "with empty entity but non-default Content-Type" in new TestSetup() { - HttpResponse(200, List(Age(30)), - Chunked(ContentTypes.`application/json`, Source.empty)) should renderTo { - """HTTP/1.1 200 OK - |Age: 30 - |Server: akka-http/1.0.0 - |Date: Thu, 25 Aug 2011 09:10:29 GMT - |Content-Type: application/json - | - |""" - } - } - - "with one chunk and no explicit LastChunk" in new TestSetup() { - HttpResponse(entity = Chunked( - ContentTypes.`text/plain(UTF-8)`, - source("Yahoooo"))) should renderTo { - """HTTP/1.1 200 OK - |Server: akka-http/1.0.0 - |Date: Thu, 25 Aug 2011 09:10:29 GMT - |Transfer-Encoding: chunked - |Content-Type: text/plain; charset=UTF-8 - | - |7 - |Yahoooo - |0 - | - |""" - } - } - - "with one chunk and an explicit LastChunk" in new TestSetup() { - HttpResponse(entity = Chunked( - ContentTypes.`text/plain(UTF-8)`, - source( - Chunk(ByteString("body123"), """key=value;another="tl;dr""""), - LastChunk("foo=bar", List(Age(30), RawHeader("Cache-Control", "public")))))) should renderTo { - """HTTP/1.1 200 OK - |Server: akka-http/1.0.0 - |Date: Thu, 25 Aug 2011 09:10:29 GMT - |Transfer-Encoding: chunked - |Content-Type: text/plain; charset=UTF-8 - | - |7;key=value;another="tl;dr" - |body123 - |0;foo=bar - |Age: 30 - |Cache-Control: public - | - |""" - } - } - - "with one chunk and and extra LastChunks at the end (which should be ignored)" in new TestSetup() { - HttpResponse(entity = Chunked( - ContentTypes.`text/plain(UTF-8)`, - source( - Chunk(ByteString("body123"), """key=value;another="tl;dr""""), - LastChunk("foo=bar", List(Age(30), RawHeader("Cache-Control", "public"))), LastChunk))) should renderTo { - """HTTP/1.1 200 OK - |Server: akka-http/1.0.0 - |Date: Thu, 25 Aug 2011 09:10:29 GMT - |Transfer-Encoding: chunked - |Content-Type: text/plain; charset=UTF-8 - | - |7;key=value;another="tl;dr" - |body123 - |0;foo=bar - |Age: 30 - |Cache-Control: public - | - |""" - } - } - - "with a custom Transfer-Encoding header" in new TestSetup() { - HttpResponse( - headers = List(`Transfer-Encoding`(TransferEncodings.Extension("fancy"))), - entity = Chunked(ContentTypes.`text/plain(UTF-8)`, source("Yahoooo"))) should renderTo { - """HTTP/1.1 200 OK - |Transfer-Encoding: fancy, chunked - |Server: akka-http/1.0.0 - |Date: Thu, 25 Aug 2011 09:10:29 GMT - |Content-Type: text/plain; charset=UTF-8 - | - |7 - |Yahoooo - |0 - | - |""" - } - } - } - - "chunked responses to a HTTP/1.0 request" - { - "with two chunks" in new TestSetup() { - ResponseRenderingContext( - requestProtocol = HttpProtocols.`HTTP/1.0`, - response = HttpResponse(entity = Chunked( - ContentTypes.`application/json`, - source(Chunk("abc"), Chunk("defg"))))) should renderTo( - """HTTP/1.1 200 OK - |Server: akka-http/1.0.0 - |Date: Thu, 25 Aug 2011 09:10:29 GMT - |Connection: close - |Content-Type: application/json - | - |abcdefg""", close = true) - } - - "with one chunk and an explicit LastChunk" in new TestSetup() { - ResponseRenderingContext( - requestProtocol = HttpProtocols.`HTTP/1.0`, - response = HttpResponse(entity = Chunked( - ContentTypes.`text/plain(UTF-8)`, - source( - Chunk(ByteString("body123"), """key=value;another="tl;dr""""), - LastChunk("foo=bar", List(Age(30), RawHeader("Cache-Control", "public"))))))) should renderTo( - """HTTP/1.1 200 OK - |Server: akka-http/1.0.0 - |Date: Thu, 25 Aug 2011 09:10:29 GMT - |Connection: close - |Content-Type: text/plain; charset=UTF-8 - | - |body123""", close = true) - } - } - - "properly handle the Server header" - { - "if no default is set and no explicit Server header given" in new TestSetup(None) { - HttpResponse(200) should renderTo { - """HTTP/1.1 200 OK - |Date: Thu, 25 Aug 2011 09:10:29 GMT - |Content-Length: 0 - | - |""" - } - } - "if a default is set but an explicit Server header given" in new TestSetup() { - HttpResponse(200, List(Server("server/1.0"))) should renderTo { - """HTTP/1.1 200 OK - |Server: server/1.0 - |Date: Thu, 25 Aug 2011 09:10:29 GMT - |Content-Length: 0 - | - |""" - } - } - } - - "render a CustomHeader header" - { - "if renderInResponses = true" in new TestSetup(None) { - case class MyHeader(number: Int) extends CustomHeader { - def renderInRequests = false - def renderInResponses = true - def name: String = "X-My-Header" - def value: String = s"No$number" - } - HttpResponse(200, List(MyHeader(5))) should renderTo { - """HTTP/1.1 200 OK - |X-My-Header: No5 - |Date: Thu, 25 Aug 2011 09:10:29 GMT - |Content-Length: 0 - | - |""" - } - } - "not if renderInResponses = false" in new TestSetup(None) { - case class MyInternalHeader(number: Int) extends CustomHeader { - def renderInRequests = false - def renderInResponses = false - def name: String = "X-My-Internal-Header" - def value: String = s"No$number" - } - HttpResponse(200, List(MyInternalHeader(5))) should renderTo { - """HTTP/1.1 200 OK - |Date: Thu, 25 Aug 2011 09:10:29 GMT - |Content-Length: 0 - | - |""" - } - } - } - - "The 'Connection' header should be rendered correctly" in new TestSetup() { - import org.scalatest.prop.TableDrivenPropertyChecks._ - import HttpProtocols._ - - def NONE: Option[Connection] = None - def CLOSE: Option[Connection] = Some(Connection("close")) - def KEEPA: Option[Connection] = Some(Connection("Keep-Alive")) - // format: OFF - val table = Table( - //--- requested by the client ----// //----- set by server app -----// //--- actually done ---// - // Request Request Response Response Rendered Connection - // Request Method Connection Response Connection Entity Connection Closed after - // Protocol is HEAD Header Protocol Header CloseDelim Header Response - ("Req Prot", "HEAD Req", "Req CH", "Res Prot", "Res CH", "Res CD", "Ren Conn", "Close"), - (`HTTP/1.1`, false, NONE, `HTTP/1.1`, NONE, false, NONE, false), - (`HTTP/1.1`, false, NONE, `HTTP/1.1`, NONE, true, CLOSE, true), - (`HTTP/1.1`, false, NONE, `HTTP/1.1`, CLOSE, false, CLOSE, true), - (`HTTP/1.1`, false, NONE, `HTTP/1.1`, CLOSE, true, CLOSE, true), - (`HTTP/1.1`, false, NONE, `HTTP/1.1`, KEEPA, false, NONE, false), - (`HTTP/1.1`, false, NONE, `HTTP/1.1`, KEEPA, true, CLOSE, true), - (`HTTP/1.1`, false, NONE, `HTTP/1.0`, NONE, false, CLOSE, true), - (`HTTP/1.1`, false, NONE, `HTTP/1.0`, NONE, true, CLOSE, true), - (`HTTP/1.1`, false, NONE, `HTTP/1.0`, CLOSE, false, CLOSE, true), - (`HTTP/1.1`, false, NONE, `HTTP/1.0`, CLOSE, true, CLOSE, true), - (`HTTP/1.1`, false, NONE, `HTTP/1.0`, KEEPA, false, KEEPA, false), - (`HTTP/1.1`, false, NONE, `HTTP/1.0`, KEEPA, true, CLOSE, true), - (`HTTP/1.1`, false, CLOSE, `HTTP/1.1`, NONE, false, CLOSE, true), - (`HTTP/1.1`, false, CLOSE, `HTTP/1.1`, NONE, true, CLOSE, true), - (`HTTP/1.1`, false, CLOSE, `HTTP/1.1`, CLOSE, false, CLOSE, true), - (`HTTP/1.1`, false, CLOSE, `HTTP/1.1`, CLOSE, true, CLOSE, true), - (`HTTP/1.1`, false, CLOSE, `HTTP/1.1`, KEEPA, false, KEEPA, false), - (`HTTP/1.1`, false, CLOSE, `HTTP/1.1`, KEEPA, true, CLOSE, true), - (`HTTP/1.1`, false, CLOSE, `HTTP/1.0`, NONE, false, CLOSE, true), - (`HTTP/1.1`, false, CLOSE, `HTTP/1.0`, NONE, true, CLOSE, true), - (`HTTP/1.1`, false, CLOSE, `HTTP/1.0`, CLOSE, false, CLOSE, true), - (`HTTP/1.1`, false, CLOSE, `HTTP/1.0`, CLOSE, true, CLOSE, true), - (`HTTP/1.1`, false, CLOSE, `HTTP/1.0`, KEEPA, false, KEEPA, false), - (`HTTP/1.1`, false, CLOSE, `HTTP/1.0`, KEEPA, true, CLOSE, true), - (`HTTP/1.1`, true, NONE, `HTTP/1.1`, NONE, false, NONE, false), - (`HTTP/1.1`, true, NONE, `HTTP/1.1`, NONE, true, NONE, false), - (`HTTP/1.1`, true, NONE, `HTTP/1.1`, CLOSE, false, CLOSE, true), - (`HTTP/1.1`, true, NONE, `HTTP/1.1`, CLOSE, true, CLOSE, true), - (`HTTP/1.1`, true, NONE, `HTTP/1.1`, KEEPA, false, NONE, false), - (`HTTP/1.1`, true, NONE, `HTTP/1.1`, KEEPA, true, NONE, false), - (`HTTP/1.1`, true, NONE, `HTTP/1.0`, NONE, false, CLOSE, true), - (`HTTP/1.1`, true, NONE, `HTTP/1.0`, NONE, true, CLOSE, true), - (`HTTP/1.1`, true, NONE, `HTTP/1.0`, CLOSE, false, CLOSE, true), - (`HTTP/1.1`, true, NONE, `HTTP/1.0`, CLOSE, true, CLOSE, true), - (`HTTP/1.1`, true, NONE, `HTTP/1.0`, KEEPA, false, KEEPA, false), - (`HTTP/1.1`, true, NONE, `HTTP/1.0`, KEEPA, true, KEEPA, false), - (`HTTP/1.1`, true, CLOSE, `HTTP/1.1`, NONE, false, CLOSE, true), - (`HTTP/1.1`, true, CLOSE, `HTTP/1.1`, NONE, true, CLOSE, true), - (`HTTP/1.1`, true, CLOSE, `HTTP/1.1`, CLOSE, false, CLOSE, true), - (`HTTP/1.1`, true, CLOSE, `HTTP/1.1`, CLOSE, true, CLOSE, true), - (`HTTP/1.1`, true, CLOSE, `HTTP/1.1`, KEEPA, false, KEEPA, false), - (`HTTP/1.1`, true, CLOSE, `HTTP/1.1`, KEEPA, true, KEEPA, false), - (`HTTP/1.1`, true, CLOSE, `HTTP/1.0`, NONE, false, CLOSE, true), - (`HTTP/1.1`, true, CLOSE, `HTTP/1.0`, NONE, true, CLOSE, true), - (`HTTP/1.1`, true, CLOSE, `HTTP/1.0`, CLOSE, false, CLOSE, true), - (`HTTP/1.1`, true, CLOSE, `HTTP/1.0`, CLOSE, true, CLOSE, true), - (`HTTP/1.1`, true, CLOSE, `HTTP/1.0`, KEEPA, false, KEEPA, false), - (`HTTP/1.1`, true, CLOSE, `HTTP/1.0`, KEEPA, true, KEEPA, false), - (`HTTP/1.0`, false, NONE, `HTTP/1.1`, NONE, false, CLOSE, true), - (`HTTP/1.0`, false, NONE, `HTTP/1.1`, NONE, true, CLOSE, true), - (`HTTP/1.0`, false, NONE, `HTTP/1.1`, CLOSE, false, CLOSE, true), - (`HTTP/1.0`, false, NONE, `HTTP/1.1`, CLOSE, true, CLOSE, true), - (`HTTP/1.0`, false, NONE, `HTTP/1.1`, KEEPA, false, KEEPA, false), - (`HTTP/1.0`, false, NONE, `HTTP/1.1`, KEEPA, true, CLOSE, true), - (`HTTP/1.0`, false, NONE, `HTTP/1.0`, NONE, false, NONE, true), - (`HTTP/1.0`, false, NONE, `HTTP/1.0`, NONE, true, NONE, true), - (`HTTP/1.0`, false, NONE, `HTTP/1.0`, CLOSE, false, NONE, true), - (`HTTP/1.0`, false, NONE, `HTTP/1.0`, CLOSE, true, NONE, true), - (`HTTP/1.0`, false, NONE, `HTTP/1.0`, KEEPA, false, KEEPA, false), - (`HTTP/1.0`, false, NONE, `HTTP/1.0`, KEEPA, true, NONE, true), - (`HTTP/1.0`, false, KEEPA, `HTTP/1.1`, NONE, false, KEEPA, false), - (`HTTP/1.0`, false, KEEPA, `HTTP/1.1`, NONE, true, CLOSE, true), - (`HTTP/1.0`, false, KEEPA, `HTTP/1.1`, CLOSE, false, CLOSE, true), - (`HTTP/1.0`, false, KEEPA, `HTTP/1.1`, CLOSE, true, CLOSE, true), - (`HTTP/1.0`, false, KEEPA, `HTTP/1.1`, KEEPA, false, KEEPA, false), - (`HTTP/1.0`, false, KEEPA, `HTTP/1.1`, KEEPA, true, CLOSE, true), - (`HTTP/1.0`, false, KEEPA, `HTTP/1.0`, NONE, false, KEEPA, false), - (`HTTP/1.0`, false, KEEPA, `HTTP/1.0`, NONE, true, CLOSE, true), - (`HTTP/1.0`, false, KEEPA, `HTTP/1.0`, CLOSE, false, CLOSE, true), - (`HTTP/1.0`, false, KEEPA, `HTTP/1.0`, CLOSE, true, CLOSE, true), - (`HTTP/1.0`, false, KEEPA, `HTTP/1.0`, KEEPA, false, KEEPA, false), - (`HTTP/1.0`, false, KEEPA, `HTTP/1.0`, KEEPA, true, CLOSE, true), - (`HTTP/1.0`, true, NONE, `HTTP/1.1`, NONE, false, CLOSE, true), - (`HTTP/1.0`, true, NONE, `HTTP/1.1`, NONE, true, CLOSE, true), - (`HTTP/1.0`, true, NONE, `HTTP/1.1`, CLOSE, false, CLOSE, true), - (`HTTP/1.0`, true, NONE, `HTTP/1.1`, CLOSE, true, CLOSE, true), - (`HTTP/1.0`, true, NONE, `HTTP/1.1`, KEEPA, false, KEEPA, false), - (`HTTP/1.0`, true, NONE, `HTTP/1.1`, KEEPA, true, KEEPA, false), - (`HTTP/1.0`, true, NONE, `HTTP/1.0`, NONE, false, NONE, true), - (`HTTP/1.0`, true, NONE, `HTTP/1.0`, NONE, true, NONE, true), - (`HTTP/1.0`, true, NONE, `HTTP/1.0`, CLOSE, false, NONE, true), - (`HTTP/1.0`, true, NONE, `HTTP/1.0`, CLOSE, true, NONE, true), - (`HTTP/1.0`, true, NONE, `HTTP/1.0`, KEEPA, false, KEEPA, false), - (`HTTP/1.0`, true, NONE, `HTTP/1.0`, KEEPA, true, KEEPA, false), - (`HTTP/1.0`, true, KEEPA, `HTTP/1.1`, NONE, false, KEEPA, false), - (`HTTP/1.0`, true, KEEPA, `HTTP/1.1`, NONE, true, KEEPA, false), - (`HTTP/1.0`, true, KEEPA, `HTTP/1.1`, CLOSE, false, CLOSE, true), - (`HTTP/1.0`, true, KEEPA, `HTTP/1.1`, CLOSE, true, CLOSE, true), - (`HTTP/1.0`, true, KEEPA, `HTTP/1.1`, KEEPA, false, KEEPA, false), - (`HTTP/1.0`, true, KEEPA, `HTTP/1.1`, KEEPA, true, KEEPA, false), - (`HTTP/1.0`, true, KEEPA, `HTTP/1.0`, NONE, false, KEEPA, false), - (`HTTP/1.0`, true, KEEPA, `HTTP/1.0`, NONE, true, KEEPA, false), - (`HTTP/1.0`, true, KEEPA, `HTTP/1.0`, CLOSE, false, CLOSE, true), - (`HTTP/1.0`, true, KEEPA, `HTTP/1.0`, CLOSE, true, CLOSE, true), - (`HTTP/1.0`, true, KEEPA, `HTTP/1.0`, KEEPA, false, KEEPA, false), - (`HTTP/1.0`, true, KEEPA, `HTTP/1.0`, KEEPA, true, KEEPA, false)) - // format: ON - - forAll(table)((reqProto, headReq, reqCH, resProto, resCH, resCD, renCH, close) ⇒ - ResponseRenderingContext( - response = HttpResponse(200, headers = resCH.toList, - entity = if (resCD) HttpEntity.CloseDelimited( - ContentTypes.`text/plain(UTF-8)`, - Source.single(ByteString("ENTITY"))) - else HttpEntity("ENTITY"), protocol = resProto), - requestMethod = if (headReq) HttpMethods.HEAD else HttpMethods.GET, - requestProtocol = reqProto, - closeRequested = HttpMessage.connectionCloseExpected(reqProto, reqCH)) should renderTo( - s"""${resProto.value} 200 OK - |Server: akka-http/1.0.0 - |Date: Thu, 25 Aug 2011 09:10:29 GMT - |${renCH.fold("")(_ + "\n")}Content-Type: text/plain; charset=UTF-8 - |${if (resCD) "" else "Content-Length: 6\n"} - |${if (headReq) "" else "ENTITY"}""", close)) - } - } - - override def afterAll() = system.terminate() - - class TestSetup(val serverHeader: Option[Server] = Some(Server("akka-http/1.0.0"))) - extends HttpResponseRendererFactory(serverHeader, responseHeaderSizeHint = 64, NoLogging) { - - def awaitAtMost: FiniteDuration = 3.seconds - - def renderTo(expected: String): Matcher[HttpResponse] = - renderToImpl(expected, checkClose = None) compose (ResponseRenderingContext(_)) - - def renderTo(expected: String, close: Boolean): Matcher[ResponseRenderingContext] = - renderToImpl(expected, checkClose = Some(close)) - - def renderToImpl(expected: String, checkClose: Option[Boolean]): Matcher[ResponseRenderingContext] = - equal(expected.stripMarginWithNewline("\r\n") → checkClose).matcher[(String, Option[Boolean])] compose { ctx ⇒ - val (wasCompletedFuture, resultFuture) = - (Source.single(ctx) ++ Source.maybe[ResponseRenderingContext]) // never send upstream completion - .via(renderer.named("renderer")) - .map { - case ResponseRenderingOutput.HttpData(bytes) ⇒ bytes - case _: ResponseRenderingOutput.SwitchToWebSocket ⇒ throw new IllegalStateException("Didn't expect websocket response") - } - .groupedWithin(1000, 200.millis) - .watchTermination()(Keep.right) - .toMat(Sink.head)(Keep.both).run() - - val wasCompleted: Option[Boolean] = checkClose match { - case None ⇒ None - case Some(close) ⇒ - // we try to find out if the renderer has already flagged completion even without the upstream being completed - try { - // note how this relates to the groupedWithin timeout above which will always - // close the stream, so only streams closed before that was _actually_ closed - // by the server blueprint - Await.ready(wasCompletedFuture, 150.millis) - Some(true) - } catch { - case NonFatal(_) ⇒ Some(false) - } - } - - Await.result(resultFuture, awaitAtMost).reduceLeft(_ ++ _).utf8String → wasCompleted - } - - override def currentTimeMillis() = DateTime(2011, 8, 25, 9, 10, 29).clicks // provide a stable date for testing - } - - def source[T](elems: T*) = Source(elems.toList) -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/server/HttpServerBug2108Spec.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/server/HttpServerBug2108Spec.scala deleted file mode 100644 index 9428ae00f7..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/server/HttpServerBug2108Spec.scala +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.impl.engine.server - -import akka.http.scaladsl.model.HttpEntity.Chunked -import akka.http.scaladsl.model.HttpMethods._ -import akka.http.scaladsl.model.{ ContentType, HttpRequest, HttpResponse } -import akka.http.scaladsl.model.MediaTypes._ -import akka.stream.{ ActorMaterializer, Materializer } -import akka.stream.testkit.Utils.{ TE, _ } -import akka.testkit.{ AkkaSpec, EventFilter } -import org.scalatest.Inside - -import scala.concurrent.Await -import scala.concurrent.duration._ - -class HttpServerBug2108Spec extends AkkaSpec( - """ - akka.loglevel = WARNING - akka.loggers = ["akka.testkit.TestEventListener", "akka.event.Logging$DefaultLogger"] - akka.http.server.request-timeout = infinite - akka.test.filter-leeway=300ms""") with Inside { spec ⇒ - implicit val materializer = ActorMaterializer() - - "The HttpServer" should { - - "not cause internal graph failures when consuming a `100 Continue` entity triggers a failure" in assertAllStagesStopped(new HttpServerTestSetupBase { - override implicit def system = HttpServerBug2108Spec.this.system - override implicit def materializer: Materializer = HttpServerBug2108Spec.this.materializer - - send("""POST / HTTP/1.1 - |Host: example.com - |Expect: 100-continue - |Transfer-Encoding: chunked - | - |""") - inside(expectRequest()) { - case HttpRequest(POST, _, _, Chunked(ContentType(`application/octet-stream`, None), data), _) ⇒ - val done = data.runForeach(_ ⇒ throw TE("failed on first chunk")) - - expectResponseWithWipedDate( - """HTTP/1.1 100 Continue - |Server: akka-http/test - |Date: XXXX - | - |""") - send("""10 - |0123456789ABCDEF - |0 - | - |""") - - // Bug #21008 does not actually ever make the request fail instead it logs the exception and behaves - // nicely so we need to check that the exception didn't get logged - var sawException = false - try { - EventFilter[IllegalArgumentException](occurrences = 1) intercept { - // make sure the failure has happened - Await.ready(done, 10.seconds) - // and then when the failure has happened/future completes, we push a reply - responses.sendNext(HttpResponse(entity = "Yeah")) - - } - // got such an error, that is bad, - sawException = true - } catch { - case _: AssertionError ⇒ sawException = false - } - if (sawException) fail("HttpServerBluePrint.ControllerStage: requirement failed: Cannot pull closed port (requestParsingIn)") - - // and the client should still get that ok - expectResponseWithWipedDate( - """HTTP/1.1 200 OK - |Server: akka-http/test - |Date: XXXX - |Connection: close - |Content-Type: text/plain; charset=UTF-8 - |Content-Length: 4 - | - |Yeah""") - - } - - netIn.sendComplete() - netOut.expectComplete() - - }) - - } -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/server/HttpServerSpec.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/server/HttpServerSpec.scala deleted file mode 100644 index 17c4ffdc20..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/server/HttpServerSpec.scala +++ /dev/null @@ -1,1156 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.server - -import java.net.{ InetAddress, InetSocketAddress } - -import akka.http.impl.util._ -import akka.http.scaladsl.Http.ServerLayer -import akka.http.scaladsl.model.HttpEntity._ -import akka.http.scaladsl.model.HttpMethods._ -import akka.http.scaladsl.model.MediaTypes._ -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers._ -import akka.http.scaladsl.settings.ServerSettings -import akka.stream.scaladsl._ -import akka.stream.testkit.Utils.assertAllStagesStopped -import akka.stream.testkit._ -import akka.stream.{ ActorMaterializer, Fusing } -import akka.testkit.AkkaSpec -import akka.util.ByteString -import org.scalatest.Inside - -import scala.annotation.tailrec -import scala.concurrent.duration._ -import scala.reflect.ClassTag -import scala.util.Random - -class HttpServerSpec extends AkkaSpec( - """akka.loggers = [] - akka.loglevel = OFF - akka.http.server.request-timeout = infinite""") with Inside { spec ⇒ - implicit val materializer = ActorMaterializer() - - "The server implementation" should { - "deliver an empty request as soon as all headers are received" in assertAllStagesStopped(new TestSetup { - send("""GET / HTTP/1.1 - |Host: example.com - | - |""") - - expectRequest() mapHeaders (_.filterNot(_.is("timeout-access"))) shouldEqual HttpRequest(uri = "http://example.com/", headers = List(Host("example.com"))) - - shutdownBlueprint() - }) - - "deliver a request as soon as all headers are received" in assertAllStagesStopped(new TestSetup { - send("""POST / HTTP/1.1 - |Host: example.com - |Content-Length: 12 - | - |""") - - inside(expectRequest()) { - case HttpRequest(POST, _, _, HttpEntity.Default(_, 12, data), _) ⇒ - val dataProbe = TestSubscriber.manualProbe[ByteString] - data.to(Sink.fromSubscriber(dataProbe)).run() - val sub = dataProbe.expectSubscription() - sub.request(10) - dataProbe.expectNoMsg(50.millis) - - send("abcdef") - dataProbe.expectNext(ByteString("abcdef")) - - send("ghijk") - dataProbe.expectNext(ByteString("ghijk")) - dataProbe.expectNoMsg(50.millis) - } - - shutdownBlueprint() - }) - - "deliver an error response as soon as a parsing error occurred" in assertAllStagesStopped(new TestSetup { - send("""GET / HTTP/1.2 - |Host: example.com - | - |""") - - requests.request(1) - - expectResponseWithWipedDate( - """HTTP/1.1 505 HTTP Version Not Supported - |Server: akka-http/test - |Date: XXXX - |Connection: close - |Content-Type: text/plain; charset=UTF-8 - |Content-Length: 74 - | - |The server does not support the HTTP protocol version used in the request.""") - - netOut.expectComplete() - netIn.sendComplete() - }) - - "report an invalid Chunked stream" in assertAllStagesStopped(new TestSetup { - send("""POST / HTTP/1.1 - |Host: example.com - |Transfer-Encoding: chunked - | - |6 - |abcdef - |""") - - inside(expectRequest()) { - case HttpRequest(POST, _, _, HttpEntity.Chunked(_, data), _) ⇒ - val dataProbe = TestSubscriber.manualProbe[ChunkStreamPart] - data.to(Sink.fromSubscriber(dataProbe)).run() - val sub = dataProbe.expectSubscription() - sub.request(10) - dataProbe.expectNext(Chunk(ByteString("abcdef"))) - dataProbe.expectNoMsg(50.millis) - - send("3ghi\r\n") // missing "\r\n" after the number of bytes - val error = dataProbe.expectError() - error.getMessage shouldEqual "Illegal character 'g' in chunk start" - requests.expectComplete() - - responses.expectRequest() - responses.sendError(error.asInstanceOf[Exception]) - - expectResponseWithWipedDate( - """HTTP/1.1 400 Bad Request - |Server: akka-http/test - |Date: XXXX - |Connection: close - |Content-Type: text/plain; charset=UTF-8 - |Content-Length: 36 - | - |Illegal character 'g' in chunk start""") - } - - netOut.expectComplete() - netIn.sendComplete() - }) - - "deliver the request entity as it comes in strictly for an immediately completed Strict entity" in assertAllStagesStopped(new TestSetup { - send("""POST /strict HTTP/1.1 - |Host: example.com - |Content-Length: 12 - | - |abcdefghijkl""") - - expectRequest() mapHeaders (_.filterNot(_.is("timeout-access"))) shouldEqual - HttpRequest( - method = POST, - uri = "http://example.com/strict", - headers = List(Host("example.com")), - entity = HttpEntity.Strict(ContentTypes.`application/octet-stream`, ByteString("abcdefghijkl"))) - - shutdownBlueprint() - }) - - "deliver the request entity as it comes in for a Default entity" in assertAllStagesStopped(new TestSetup { - send("""POST / HTTP/1.1 - |Host: example.com - |Content-Length: 12 - | - |abcdef""") - - inside(expectRequest()) { - case HttpRequest(POST, _, _, HttpEntity.Default(_, 12, data), _) ⇒ - val dataProbe = TestSubscriber.manualProbe[ByteString] - data.to(Sink.fromSubscriber(dataProbe)).run() - val sub = dataProbe.expectSubscription() - sub.request(10) - dataProbe.expectNext(ByteString("abcdef")) - - send("ghijk") - dataProbe.expectNext(ByteString("ghijk")) - dataProbe.expectNoMsg(50.millis) - } - - shutdownBlueprint() - }) - - "deliver the request entity as it comes in for a chunked entity" in assertAllStagesStopped(new TestSetup { - send("""POST / HTTP/1.1 - |Host: example.com - |Transfer-Encoding: chunked - | - |6 - |abcdef - |""") - - inside(expectRequest()) { - case HttpRequest(POST, _, _, HttpEntity.Chunked(_, data), _) ⇒ - val dataProbe = TestSubscriber.manualProbe[ChunkStreamPart] - data.to(Sink.fromSubscriber(dataProbe)).run() - val sub = dataProbe.expectSubscription() - sub.request(10) - dataProbe.expectNext(Chunk(ByteString("abcdef"))) - - send("3\r\nghi\r\n") - dataProbe.expectNext(Chunk(ByteString("ghi"))) - dataProbe.expectNoMsg(50.millis) - } - shutdownBlueprint() - }) - - "deliver the second message properly after a Strict entity" in assertAllStagesStopped(new TestSetup { - send("""POST /strict HTTP/1.1 - |Host: example.com - |Content-Length: 12 - | - |abcdefghijkl""") - - expectRequest() mapHeaders (_.filterNot(_.is("timeout-access"))) shouldEqual - HttpRequest( - method = POST, - uri = "http://example.com/strict", - headers = List(Host("example.com")), - entity = HttpEntity.Strict(ContentTypes.`application/octet-stream`, ByteString("abcdefghijkl"))) - - send("""POST /next-strict HTTP/1.1 - |Host: example.com - |Content-Length: 12 - | - |mnopqrstuvwx""") - - expectRequest() mapHeaders (_.filterNot(_.is("timeout-access"))) shouldEqual - HttpRequest( - method = POST, - uri = "http://example.com/next-strict", - headers = List(Host("example.com")), - entity = HttpEntity.Strict(ContentTypes.`application/octet-stream`, ByteString("mnopqrstuvwx"))) - shutdownBlueprint() - }) - - "deliver the second message properly after a Default entity" in assertAllStagesStopped(new TestSetup { - send("""POST / HTTP/1.1 - |Host: example.com - |Content-Length: 12 - | - |abcdef""") - - inside(expectRequest()) { - case HttpRequest(POST, _, _, HttpEntity.Default(_, 12, data), _) ⇒ - val dataProbe = TestSubscriber.manualProbe[ByteString] - data.to(Sink.fromSubscriber(dataProbe)).run() - val sub = dataProbe.expectSubscription() - sub.request(10) - dataProbe.expectNext(ByteString("abcdef")) - - send("ghij") - dataProbe.expectNext(ByteString("ghij")) - - send("kl") - dataProbe.expectNext(ByteString("kl")) - dataProbe.expectComplete() - } - - send("""POST /next-strict HTTP/1.1 - |Host: example.com - |Content-Length: 5 - | - |abcde""") - - inside(expectRequest()) { - case HttpRequest(POST, _, _, HttpEntity.Strict(_, data), _) ⇒ - data shouldEqual ByteString("abcde") - } - shutdownBlueprint() - }) - - "deliver the second message properly after a Chunked entity" in assertAllStagesStopped(new TestSetup { - send("""POST /chunked HTTP/1.1 - |Host: example.com - |Transfer-Encoding: chunked - | - |6 - |abcdef - |""") - - inside(expectRequest()) { - case HttpRequest(POST, _, _, HttpEntity.Chunked(_, data), _) ⇒ - val dataProbe = TestSubscriber.manualProbe[ChunkStreamPart] - data.to(Sink.fromSubscriber(dataProbe)).run() - val sub = dataProbe.expectSubscription() - sub.request(10) - dataProbe.expectNext(Chunk(ByteString("abcdef"))) - - send("3\r\nghi\r\n") - dataProbe.expectNext(ByteString("ghi")) - dataProbe.expectNoMsg(50.millis) - - send("0\r\n\r\n") - dataProbe.expectNext(LastChunk) - dataProbe.expectComplete() - } - - send("""POST /next-strict HTTP/1.1 - |Host: example.com - |Content-Length: 5 - | - |abcde""") - - inside(expectRequest()) { - case HttpRequest(POST, _, _, HttpEntity.Strict(_, data), _) ⇒ - data shouldEqual ByteString("abcde") - } - shutdownBlueprint() - }) - - "close the request entity stream when the entity is complete for a Default entity" in assertAllStagesStopped(new TestSetup { - send("""POST / HTTP/1.1 - |Host: example.com - |Content-Length: 12 - | - |abcdef""") - - inside(expectRequest()) { - case HttpRequest(POST, _, _, HttpEntity.Default(_, 12, data), _) ⇒ - val dataProbe = TestSubscriber.manualProbe[ByteString] - data.to(Sink.fromSubscriber(dataProbe)).run() - val sub = dataProbe.expectSubscription() - sub.request(10) - dataProbe.expectNext(ByteString("abcdef")) - - send("ghijkl") - dataProbe.expectNext(ByteString("ghijkl")) - dataProbe.expectComplete() - } - shutdownBlueprint() - }) - - "close the request entity stream when the entity is complete for a Chunked entity" in assertAllStagesStopped(new TestSetup { - send("""POST / HTTP/1.1 - |Host: example.com - |Transfer-Encoding: chunked - | - |6 - |abcdef - |""") - - inside(expectRequest()) { - case HttpRequest(POST, _, _, HttpEntity.Chunked(_, data), _) ⇒ - val dataProbe = TestSubscriber.manualProbe[ChunkStreamPart] - data.to(Sink.fromSubscriber(dataProbe)).run() - val sub = dataProbe.expectSubscription() - sub.request(10) - dataProbe.expectNext(Chunk(ByteString("abcdef"))) - dataProbe.expectNoMsg(50.millis) - - send("0\r\n\r\n") - dataProbe.expectNext(LastChunk) - dataProbe.expectComplete() - } - shutdownBlueprint() - }) - - "close the connection if request entity stream has been cancelled" in assertAllStagesStopped(new TestSetup { - // two chunks sent by client - send("""POST / HTTP/1.1 - |Host: example.com - |Transfer-Encoding: chunked - | - |6 - |abcdef - |6 - |abcdef - |0 - | - |""") - - inside(expectRequest()) { - case HttpRequest(POST, _, _, HttpEntity.Chunked(_, data), _) ⇒ - val dataProbe = TestSubscriber.manualProbe[ChunkStreamPart] - // but only one consumed by server - data.take(1).to(Sink.fromSubscriber(dataProbe)).run() - val sub = dataProbe.expectSubscription() - sub.request(1) - dataProbe.expectNext(Chunk(ByteString("abcdef"))) - dataProbe.expectComplete() - // connection closes once requested elements are consumed - netIn.expectCancellation() - } - shutdownBlueprint() - }) - - "proceed to next request once previous request's entity has been drained" in assertAllStagesStopped(new TestSetup { - def twice(action: ⇒ Unit): Unit = { action; action } - - twice { - send("""POST / HTTP/1.1 - |Host: example.com - |Transfer-Encoding: chunked - | - |6 - |abcdef - |0 - | - |""") - - val whenComplete = expectRequest().entity.dataBytes.runWith(Sink.ignore) - whenComplete.futureValue should be(akka.Done) - } - shutdownBlueprint() - }) - - "report a truncated entity stream on the entity data stream and the main stream for a Default entity" in assertAllStagesStopped(new TestSetup { - send("""POST / HTTP/1.1 - |Host: example.com - |Content-Length: 12 - | - |abcdef""") - inside(expectRequest()) { - case HttpRequest(POST, _, _, HttpEntity.Default(_, 12, data), _) ⇒ - val dataProbe = TestSubscriber.manualProbe[ByteString] - data.to(Sink.fromSubscriber(dataProbe)).run() - val sub = dataProbe.expectSubscription() - sub.request(10) - dataProbe.expectNext(ByteString("abcdef")) - dataProbe.expectNoMsg(50.millis) - closeNetworkInput() - dataProbe.expectError().getMessage shouldEqual "Entity stream truncation" - } - shutdownBlueprint() - }) - - "report a truncated entity stream on the entity data stream and the main stream for a Chunked entity" in assertAllStagesStopped(new TestSetup { - send("""POST / HTTP/1.1 - |Host: example.com - |Transfer-Encoding: chunked - | - |6 - |abcdef - |""") - inside(expectRequest()) { - case HttpRequest(POST, _, _, HttpEntity.Chunked(_, data), _) ⇒ - val dataProbe = TestSubscriber.manualProbe[ChunkStreamPart] - data.to(Sink.fromSubscriber(dataProbe)).run() - val sub = dataProbe.expectSubscription() - sub.request(10) - dataProbe.expectNext(Chunk(ByteString("abcdef"))) - dataProbe.expectNoMsg(50.millis) - closeNetworkInput() - dataProbe.expectError().getMessage shouldEqual "Entity stream truncation" - } - shutdownBlueprint() - }) - - "translate HEAD request to GET request when transparent-head-requests are enabled" in assertAllStagesStopped(new TestSetup { - override def settings = ServerSettings(system).withTransparentHeadRequests(true) - send("""HEAD / HTTP/1.1 - |Host: example.com - | - |""") - expectRequest() mapHeaders (_.filterNot(_.is("timeout-access"))) shouldEqual HttpRequest(GET, uri = "http://example.com/", headers = List(Host("example.com"))) - shutdownBlueprint() - }) - - "keep HEAD request when transparent-head-requests are disabled" in assertAllStagesStopped(new TestSetup { - override def settings = ServerSettings(system).withTransparentHeadRequests(false) - send("""HEAD / HTTP/1.1 - |Host: example.com - | - |""") - expectRequest() mapHeaders (_.filterNot(_.is("timeout-access"))) shouldEqual HttpRequest(HEAD, uri = "http://example.com/", headers = List(Host("example.com"))) - shutdownBlueprint() - }) - - "not emit entities when responding to HEAD requests if transparent-head-requests is enabled (with Strict)" in assertAllStagesStopped(new TestSetup { - send("""HEAD / HTTP/1.1 - |Host: example.com - | - |""") - inside(expectRequest()) { - case HttpRequest(GET, _, _, _, _) ⇒ - responses.sendNext(HttpResponse(entity = HttpEntity.Strict(ContentTypes.`text/plain(UTF-8)`, ByteString("abcd")))) - expectResponseWithWipedDate( - """|HTTP/1.1 200 OK - |Server: akka-http/test - |Date: XXXX - |Content-Type: text/plain; charset=UTF-8 - |Content-Length: 4 - | - |""") - } - - netIn.sendComplete() - netOut.expectComplete() - }) - - "not emit entities when responding to HEAD requests if transparent-head-requests is enabled (with Default)" in assertAllStagesStopped(new TestSetup { - send("""HEAD / HTTP/1.1 - |Host: example.com - | - |""") - val data = TestPublisher.manualProbe[ByteString]() - inside(expectRequest()) { - case HttpRequest(GET, _, _, _, _) ⇒ - responses.sendNext(HttpResponse(entity = HttpEntity.Default(ContentTypes.`text/plain(UTF-8)`, 4, Source.fromPublisher(data)))) - val dataSub = data.expectSubscription() - dataSub.expectCancellation() - expectResponseWithWipedDate( - """|HTTP/1.1 200 OK - |Server: akka-http/test - |Date: XXXX - |Content-Type: text/plain; charset=UTF-8 - |Content-Length: 4 - | - |""") - } - - netIn.sendComplete() - netOut.expectComplete() - }) - - "not emit entities when responding to HEAD requests if transparent-head-requests is enabled (with CloseDelimited)" in assertAllStagesStopped(new TestSetup { - send("""HEAD / HTTP/1.1 - |Host: example.com - | - |""") - val data = TestPublisher.manualProbe[ByteString]() - inside(expectRequest()) { - case HttpRequest(GET, _, _, _, _) ⇒ - responses.sendNext(HttpResponse(entity = HttpEntity.CloseDelimited(ContentTypes.`text/plain(UTF-8)`, Source.fromPublisher(data)))) - val dataSub = data.expectSubscription() - dataSub.expectCancellation() - expectResponseWithWipedDate( - """|HTTP/1.1 200 OK - |Server: akka-http/test - |Date: XXXX - |Content-Type: text/plain; charset=UTF-8 - | - |""") - } - // No close should happen here since this was a HEAD request - netOut.expectNoBytes(50.millis) - - netIn.sendComplete() - netOut.expectComplete() - }) - - "not emit entities when responding to HEAD requests if transparent-head-requests is enabled (with Chunked)" in assertAllStagesStopped(new TestSetup { - send("""HEAD / HTTP/1.1 - |Host: example.com - | - |""") - val data = TestPublisher.manualProbe[ChunkStreamPart]() - inside(expectRequest()) { - case HttpRequest(GET, _, _, _, _) ⇒ - responses.sendNext(HttpResponse(entity = HttpEntity.Chunked(ContentTypes.`text/plain(UTF-8)`, Source.fromPublisher(data)))) - val dataSub = data.expectSubscription() - dataSub.expectCancellation() - expectResponseWithWipedDate( - """|HTTP/1.1 200 OK - |Server: akka-http/test - |Date: XXXX - |Transfer-Encoding: chunked - |Content-Type: text/plain; charset=UTF-8 - | - |""") - } - - netIn.sendComplete() - netOut.expectComplete() - }) - - "respect Connection headers of HEAD requests if transparent-head-requests is enabled" in assertAllStagesStopped(new TestSetup { - send("""HEAD / HTTP/1.1 - |Host: example.com - |Connection: close - | - |""") - val data = TestPublisher.manualProbe[ByteString]() - inside(expectRequest()) { - case HttpRequest(GET, _, _, _, _) ⇒ - responses.sendNext(HttpResponse(entity = CloseDelimited(ContentTypes.`text/plain(UTF-8)`, Source.fromPublisher(data)))) - val dataSub = data.expectSubscription() - dataSub.expectCancellation() - netOut.expectBytes(1) - } - netOut.expectComplete() - - netIn.sendComplete() - }) - - "produce a `100 Continue` response when requested by a `Default` entity" in assertAllStagesStopped(new TestSetup { - send("""POST / HTTP/1.1 - |Host: example.com - |Expect: 100-continue - |Content-Length: 16 - | - |""") - inside(expectRequest()) { - case HttpRequest(POST, _, _, Default(ContentType(`application/octet-stream`, None), 16, data), _) ⇒ - val dataProbe = TestSubscriber.manualProbe[ByteString] - data.to(Sink.fromSubscriber(dataProbe)).run() - val dataSub = dataProbe.expectSubscription() - netOut.expectNoBytes(50.millis) - dataSub.request(1) // triggers `100 Continue` response - expectResponseWithWipedDate( - """HTTP/1.1 100 Continue - |Server: akka-http/test - |Date: XXXX - | - |""") - dataProbe.expectNoMsg(50.millis) - send("0123456789ABCDEF") - dataProbe.expectNext(ByteString("0123456789ABCDEF")) - dataSub.request(1) - dataProbe.expectComplete() - responses.sendNext(HttpResponse(entity = "Yeah")) - expectResponseWithWipedDate( - """HTTP/1.1 200 OK - |Server: akka-http/test - |Date: XXXX - |Content-Type: text/plain; charset=UTF-8 - |Content-Length: 4 - | - |Yeah""") - } - - netIn.sendComplete() - netOut.expectComplete() - }) - - "produce a `100 Continue` response when requested by a `Chunked` entity" in assertAllStagesStopped(new TestSetup { - send("""POST / HTTP/1.1 - |Host: example.com - |Expect: 100-continue - |Transfer-Encoding: chunked - | - |""") - inside(expectRequest()) { - case HttpRequest(POST, _, _, Chunked(ContentType(`application/octet-stream`, None), data), _) ⇒ - val dataProbe = TestSubscriber.manualProbe[ChunkStreamPart] - data.to(Sink.fromSubscriber(dataProbe)).run() - val dataSub = dataProbe.expectSubscription() - netOut.expectNoBytes(50.millis) - dataSub.request(2) // triggers `100 Continue` response - expectResponseWithWipedDate( - """HTTP/1.1 100 Continue - |Server: akka-http/test - |Date: XXXX - | - |""") - dataProbe.expectNoMsg(50.millis) - send("""10 - |0123456789ABCDEF - |0 - | - |""") - dataProbe.expectNext(Chunk(ByteString("0123456789ABCDEF"))) - dataProbe.expectNext(LastChunk) - dataSub.request(1) - dataProbe.expectComplete() - responses.sendNext(HttpResponse(entity = "Yeah")) - expectResponseWithWipedDate( - """HTTP/1.1 200 OK - |Server: akka-http/test - |Date: XXXX - |Content-Type: text/plain; charset=UTF-8 - |Content-Length: 4 - | - |Yeah""") - } - - netIn.sendComplete() - netOut.expectComplete() - }) - - "render a closing response instead of `100 Continue` if request entity is not requested" in assertAllStagesStopped(new TestSetup { - send( - """POST / HTTP/1.1 - |Host: example.com - |Expect: 100-continue - |Content-Length: 16 - | - |""") - inside(expectRequest()) { - case HttpRequest(POST, _, _, Default(ContentType(`application/octet-stream`, None), 16, data), _) ⇒ - responses.sendNext(HttpResponse(entity = "Yeah")) - expectResponseWithWipedDate( - """HTTP/1.1 200 OK - |Server: akka-http/test - |Date: XXXX - |Connection: close - |Content-Type: text/plain; charset=UTF-8 - |Content-Length: 4 - | - |Yeah""") - } - - // client then closes the connection - netIn.sendComplete() - requests.expectComplete() - netOut.expectComplete() - }) - - "render a 500 response on response stream errors from the application" in assertAllStagesStopped(new TestSetup { - send("""GET / HTTP/1.1 - |Host: example.com - | - |""".stripMarginWithNewline("\r\n")) - - expectRequest() mapHeaders (_.filterNot(_.is("timeout-access"))) shouldEqual HttpRequest(uri = "http://example.com/", headers = List(Host("example.com"))) - - responses.expectRequest() - responses.sendError(new RuntimeException("CRASH BOOM BANG")) - - expectResponseWithWipedDate( - """HTTP/1.1 500 Internal Server Error - |Server: akka-http/test - |Date: XXXX - |Connection: close - |Content-Length: 0 - | - |""") - - netIn.sendComplete() - netOut.expectComplete() - }) - - "correctly consume and render large requests and responses" in assertAllStagesStopped(new TestSetup { - send("""POST / HTTP/1.1 - |Host: example.com - |Content-Length: 100000 - | - |""") - - val HttpRequest(POST, _, _, entity, _) = expectRequest() - responses.sendNext(HttpResponse(entity = entity)) - - expectResponseWithWipedDate( - """HTTP/1.1 200 OK - |Server: akka-http/test - |Date: XXXX - |Connection: close - |Content-Type: application/octet-stream - |Content-Length: 100000 - | - |""") - - val random = new Random() - @tailrec def rec(bytesLeft: Int): Unit = - if (bytesLeft > 0) { - val count = math.min(random.nextInt(1000) + 1, bytesLeft) - val data = random.alphanumeric.take(count).mkString - send(data) - netOut.expectUtf8EncodedString(data) - rec(bytesLeft - count) - } - rec(100000) - - netIn.sendComplete() - responses.sendComplete() - requests.request(1) - requests.expectComplete() - netOut.expectComplete() - }) - - "deliver a request with a non-RFC3986 request-target" in assertAllStagesStopped(new TestSetup { - send("""GET //foo HTTP/1.1 - |Host: example.com - | - |""") - - expectRequest() mapHeaders (_.filterNot(_.is("timeout-access"))) shouldEqual HttpRequest(uri = "http://example.com//foo", headers = List(Host("example.com"))) - shutdownBlueprint() - }) - - "use default-host-header for HTTP/1.0 requests" in assertAllStagesStopped(new TestSetup { - send("""GET /abc HTTP/1.0 - | - |""") - - expectRequest() mapHeaders (_.filterNot(_.is("timeout-access"))) shouldEqual HttpRequest(uri = "http://example.com/abc", protocol = HttpProtocols.`HTTP/1.0`) - - override def settings: ServerSettings = super.settings.withDefaultHostHeader(Host("example.com")) - - shutdownBlueprint() - }) - - "fail an HTTP/1.0 request with 400 if no default-host-header is set" in assertAllStagesStopped(new TestSetup { - send("""GET /abc HTTP/1.0 - | - |""") - - requests.request(1) - - expectResponseWithWipedDate( - """|HTTP/1.1 400 Bad Request - |Server: akka-http/test - |Date: XXXX - |Connection: close - |Content-Type: text/plain; charset=UTF-8 - |Content-Length: 41 - | - |Request is missing required `Host` header""") - - netIn.sendComplete() - netOut.expectComplete() - }) - - "support remote-address-header" in assertAllStagesStopped(new TestSetup { - lazy val theAddress = InetAddress.getByName("127.5.2.1") - - override def remoteAddress: Option[InetSocketAddress] = - Some(new InetSocketAddress(theAddress, 8080)) - - override def settings: ServerSettings = - super.settings.withRemoteAddressHeader(true) - - send("""GET / HTTP/1.1 - |Host: example.com - | - |""".stripMarginWithNewline("\r\n")) - - val request = expectRequest() - request.headers should contain(`Remote-Address`(RemoteAddress(theAddress, Some(8080)))) - - shutdownBlueprint() - }) - - "support remote-address-header when blueprint not constructed with it" in assertAllStagesStopped(new TestSetup { - // coverage for #21130 - lazy val theAddress = InetAddress.getByName("127.5.2.1") - - override def settings: ServerSettings = - super.settings.withRemoteAddressHeader(true) - - // this is the normal behavior for bindAndHandle(flow), it will set an attribute - // with remote ip before flow is materialized, rather than from the blueprint apply method - override def modifyServer(server: ServerLayer): ServerLayer = { - BidiFlow.fromGraph(Fusing.aggressive(server).withAttributes( - HttpAttributes.remoteAddress(Some(new InetSocketAddress(theAddress, 8080))) - )) - } - - send("""GET / HTTP/1.1 - |Host: example.com - | - |""".stripMarginWithNewline("\r\n")) - - val request = expectRequest() - request.headers should contain(`Remote-Address`(RemoteAddress(theAddress, Some(8080)))) - - shutdownBlueprint() - }) - - "support request timeouts" which { - - "are defined via the config" in assertAllStagesStopped(new RequestTimeoutTestSetup(10.millis) { - send("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n") - expectRequest().header[`Timeout-Access`] shouldBe defined - expectResponseWithWipedDate( - """HTTP/1.1 503 Service Unavailable - |Server: akka-http/test - |Date: XXXX - |Content-Type: text/plain; charset=UTF-8 - |Content-Length: 105 - | - |The server was not able to produce a timely response to your request. - |Please try again in a short while!""") - - netIn.sendComplete() - netOut.expectComplete() - }) - - "are programmatically increased (not expiring)" in assertAllStagesStopped(new RequestTimeoutTestSetup(50.millis) { - send("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n") - expectRequest().header[`Timeout-Access`].foreach(_.timeoutAccess.updateTimeout(250.millis)) - netOut.expectNoBytes(150.millis) - responses.sendNext(HttpResponse()) - expectResponseWithWipedDate( - """HTTP/1.1 200 OK - |Server: akka-http/test - |Date: XXXX - |Content-Length: 0 - | - |""") - - netIn.sendComplete() - netOut.expectComplete() - }) - - "are programmatically increased (expiring)" in assertAllStagesStopped(new RequestTimeoutTestSetup(50.millis) { - send("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n") - expectRequest().header[`Timeout-Access`].foreach(_.timeoutAccess.updateTimeout(250.millis)) - netOut.expectNoBytes(150.millis) - expectResponseWithWipedDate( - """HTTP/1.1 503 Service Unavailable - |Server: akka-http/test - |Date: XXXX - |Content-Type: text/plain; charset=UTF-8 - |Content-Length: 105 - | - |The server was not able to produce a timely response to your request. - |Please try again in a short while!""") - - netIn.sendComplete() - netOut.expectComplete() - }) - - "are programmatically decreased" in assertAllStagesStopped(new RequestTimeoutTestSetup(250.millis) { - send("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n") - expectRequest().header[`Timeout-Access`].foreach(_.timeoutAccess.updateTimeout(50.millis)) - val mark = System.nanoTime() - expectResponseWithWipedDate( - """HTTP/1.1 503 Service Unavailable - |Server: akka-http/test - |Date: XXXX - |Content-Type: text/plain; charset=UTF-8 - |Content-Length: 105 - | - |The server was not able to produce a timely response to your request. - |Please try again in a short while!""") - (System.nanoTime() - mark) should be < (200 * 1000000L) - - netIn.sendComplete() - netOut.expectComplete() - }) - - "have a programmatically set timeout handler" in assertAllStagesStopped(new RequestTimeoutTestSetup(400.millis) { - send("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n") - val timeoutResponse = HttpResponse(StatusCodes.InternalServerError, entity = "OOPS!") - expectRequest().header[`Timeout-Access`].foreach(_.timeoutAccess.updateHandler(_ ⇒ timeoutResponse)) - expectResponseWithWipedDate( - """HTTP/1.1 500 Internal Server Error - |Server: akka-http/test - |Date: XXXX - |Content-Type: text/plain; charset=UTF-8 - |Content-Length: 5 - | - |OOPS!""") - - netIn.sendComplete() - netOut.expectComplete() - }) - } - - "add `Connection: close` to early responses" in assertAllStagesStopped(new TestSetup { - send("""POST / HTTP/1.1 - |Host: example.com - |Content-Length: 100000 - | - |""") - - val HttpRequest(POST, _, _, entity, _) = expectRequest() - responses.sendNext(HttpResponse(status = StatusCodes.InsufficientStorage)) - entity.dataBytes.runWith(Sink.ignore) - - expectResponseWithWipedDate( - """HTTP/1.1 507 Insufficient Storage - |Server: akka-http/test - |Date: XXXX - |Connection: close - |Content-Length: 0 - | - |""") - - netIn.sendComplete() - requests.expectComplete() - netOut.expectComplete() - }) - - "support request length verification" which afterWord("is defined via") { - - class LengthVerificationTest(maxContentLength: Int) extends TestSetup(maxContentLength) { - val entityBase = "0123456789ABCD" - def sendStrictRequestWithLength(bytes: Int) = - send(s"""POST /foo HTTP/1.1 - |Host: example.com - |Content-Length: $bytes - | - |${entityBase take bytes}""") - def sendDefaultRequestWithLength(bytes: Int) = { - send(s"""POST /foo HTTP/1.1 - |Host: example.com - |Content-Length: $bytes - | - |${entityBase take 3}""") - send(entityBase.slice(3, 7)) - send(entityBase.slice(7, bytes)) - } - def sendChunkedRequestWithLength(bytes: Int) = - send(s"""POST /foo HTTP/1.1 - |Host: example.com - |Transfer-Encoding: chunked - | - |3 - |${entityBase take 3} - |4 - |${entityBase.slice(3, 7)} - |${bytes - 7} - |${entityBase.slice(7, bytes)} - |0 - | - |""") - - implicit class XRequest(request: HttpRequest) { - def expectEntity[T <: HttpEntity: ClassTag](bytes: Int) = - inside(request) { - case HttpRequest(POST, _, _, entity: T, _) ⇒ - entity.toStrict(100.millis).awaitResult(100.millis).data.utf8String shouldEqual entityBase.take(bytes) - } - - def expectDefaultEntityWithSizeError(limit: Int, actualSize: Int) = - inside(request) { - case HttpRequest(POST, _, _, entity @ HttpEntity.Default(_, `actualSize`, _), _) ⇒ - val error = the[Exception] - .thrownBy(entity.dataBytes.runFold(ByteString.empty)(_ ++ _).awaitResult(100.millis)) - .getCause - error shouldEqual EntityStreamSizeException(limit, Some(actualSize)) - error.getMessage should include("exceeded content length limit") - - responses.expectRequest() - responses.sendError(error.asInstanceOf[Exception]) - - expectResponseWithWipedDate( - s"""HTTP/1.1 413 Request Entity Too Large - |Server: akka-http/test - |Date: XXXX - |Connection: close - |Content-Type: text/plain; charset=UTF-8 - |Content-Length: 75 - | - |Request Content-Length of $actualSize bytes exceeds the configured limit of $limit bytes""") - } - - def expectChunkedEntityWithSizeError(limit: Int) = - inside(request) { - case HttpRequest(POST, _, _, entity: HttpEntity.Chunked, _) ⇒ - val error = the[Exception] - .thrownBy(entity.dataBytes.runFold(ByteString.empty)(_ ++ _).awaitResult(100.millis)) - .getCause - error shouldEqual EntityStreamSizeException(limit, None) - error.getMessage should include("exceeded content length limit") - - responses.expectRequest() - responses.sendError(error.asInstanceOf[Exception]) - - expectResponseWithWipedDate( - s"""HTTP/1.1 413 Request Entity Too Large - |Server: akka-http/test - |Date: XXXX - |Connection: close - |Content-Type: text/plain; charset=UTF-8 - |Content-Length: 81 - | - |Aggregated data length of request entity exceeds the configured limit of $limit bytes""") - } - } - } - - "the config setting (strict entity)" in new LengthVerificationTest(maxContentLength = 10) { - sendStrictRequestWithLength(10) - expectRequest().expectEntity[HttpEntity.Strict](10) - - // entities that would be strict but have a Content-Length > the configured maximum are delivered - // as single element Default entities! - sendStrictRequestWithLength(11) - expectRequest().expectDefaultEntityWithSizeError(limit = 10, actualSize = 11) - } - - "the config setting (default entity)" in new LengthVerificationTest(maxContentLength = 10) { - sendDefaultRequestWithLength(10) - expectRequest().expectEntity[HttpEntity.Default](10) - - sendDefaultRequestWithLength(11) - expectRequest().expectDefaultEntityWithSizeError(limit = 10, actualSize = 11) - } - - "the config setting (chunked entity)" in new LengthVerificationTest(maxContentLength = 10) { - sendChunkedRequestWithLength(10) - expectRequest().expectEntity[HttpEntity.Chunked](10) - - sendChunkedRequestWithLength(11) - expectRequest().expectChunkedEntityWithSizeError(limit = 10) - } - - "a smaller programmatically-set limit (strict entity)" in new LengthVerificationTest(maxContentLength = 12) { - sendStrictRequestWithLength(10) - expectRequest().mapEntity(_ withSizeLimit 10).expectEntity[HttpEntity.Strict](10) - - // entities that would be strict but have a Content-Length > the configured maximum are delivered - // as single element Default entities! - sendStrictRequestWithLength(11) - expectRequest().mapEntity(_ withSizeLimit 10).expectDefaultEntityWithSizeError(limit = 10, actualSize = 11) - } - - "a smaller programmatically-set limit (default entity)" in new LengthVerificationTest(maxContentLength = 12) { - sendDefaultRequestWithLength(10) - expectRequest().mapEntity(_ withSizeLimit 10).expectEntity[HttpEntity.Default](10) - - sendDefaultRequestWithLength(11) - expectRequest().mapEntity(_ withSizeLimit 10).expectDefaultEntityWithSizeError(limit = 10, actualSize = 11) - } - - "a smaller programmatically-set limit (chunked entity)" in new LengthVerificationTest(maxContentLength = 12) { - sendChunkedRequestWithLength(10) - expectRequest().mapEntity(_ withSizeLimit 10).expectEntity[HttpEntity.Chunked](10) - - sendChunkedRequestWithLength(11) - expectRequest().mapEntity(_ withSizeLimit 10).expectChunkedEntityWithSizeError(limit = 10) - } - - "a larger programmatically-set limit (strict entity)" in new LengthVerificationTest(maxContentLength = 8) { - // entities that would be strict but have a Content-Length > the configured maximum are delivered - // as single element Default entities! - sendStrictRequestWithLength(10) - expectRequest().mapEntity(_ withSizeLimit 10).expectEntity[HttpEntity.Default](10) - - sendStrictRequestWithLength(11) - expectRequest().mapEntity(_ withSizeLimit 10).expectDefaultEntityWithSizeError(limit = 10, actualSize = 11) - } - - "a larger programmatically-set limit (default entity)" in new LengthVerificationTest(maxContentLength = 8) { - sendDefaultRequestWithLength(10) - expectRequest().mapEntity(_ withSizeLimit 10).expectEntity[HttpEntity.Default](10) - - sendDefaultRequestWithLength(11) - expectRequest().mapEntity(_ withSizeLimit 10).expectDefaultEntityWithSizeError(limit = 10, actualSize = 11) - } - - "a larger programmatically-set limit (chunked entity)" in new LengthVerificationTest(maxContentLength = 8) { - sendChunkedRequestWithLength(10) - expectRequest().mapEntity(_ withSizeLimit 10).expectEntity[HttpEntity.Chunked](10) - - sendChunkedRequestWithLength(11) - expectRequest().mapEntity(_ withSizeLimit 10).expectChunkedEntityWithSizeError(limit = 10) - } - - "the config setting applied before another attribute (default entity)" in new LengthVerificationTest(maxContentLength = 10) { - def nameDataSource(name: String): RequestEntity ⇒ RequestEntity = { - case x: HttpEntity.Default ⇒ x.copy(data = x.data named name) - case _ ⇒ ??? // prevent a compile-time warning - } - sendDefaultRequestWithLength(10) - expectRequest().mapEntity(nameDataSource("foo")).expectEntity[HttpEntity.Default](10) - - sendDefaultRequestWithLength(11) - expectRequest().mapEntity(nameDataSource("foo")).expectDefaultEntityWithSizeError(limit = 10, actualSize = 11) - } - } - } - class TestSetup(maxContentLength: Int = -1) extends HttpServerTestSetupBase { - implicit def system = spec.system - implicit def materializer = spec.materializer - - override def settings = { - val s = super.settings - if (maxContentLength < 0) s - else s.withParserSettings(s.parserSettings.withMaxContentLength(maxContentLength)) - } - } - class RequestTimeoutTestSetup(requestTimeout: Duration) extends TestSetup { - override def settings = { - val s = super.settings - s.withTimeouts(s.timeouts.withRequestTimeout(requestTimeout)) - } - } -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/server/HttpServerTestSetupBase.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/server/HttpServerTestSetupBase.scala deleted file mode 100644 index 0a01e3a281..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/server/HttpServerTestSetupBase.scala +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.server - -import java.net.InetSocketAddress - -import akka.http.impl.engine.ws.ByteStringSinkProbe -import akka.http.scaladsl.settings.ServerSettings -import akka.stream.TLSProtocol._ - -import scala.concurrent.duration.FiniteDuration -import akka.actor.ActorSystem -import akka.event.NoLogging -import akka.util.ByteString -import akka.stream._ -import akka.stream.scaladsl._ -import akka.stream.testkit.{ TestPublisher, TestSubscriber } -import akka.http.impl.util._ -import akka.http.scaladsl.Http -import akka.http.scaladsl.model.headers.{ ProductVersion, Server } -import akka.http.scaladsl.model.{ HttpRequest, HttpResponse } - -abstract class HttpServerTestSetupBase { - implicit def system: ActorSystem - implicit def materializer: Materializer - - val requests = TestSubscriber.probe[HttpRequest] - val responses = TestPublisher.probe[HttpResponse]() - - def settings = ServerSettings(system) - .withServerHeader(Some(Server(List(ProductVersion("akka-http", "test"))))) - def remoteAddress: Option[InetSocketAddress] = None - - // hook to modify server, for example add attributes - def modifyServer(server: Http.ServerLayer): Http.ServerLayer = server - - val (netIn, netOut) = { - val netIn = TestPublisher.probe[ByteString]() - val netOut = ByteStringSinkProbe() - - RunnableGraph.fromGraph(GraphDSL.create(modifyServer(HttpServerBluePrint(settings, remoteAddress = remoteAddress, log = NoLogging))) { implicit b ⇒ server ⇒ - import GraphDSL.Implicits._ - Source.fromPublisher(netIn) ~> Flow[ByteString].map(SessionBytes(null, _)) ~> server.in2 - server.out1 ~> Flow[SslTlsOutbound].collect { case SendBytes(x) ⇒ x }.buffer(1, OverflowStrategy.backpressure) ~> netOut.sink - server.out2 ~> Sink.fromSubscriber(requests) - Source.fromPublisher(responses) ~> server.in1 - ClosedShape - }).run() - - netIn → netOut - } - - def expectResponseWithWipedDate(expected: String): Unit = { - val trimmed = expected.stripMarginWithNewline("\r\n") - // XXXX = 4 bytes, ISO Date Time String = 29 bytes => need to request 25 bytes more than expected string - val expectedSize = ByteString(trimmed, "utf8").length + 25 - val received = wipeDate(netOut.expectBytes(expectedSize).utf8String) - assert(received == trimmed, s"Expected request '$trimmed' but got '$received'") - } - - def wipeDate(string: String) = - string.fastSplit('\n').map { - case s if s.startsWith("Date:") ⇒ "Date: XXXX\r" - case s ⇒ s - }.mkString("\n") - - def expectRequest(): HttpRequest = requests.requestNext() - def expectNoRequest(max: FiniteDuration): Unit = requests.expectNoMsg(max) - def expectSubscribe(): Unit = netOut.expectComplete() - def expectSubscribeAndNetworkClose(): Unit = netOut.expectSubscriptionAndComplete() - def expectNetworkClose(): Unit = netOut.expectComplete() - - def send(data: ByteString): Unit = netIn.sendNext(data) - def send(string: String): Unit = send(ByteString(string.stripMarginWithNewline("\r\n"), "UTF8")) - - def closeNetworkInput(): Unit = netIn.sendComplete() - - def shutdownBlueprint(): Unit = { - netIn.sendComplete() - requests.expectComplete() - - responses.sendComplete() - netOut.expectBytes(ByteString("HTT")) // ??? - netOut.expectComplete() - } -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/server/PrepareRequestsSpec.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/server/PrepareRequestsSpec.scala deleted file mode 100644 index 2ae524a0c4..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/server/PrepareRequestsSpec.scala +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (C) 2016 Lightbend Inc. - */ -package akka.http.impl.engine.server - -import akka.http.impl.engine.parsing.ParserOutput -import akka.http.impl.engine.parsing.ParserOutput.{ StrictEntityCreator, EntityStreamError, EntityChunk, StreamedEntityCreator } -import akka.http.impl.engine.server.HttpServerBluePrint.PrepareRequests -import akka.http.scaladsl.model._ -import akka.http.scaladsl.settings.ServerSettings -import akka.stream.{ Attributes, ActorMaterializer } -import akka.stream.scaladsl.{ Sink, Source, Flow } -import akka.stream.testkit.{ TestSubscriber, TestPublisher } -import akka.testkit.AkkaSpec -import akka.util.ByteString -import scala.concurrent.duration._ - -class PrepareRequestsSpec extends AkkaSpec { - - val chunkedStart = - ParserOutput.RequestStart( - HttpMethods.GET, - Uri("http://example.com/"), - HttpProtocols.`HTTP/1.1`, - List(), - StreamedEntityCreator[ParserOutput, RequestEntity] { entityChunks ⇒ - val chunks = entityChunks.collect { - case EntityChunk(chunk) ⇒ chunk - case EntityStreamError(info) ⇒ throw EntityStreamException(info) - } - HttpEntity.Chunked(ContentTypes.`application/octet-stream`, HttpEntity.limitableChunkSource(chunks)) - }, - expect100Continue = true, - closeRequested = false) - - val chunkPart = - ParserOutput.EntityChunk(HttpEntity.ChunkStreamPart(ByteString("abc"))) - - val chunkRequestComplete = - ParserOutput.MessageEnd - - val strictRequest = - ParserOutput.RequestStart( - HttpMethods.GET, - Uri("http://example.com/"), - HttpProtocols.`HTTP/1.1`, - List(), - StrictEntityCreator(HttpEntity.Strict(ContentTypes.`application/octet-stream`, ByteString("body"))), - true, - false) - - "The PrepareRequest stage" should { - - "not fail when there is demand from both streamed entity consumption and regular flow" in { - implicit val materializer = ActorMaterializer() - // covers bug #19623 where a reply before the streamed - // body has been consumed causes pull/push twice - val inProbe = TestPublisher.manualProbe[ParserOutput.RequestOutput]() - val upstreamProbe = TestSubscriber.manualProbe[HttpRequest]() - - val stage = Flow.fromGraph(new PrepareRequests(ServerSettings(system))) - - Source.fromPublisher(inProbe) - .via(stage) - .to(Sink.fromSubscriber(upstreamProbe)) - .withAttributes(Attributes.inputBuffer(1, 1)) - .run() - - val upstreamSub = upstreamProbe.expectSubscription() - val inSub = inProbe.expectSubscription() - - // let request with streamed entity through - upstreamSub.request(1) - inSub.expectRequest(1) - inSub.sendNext(chunkedStart) - - val request = upstreamProbe.expectNext() - - // and subscribe to it's streamed entity - val entityProbe = TestSubscriber.manualProbe[ByteString]() - request.entity.dataBytes.to(Sink.fromSubscriber(entityProbe)) - .withAttributes(Attributes.inputBuffer(1, 1)) - .run() - - val entitySub = entityProbe.expectSubscription() - - // the bug happens when both the client has signalled demand - // and the streamed entity has - upstreamSub.request(1) - entitySub.request(1) - - // then comes the next chunk from the actual request - inSub.expectRequest(1) - - // bug would fail stream here with exception - upstreamProbe.expectNoMsg(100.millis) - - inSub.sendNext(ParserOutput.EntityChunk(HttpEntity.ChunkStreamPart(ByteString("abc")))) - entityProbe.expectNext() - entitySub.request(1) - inSub.sendNext(ParserOutput.MessageEnd) - entityProbe.expectComplete() - - // the rest of the test covers the saved pull - // that should go downstream when the streamed entity - // has reached it's end - inSub.expectRequest(1) - inSub.sendNext(strictRequest) - - upstreamProbe.expectNext() - - } - - "not complete running entity stream when upstream cancels" in { - implicit val materializer = ActorMaterializer() - - val inProbe = TestPublisher.manualProbe[ParserOutput.RequestOutput]() - val upstreamProbe = TestSubscriber.manualProbe[HttpRequest]() - - val stage = Flow.fromGraph(new PrepareRequests(ServerSettings(system))) - - Source.fromPublisher(inProbe) - .via(stage) - .to(Sink.fromSubscriber(upstreamProbe)) - .withAttributes(Attributes.inputBuffer(1, 1)) - .run() - - val upstreamSub = upstreamProbe.expectSubscription() - val inSub = inProbe.expectSubscription() - - // let request with streamed entity through - upstreamSub.request(1) - inSub.expectRequest(1) - inSub.sendNext(chunkedStart) - - val request = upstreamProbe.expectNext() - - // and subscribe to it's streamed entity - val entityProbe = TestSubscriber.manualProbe[ByteString]() - request.entity.dataBytes.to(Sink.fromSubscriber(entityProbe)) - .withAttributes(Attributes.inputBuffer(1, 1)) - .run() - - val entitySub = entityProbe.expectSubscription() - - // user logic cancels flow - upstreamSub.cancel() - - // but incoming chunks should still end up in entity - entitySub.request(1) - inSub.expectRequest(1) - inSub.sendNext(chunkPart) - entityProbe.expectNext() - - entitySub.request(1) - inSub.expectRequest(1) - inSub.sendNext(chunkRequestComplete) - - // and then when entity is complete, the stage should complete - entityProbe.expectComplete() - - } - - "complete stage if chunked stream is completed without reaching end of chunks" in { - // a bit unsure about this, but to document the assumption - implicit val materializer = ActorMaterializer() - - val inProbe = TestPublisher.manualProbe[ParserOutput.RequestOutput]() - val upstreamProbe = TestSubscriber.manualProbe[HttpRequest]() - - val stage = Flow.fromGraph(new PrepareRequests(ServerSettings(system))) - - Source.fromPublisher(inProbe) - .via(stage) - .to(Sink.fromSubscriber(upstreamProbe)) - .withAttributes(Attributes.inputBuffer(1, 1)) - .run() - - val upstreamSub = upstreamProbe.expectSubscription() - val inSub = inProbe.expectSubscription() - - // let request with streamed entity through - upstreamSub.request(1) - inSub.expectRequest(1) - inSub.sendNext(chunkedStart) - - val request = upstreamProbe.expectNext() - - // and subscribe to it's streamed entity - val entityProbe = TestSubscriber.manualProbe[ByteString]() - request.entity.dataBytes.to(Sink.fromSubscriber(entityProbe)) - .withAttributes(Attributes.inputBuffer(1, 1)) - .run() - - val entitySub = entityProbe.expectSubscription() - - // incoming stream is completed, but we never got the chunk end - inSub.sendComplete() - - // assumption: should cause stage to complete - entityProbe.expectComplete() - upstreamProbe.expectComplete() - - } - - "cancel the stage when the entity stream is canceled" in { - implicit val materializer = ActorMaterializer() - - val inProbe = TestPublisher.manualProbe[ParserOutput.RequestOutput]() - val upstreamProbe = TestSubscriber.manualProbe[HttpRequest]() - - val stage = Flow.fromGraph(new PrepareRequests(ServerSettings(system))) - - Source.fromPublisher(inProbe) - .via(stage) - .to(Sink.fromSubscriber(upstreamProbe)) - .withAttributes(Attributes.inputBuffer(1, 1)) - .run() - - val upstreamSub = upstreamProbe.expectSubscription() - val inSub = inProbe.expectSubscription() - - // let request with streamed entity through - upstreamSub.request(1) - inSub.expectRequest(1) - inSub.sendNext(chunkedStart) - - val request = upstreamProbe.expectNext() - - // and subscribe to it's streamed entity - val entityProbe = TestSubscriber.manualProbe[ByteString]() - request.entity.dataBytes.to(Sink.fromSubscriber(entityProbe)) - .withAttributes(Attributes.inputBuffer(1, 1)) - .run() - - val entitySub = entityProbe.expectSubscription() - - // user logic cancels entity stream - entitySub.cancel() - - inSub.expectCancellation() - } - } - -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/ws/BitBuilder.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/ws/BitBuilder.scala deleted file mode 100644 index f92823e538..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/ws/BitBuilder.scala +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.ws - -import scala.annotation.tailrec -import scala.util.{ Try, Failure, Success } -import akka.util.ByteString -import akka.parboiled2 -import parboiled2._ - -object BitBuilder { - implicit class BitBuilderContext(val ctx: StringContext) { - def b(args: Any*): ByteString = { - val input = ctx.parts.mkString.replace("\r\n", "\n") - val parser = new BitSpecParser(input) - val bits = parser.parseBits() - bits.get.toByteString - } - } -} - -final case class Bits(elements: Seq[Bits.BitElement]) { - def toByteString: ByteString = { - import Bits._ - - val bits = elements.map(_.bits).sum - - require(bits % 8 == 0) - val data = new Array[Byte](bits / 8) - @tailrec def rec(byteIdx: Int, bitIdx: Int, remaining: Seq[Bits.BitElement]): Unit = - if (bitIdx >= 8) rec(byteIdx + 1, bitIdx - 8, remaining) - else remaining match { - case Zero +: rest ⇒ - // zero by default - rec(byteIdx, bitIdx + 1, rest) - case One +: rest ⇒ - data(byteIdx) = (data(byteIdx) | (1 << (7 - bitIdx))).toByte - rec(byteIdx, bitIdx + 1, rest) - case Multibit(bits, value) +: rest ⇒ - val numBits = math.min(8 - bitIdx, bits) - val remainingBits = bits - numBits - val highestNBits = value >> remainingBits - val lowestNBitMask = (~(0xff << numBits) & 0xff) - data(byteIdx) = (data(byteIdx) | (highestNBits & lowestNBitMask)).toByte - - if (remainingBits > 0) - rec(byteIdx + 1, 0, Multibit(remainingBits, value) +: rest) - else - rec(byteIdx, bitIdx + numBits, rest) - case Nil ⇒ - require(bitIdx == 0 && byteIdx == bits / 8) - } - rec(0, 0, elements) - - ByteString(data) // this could be ByteString1C - } -} -object Bits { - sealed trait BitElement { - def bits: Int - } - sealed abstract class SingleBit extends BitElement { - def bits: Int = 1 - } - case object Zero extends SingleBit - case object One extends SingleBit - case class Multibit(bits: Int, value: Long) extends BitElement -} - -class BitSpecParser(val input: ParserInput) extends parboiled2.Parser { - import Bits._ - def parseBits(): Try[Bits] = - bits.run() match { - case s: Success[Bits] ⇒ s - case Failure(e: ParseError) ⇒ Failure(new RuntimeException(formatError(e, new ErrorFormatter(showTraces = true)))) - case _ ⇒ throw new IllegalStateException() - } - - def bits: Rule1[Bits] = rule { zeroOrMore(element) ~ EOI ~> (Bits(_)) } - - val WSChar = CharPredicate(' ', '\t', '\n') - def ws = rule { zeroOrMore(wsElement) } - def wsElement = rule { WSChar | comment } - def comment = - rule { - '#' ~ zeroOrMore(!'\n' ~ ANY) ~ '\n' - } - - def element: Rule1[BitElement] = rule { - zero | one | multi - } - def zero: Rule1[BitElement] = rule { '0' ~ push(Zero) ~ ws } - def one: Rule1[BitElement] = rule { '1' ~ push(One) ~ ws } - def multi: Rule1[Multibit] = rule { - capture(oneOrMore('x' ~ ws)) ~> (_.count(_ == 'x')) ~ '=' ~ value ~ ws ~> Multibit - } - def value: Rule1[Long] = rule { - capture(oneOrMore(CharPredicate.HexDigit)) ~> ((str: String) ⇒ java.lang.Long.parseLong(str, 16)) - } -} \ No newline at end of file diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/ws/ByteStringSinkProbe.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/ws/ByteStringSinkProbe.scala deleted file mode 100644 index 3f9ab90fa1..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/ws/ByteStringSinkProbe.scala +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.ws - -import akka.NotUsed -import akka.actor.ActorSystem -import akka.stream.scaladsl.Sink -import akka.stream.testkit.TestSubscriber -import akka.util.ByteString - -import scala.annotation.tailrec -import scala.concurrent.duration.FiniteDuration - -trait ByteStringSinkProbe { - def sink: Sink[ByteString, NotUsed] - - def expectBytes(length: Int): ByteString - def expectBytes(expected: ByteString): Unit - - def expectUtf8EncodedString(string: String): Unit - - def expectNoBytes(): Unit - def expectNoBytes(timeout: FiniteDuration): Unit - - def expectSubscriptionAndComplete(): Unit - def expectComplete(): Unit - def expectError(): Throwable - def expectError(cause: Throwable): Unit - - def request(n: Long): Unit -} - -object ByteStringSinkProbe { - def apply()(implicit system: ActorSystem): ByteStringSinkProbe = - new ByteStringSinkProbe { - val probe = TestSubscriber.probe[ByteString]() - val sink: Sink[ByteString, NotUsed] = Sink.fromSubscriber(probe) - - def expectNoBytes(): Unit = { - probe.ensureSubscription() - probe.expectNoMsg() - } - def expectNoBytes(timeout: FiniteDuration): Unit = { - probe.ensureSubscription() - probe.expectNoMsg(timeout) - } - - var inBuffer = ByteString.empty - @tailrec def expectBytes(length: Int): ByteString = - if (inBuffer.size >= length) { - val res = inBuffer.take(length) - inBuffer = inBuffer.drop(length) - res - } else { - inBuffer ++= probe.requestNext() - expectBytes(length) - } - - def expectBytes(expected: ByteString): Unit = - assert(expectBytes(expected.length) == expected, "expected ") - - def expectUtf8EncodedString(string: String): Unit = - expectBytes(ByteString(string, "utf8")) - - def expectSubscriptionAndComplete(): Unit = probe.expectSubscriptionAndComplete() - def expectComplete(): Unit = probe.expectComplete() - def expectError(): Throwable = probe.expectError() - def expectError(cause: Throwable): Unit = probe.expectError(cause) - - def request(n: Long): Unit = probe.request(n) - } -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/ws/EchoTestClientApp.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/ws/EchoTestClientApp.scala deleted file mode 100644 index 404e864dde..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/ws/EchoTestClientApp.scala +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.ws - -import akka.NotUsed - -import scala.concurrent.duration._ - -import akka.actor.ActorSystem -import akka.http.scaladsl.Http -import akka.http.scaladsl.model.ws.{ TextMessage, BinaryMessage, Message } -import akka.stream.ActorMaterializer -import akka.stream.scaladsl._ -import akka.util.ByteString - -import scala.concurrent.Future -import scala.util.{ Failure, Success } - -/** - * An example App that runs a quick test against the websocket server at wss://echo.websocket.org - */ -object EchoTestClientApp extends App { - implicit val system = ActorSystem() - import system.dispatcher - implicit val materializer = ActorMaterializer() - - def delayedCompletion(delay: FiniteDuration): Source[Nothing, NotUsed] = - Source.single(1) - .mapAsync(1)(_ ⇒ akka.pattern.after(delay, system.scheduler)(Future(1))) - .drop(1).asInstanceOf[Source[Nothing, NotUsed]] - - def messages: List[Message] = - List( - TextMessage("Test 1"), - BinaryMessage(ByteString("abc")), - TextMessage("Test 2"), - BinaryMessage(ByteString("def"))) - - def source: Source[Message, NotUsed] = - Source(messages) ++ delayedCompletion(1.second) // otherwise, we may start closing too soon - - def sink: Sink[Message, Future[Seq[String]]] = - Flow[Message] - .mapAsync(1) { - case tm: TextMessage ⇒ - tm.textStream.runWith(Sink.fold("")(_ + _)).map(str ⇒ s"TextMessage: '$str'") - case bm: BinaryMessage ⇒ - bm.dataStream.runWith(Sink.fold(ByteString.empty)(_ ++ _)).map(bs ⇒ s"BinaryMessage: '${bs.utf8String}'") - } - .grouped(10000) - .toMat(Sink.head)(Keep.right) - - def echoClient = Flow.fromSinkAndSourceMat(sink, source)(Keep.left) - - val (upgrade, res) = Http().singleWebSocketRequest("wss://echo.websocket.org", echoClient) - res onComplete { - case Success(res) ⇒ - println("Run successful. Got these elements:") - res.foreach(println) - system.terminate() - case Failure(e) ⇒ - println("Run failed.") - e.printStackTrace() - system.terminate() - } - - system.scheduler.scheduleOnce(10.seconds)(system.terminate()) -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/ws/FramingSpec.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/ws/FramingSpec.scala deleted file mode 100644 index d2cdfa81c6..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/ws/FramingSpec.scala +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.ws - -import scala.collection.immutable -import scala.concurrent.duration._ -import org.scalatest.matchers.Matcher -import org.scalatest.{ FreeSpec, Matchers } -import akka.util.ByteString -import akka.stream.scaladsl.Source -import akka.stream.stage.Stage -import akka.http.impl.util._ - -import Protocol.Opcode - -class FramingSpec extends FreeSpec with Matchers with WithMaterializerSpec { - import BitBuilder._ - - "The WebSocket parser/renderer round-trip should work for" - { - "the frame header" - { - "interpret flags correctly" - { - "FIN" in { - b"""1000 # flags - 0000 # opcode - 0 # mask? - 0000000 # length - """ should parseTo(FrameHeader(Opcode.Continuation, None, 0, fin = true)) - } - "RSV1" in { - b"""0100 # flags - 0000 # opcode - 0 # mask? - 0000000 # length - """ should parseTo(FrameHeader(Opcode.Continuation, None, 0, fin = false, rsv1 = true)) - } - "RSV2" in { - b"""0010 # flags - 0000 # opcode - 0 # mask? - 0000000 # length - """ should parseTo(FrameHeader(Opcode.Continuation, None, 0, fin = false, rsv2 = true)) - } - "RSV3" in { - b"""0001 # flags - 0000 # opcode - 0 # mask? - 0000000 # length - """ should parseTo(FrameHeader(Opcode.Continuation, None, 0, fin = false, rsv3 = true)) - } - } - "interpret opcode correctly" - { - "Continuation" in { - b"""0000 # flags - xxxx=0 # opcode - 0 # mask? - 0000000 # length - """ should parseTo(FrameHeader(Opcode.Continuation, None, 0, fin = false)) - } - "Text" in { - b"""0000 # flags - xxxx=1 # opcode - 0 # mask? - 0000000 # length - """ should parseTo(FrameHeader(Opcode.Text, None, 0, fin = false)) - } - "Binary" in { - b"""0000 # flags - xxxx=2 # opcode - 0 # mask? - 0000000 # length - """ should parseTo(FrameHeader(Opcode.Binary, None, 0, fin = false)) - } - - "Close" in { - b"""0000 # flags - xxxx=8 # opcode - 0 # mask? - 0000000 # length - """ should parseTo(FrameHeader(Opcode.Close, None, 0, fin = false)) - } - "Ping" in { - b"""0000 # flags - xxxx=9 # opcode - 0 # mask? - 0000000 # length - """ should parseTo(FrameHeader(Opcode.Ping, None, 0, fin = false)) - } - "Pong" in { - b"""0000 # flags - xxxx=a # opcode - 0 # mask? - 0000000 # length - """ should parseTo(FrameHeader(Opcode.Pong, None, 0, fin = false)) - } - - "Other" in { - b"""0000 # flags - xxxx=6 # opcode - 0 # mask? - 0000000 # length - """ should parseTo(FrameHeader(Opcode.Other(6), None, 0, fin = false)) - } - } - "read mask correctly" in { - b"""0000 # flags - 0000 # opcode - 1 # mask? - 0000000 # length - xxxxxxxx - xxxxxxxx - xxxxxxxx - xxxxxxxx=a1b2c3d4 - """ should parseTo(FrameHeader(Opcode.Continuation, Some(0xa1b2c3d4), 0, fin = false)) - } - "read length" - { - "< 126" in { - b"""0000 # flags - xxxx=0 # opcode - 0 # mask? - xxxxxxx=5 # length - """ should parseTo(FrameHeader(Opcode.Continuation, None, 5, fin = false)) - } - "126" in { - b"""0000 # flags - xxxx=0 # opcode - 0 # mask? - xxxxxxx=7e # length - xxxxxxxx - xxxxxxxx=007e # length16 - """ should parseTo(FrameHeader(Opcode.Continuation, None, 126, fin = false)) - } - "127" in { - b"""0000 # flags - xxxx=0 # opcode - 0 # mask? - xxxxxxx=7e # length - xxxxxxxx - xxxxxxxx=007f # length16 - """ should parseTo(FrameHeader(Opcode.Continuation, None, 127, fin = false)) - } - "127 < length < 65536" in { - b"""0000 # flags - xxxx=0 # opcode - 0 # mask? - xxxxxxx=7e # length - xxxxxxxx - xxxxxxxx=d28e # length16 - """ should parseTo(FrameHeader(Opcode.Continuation, None, 0xd28e, fin = false)) - } - "65535" in { - b"""0000 # flags - xxxx=0 # opcode - 0 # mask? - xxxxxxx=7e # length - xxxxxxxx - xxxxxxxx=ffff # length16 - """ should parseTo(FrameHeader(Opcode.Continuation, None, 0xffff, fin = false)) - } - "65536" in { - b"""0000 # flags - xxxx=0 # opcode - 0 # mask? - xxxxxxx=7f # length - xxxxxxxx - xxxxxxxx - xxxxxxxx - xxxxxxxx - xxxxxxxx - xxxxxxxx - xxxxxxxx - xxxxxxxx=0000000000010000 # length64 - """ should parseTo(FrameHeader(Opcode.Continuation, None, 0x10000, fin = false)) - } - "> 65536" in { - b"""0000 # flags - xxxx=0 # opcode - 0 # mask? - xxxxxxx=7f # length - xxxxxxxx - xxxxxxxx - xxxxxxxx - xxxxxxxx - xxxxxxxx - xxxxxxxx - xxxxxxxx - xxxxxxxx=0000000123456789 # length64 - """ should parseTo(FrameHeader(Opcode.Continuation, None, 0x123456789L, fin = false)) - } - "Long.MaxValue" in { - b"""0000 # flags - xxxx=0 # opcode - 0 # mask? - xxxxxxx=7f # length - xxxxxxxx - xxxxxxxx - xxxxxxxx - xxxxxxxx - xxxxxxxx - xxxxxxxx - xxxxxxxx - xxxxxxxx=7fffffffffffffff # length64 - """ should parseTo(FrameHeader(Opcode.Continuation, None, Long.MaxValue, fin = false)) - } - } - } - - "a partial frame" in { - val header = - b"""0000 # flags - xxxx=1 # opcode - 0 # mask? - xxxxxxx=5 # length - """ - val data = ByteString("abc") - - (header ++ data) should parseTo( - FrameStart( - FrameHeader(Opcode.Text, None, 5, fin = false), - data)) - } - "a partial frame of total size > Int.MaxValue" in { - val header = - b"""0000 # flags - xxxx=0 # opcode - 0 # mask? - xxxxxxx=7f # length - xxxxxxxx - xxxxxxxx - xxxxxxxx - xxxxxxxx - xxxxxxxx - xxxxxxxx - xxxxxxxx - xxxxxxxx=00000000ffffffff # length64 - """ - val data = ByteString("abc", "ASCII") - - Seq(header, data) should parseMultipleTo( - FrameStart(FrameHeader(Opcode.Continuation, None, 0xFFFFFFFFL, fin = false), ByteString.empty), - FrameData(data, lastPart = false)) - } - "a full frame" in { - val header = - b"""0000 # flags - xxxx=0 # opcode - 0 # mask? - xxxxxxx=5 # length - """ - val data = ByteString("abcde") - - (header ++ data) should parseTo( - FrameStart(FrameHeader(Opcode.Continuation, None, 5, fin = false), data)) - } - "a full frame in chunks" in { - val header = - b"""0000 # flags - xxxx=1 # opcode - 0 # mask? - xxxxxxx=5 # length - """ - val data1 = ByteString("abc") - val data2 = ByteString("de") - - val expectedHeader = FrameHeader(Opcode.Text, None, 5, fin = false) - Seq(header, data1, data2) should parseMultipleTo( - FrameStart(expectedHeader, ByteString.empty), - FrameData(data1, lastPart = false), - FrameData(data2, lastPart = true)) - } - "several frames" in { - val header1 = - b"""0000 # flags - xxxx=0 # opcode - 0 # mask? - xxxxxxx=5 # length - """ - - val header2 = - b"""0000 # flags - xxxx=0 # opcode - 0 # mask? - xxxxxxx=7 # length - """ - - val data1 = ByteString("abcde") - val data2 = ByteString("abc") - - (header1 ++ data1 ++ header2 ++ data2) should parseTo( - FrameStart(FrameHeader(Opcode.Continuation, None, 5, fin = false), data1), - FrameStart(FrameHeader(Opcode.Continuation, None, 7, fin = false), data2)) - } - } - - private def parseTo(events: FrameEvent*): Matcher[ByteString] = - parseMultipleTo(events: _*).compose(Seq(_)) - - private def parseMultipleTo(events: FrameEvent*): Matcher[Seq[ByteString]] = - equal(events).matcher[Seq[FrameEvent]].compose { - (chunks: Seq[ByteString]) ⇒ - val result = parseToEvents(chunks) - result shouldEqual events - val rendered = renderToByteString(result) - rendered shouldEqual chunks.reduce(_ ++ _) - result - } - - private def parseToEvents(bytes: Seq[ByteString]): immutable.Seq[FrameEvent] = - Source(bytes.toVector).via(FrameEventParser).runFold(Vector.empty[FrameEvent])(_ :+ _) - .awaitResult(1.second) - private def renderToByteString(events: immutable.Seq[FrameEvent]): ByteString = - Source(events).transform(newRenderer).runFold(ByteString.empty)(_ ++ _) - .awaitResult(1.second) - - protected def newRenderer(): Stage[FrameEvent, ByteString] = new FrameEventRenderer - - import scala.language.implicitConversions - private implicit def headerToEvent(header: FrameHeader): FrameEvent = - FrameStart(header, ByteString.empty) -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/ws/MessageSpec.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/ws/MessageSpec.scala deleted file mode 100644 index 2b87ddfc18..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/ws/MessageSpec.scala +++ /dev/null @@ -1,980 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.ws - -import akka.NotUsed - -import scala.concurrent.duration._ -import scala.util.Random -import org.scalatest.{ Matchers, FreeSpec } -import akka.stream.scaladsl._ -import akka.stream.testkit._ -import akka.util.ByteString -import akka.http.scaladsl.model.ws._ -import Protocol.Opcode -import akka.testkit.EventFilter -import akka.stream.OverflowStrategy - -class MessageSpec extends FreeSpec with Matchers with WithMaterializerSpec { - import WSTestUtils._ - - val InvalidUtf8TwoByteSequence: ByteString = ByteString( - (128 + 64).toByte, // start two byte sequence - 0 // but don't finish it - ) - - "The WebSocket implementation should" - { - "collect messages from frames" - { - "for binary messages" - { - "for an empty message" in new ClientTestSetup { - val input = frameHeader(Opcode.Binary, 0, fin = true) - - pushInput(input) - expectMessage(BinaryMessage.Strict(ByteString.empty)) - } - "for one complete, strict, single frame message" in new ClientTestSetup { - val data = ByteString("abcdef", "ASCII") - val input = frameHeader(Opcode.Binary, 6, fin = true) ++ data - - pushInput(input) - expectMessage(BinaryMessage.Strict(data)) - } - "for a partial frame" in new ClientTestSetup { - val data1 = ByteString("abc", "ASCII") - val header = frameHeader(Opcode.Binary, 6, fin = true) - - pushInput(header ++ data1) - val dataSource = expectBinaryMessage().dataStream - val sub = TestSubscriber.manualProbe[ByteString]() - dataSource.runWith(Sink.fromSubscriber(sub)) - val s = sub.expectSubscription() - s.request(2) - sub.expectNext(data1) - } - "for a frame split up into parts" in new ClientTestSetup { - val data1 = ByteString("abc", "ASCII") - val header = frameHeader(Opcode.Binary, 6, fin = true) - - pushInput(header) - val dataSource = expectBinaryMessage().dataStream - val sub = TestSubscriber.manualProbe[ByteString]() - dataSource.runWith(Sink.fromSubscriber(sub)) - val s = sub.expectSubscription() - s.request(2) - pushInput(data1) - sub.expectNext(data1) - - val data2 = ByteString("def", "ASCII") - pushInput(data2) - sub.expectNext(data2) - s.request(1) - sub.expectComplete() - } - - "for a message split into several frames" in new ClientTestSetup { - val data1 = ByteString("abc", "ASCII") - val header1 = frameHeader(Opcode.Binary, 3, fin = false) - - pushInput(header1 ++ data1) - val dataSource = expectBinaryMessage().dataStream - val sub = TestSubscriber.manualProbe[ByteString]() - dataSource.runWith(Sink.fromSubscriber(sub)) - val s = sub.expectSubscription() - s.request(2) - sub.expectNext(data1) - - val header2 = frameHeader(Opcode.Continuation, 4, fin = true) - val data2 = ByteString("defg", "ASCII") - pushInput(header2 ++ data2) - sub.expectNext(data2) - s.request(1) - sub.expectComplete() - } - "for several messages" in new ClientTestSetup { - val data1 = ByteString("abc", "ASCII") - val header1 = frameHeader(Opcode.Binary, 3, fin = false) - - pushInput(header1 ++ data1) - val dataSource = expectBinaryMessage().dataStream - val sub = TestSubscriber.manualProbe[ByteString]() - dataSource.runWith(Sink.fromSubscriber(sub)) - val s = sub.expectSubscription() - s.request(2) - sub.expectNext(data1) - - val header2 = frameHeader(Opcode.Continuation, 4, fin = true) - val header3 = frameHeader(Opcode.Binary, 2, fin = true) - val data2 = ByteString("defg", "ASCII") - val data3 = ByteString("h") - pushInput(header2 ++ data2 ++ header3 ++ data3) - sub.expectNext(data2) - s.request(1) - sub.expectComplete() - - val dataSource2 = expectBinaryMessage().dataStream - val sub2 = TestSubscriber.manualProbe[ByteString]() - dataSource2.runWith(Sink.fromSubscriber(sub2)) - val s2 = sub2.expectSubscription() - s2.request(2) - sub2.expectNext(data3) - - val data4 = ByteString("i") - pushInput(data4) - sub2.expectNext(data4) - s2.request(1) - sub2.expectComplete() - } - "unmask masked input on the server side" in new ServerTestSetup { - val mask = Random.nextInt() - val (data, _) = maskedASCII("abcdef", mask) - val data1 = data.take(3) - val data2 = data.drop(3) - val header = frameHeader(Opcode.Binary, 6, fin = true, mask = Some(mask)) - - pushInput(header ++ data1) - val dataSource = expectBinaryMessage().dataStream - val sub = TestSubscriber.manualProbe[ByteString]() - dataSource.runWith(Sink.fromSubscriber(sub)) - val s = sub.expectSubscription() - s.request(2) - sub.expectNext(ByteString("abc", "ASCII")) - - pushInput(data2) - sub.expectNext(ByteString("def", "ASCII")) - s.request(1) - sub.expectComplete() - } - "unmask masked input on the server side for empty frame" in new ServerTestSetup { - val mask = Random.nextInt() - val header = frameHeader(Opcode.Binary, 0, fin = true, mask = Some(mask)) - - pushInput(header) - expectBinaryMessage(BinaryMessage.Strict(ByteString.empty)) - } - } - "for text messages" - { - "empty message" in new ClientTestSetup { - val input = frameHeader(Opcode.Text, 0, fin = true) - - pushInput(input) - expectMessage(TextMessage.Strict("")) - } - "decode complete, strict frame from utf8" in new ClientTestSetup { - val msg = "äbcdef€\uffff" - val data = ByteString(msg, "UTF-8") - val input = frameHeader(Opcode.Text, data.size, fin = true) ++ data - - pushInput(input) - expectMessage(TextMessage.Strict(msg)) - } - "decode utf8 as far as possible for partial frame" in new ClientTestSetup { - val msg = "bäcdef€" - val data = ByteString(msg, "UTF-8") - val data0 = data.slice(0, 2) - val data1 = data.slice(2, 5) - val data2 = data.slice(5, data.size) - val input = frameHeader(Opcode.Text, data.size, fin = true) ++ data0 - - pushInput(input) - val parts = expectTextMessage().textStream - val sub = TestSubscriber.manualProbe[String]() - parts.runWith(Sink.fromSubscriber(sub)) - val s = sub.expectSubscription() - s.request(4) - sub.expectNext("b") - - pushInput(data1) - sub.expectNext("äcd") - } - "decode utf8 with code point split across frames" in new ClientTestSetup { - val msg = "äbcdef€" - val data = ByteString(msg, "UTF-8") - val data0 = data.slice(0, 1) - val data1 = data.slice(1, data.size) - val header0 = frameHeader(Opcode.Text, data0.size, fin = false) - - pushInput(header0 ++ data0) - val parts = expectTextMessage().textStream - val sub = TestSubscriber.manualProbe[String]() - parts.runWith(Sink.fromSubscriber(sub)) - val s = sub.expectSubscription() - s.request(4) - sub.expectNoMsg(100.millis) - - val header1 = frameHeader(Opcode.Continuation, data1.size, fin = true) - pushInput(header1 ++ data1) - sub.expectNext("äbcdef€") - } - "unmask masked input on the server side" in new ServerTestSetup { - val mask = Random.nextInt() - val (data, _) = maskedUTF8("äbcdef€", mask) - val data1 = data.take(3) - val data2 = data.drop(3) - val header = frameHeader(Opcode.Binary, data.size, fin = true, mask = Some(mask)) - - pushInput(header ++ data1) - val dataSource = expectBinaryMessage().dataStream - val sub = TestSubscriber.manualProbe[ByteString]() - dataSource.runWith(Sink.fromSubscriber(sub)) - val s = sub.expectSubscription() - s.request(2) - sub.expectNext(ByteString("äb", "UTF-8")) - - pushInput(data2) - sub.expectNext(ByteString("cdef€", "UTF-8")) - s.request(1) - sub.expectComplete() - } - "unmask masked input on the server side for empty frame" in new ServerTestSetup { - val mask = Random.nextInt() - val header = frameHeader(Opcode.Text, 0, fin = true, mask = Some(mask)) - - pushInput(header) - expectTextMessage(TextMessage.Strict("")) - } - } - } - "render frames from messages" - { - "for binary messages" - { - "for a short strict message" in new ServerTestSetup { - val data = ByteString("abcdef", "ASCII") - val msg = BinaryMessage.Strict(data) - pushMessage(msg) - - expectFrameOnNetwork(Opcode.Binary, data, fin = true) - } - "for a strict message larger than configured maximum frame size" in pending - "for a streamed message" in new ServerTestSetup { - val data = ByteString("abcdefg", "ASCII") - val pub = TestPublisher.manualProbe[ByteString]() - val msg = BinaryMessage(Source.fromPublisher(pub)) - pushMessage(msg) - val sub = pub.expectSubscription() - - expectFrameHeaderOnNetwork(Opcode.Binary, 0, fin = false) - - val data1 = data.take(3) - val data2 = data.drop(3) - - sub.sendNext(data1) - expectFrameOnNetwork(Opcode.Continuation, data1, fin = false) - - sub.sendNext(data2) - expectFrameOnNetwork(Opcode.Continuation, data2, fin = false) - - sub.sendComplete() - expectFrameOnNetwork(Opcode.Continuation, ByteString.empty, fin = true) - } - "for a streamed message with a chunk being larger than configured maximum frame size" in pending - "and mask input on the client side" in new ClientTestSetup { - val data = ByteString("abcdefg", "ASCII") - val pub = TestPublisher.manualProbe[ByteString]() - val msg = BinaryMessage(Source.fromPublisher(pub)) - pushMessage(msg) - val sub = pub.expectSubscription() - - expectFrameHeaderOnNetwork(Opcode.Binary, 0, fin = false) - - val data1 = data.take(3) - val data2 = data.drop(3) - - sub.sendNext(data1) - expectMaskedFrameOnNetwork(Opcode.Continuation, data1, fin = false) - - sub.sendNext(data2) - expectMaskedFrameOnNetwork(Opcode.Continuation, data2, fin = false) - - sub.sendComplete() - expectFrameOnNetwork(Opcode.Continuation, ByteString.empty, fin = true) - } - "and mask input on the client side for empty frame" in new ClientTestSetup { - pushMessage(BinaryMessage(ByteString.empty)) - expectMaskedFrameOnNetwork(Opcode.Binary, ByteString.empty, fin = true) - } - } - "for text messages" - { - "for a short strict message" in new ServerTestSetup { - val text = "äbcdef" - val msg = TextMessage.Strict(text) - pushMessage(msg) - - expectFrameOnNetwork(Opcode.Text, ByteString(text, "UTF-8"), fin = true) - } - "for a strict message larger than configured maximum frame size" in pending - "for a streamed message" in new ServerTestSetup { - val text = "äbcd€fg" - val pub = TestPublisher.manualProbe[String]() - val msg = TextMessage(Source.fromPublisher(pub)) - pushMessage(msg) - val sub = pub.expectSubscription() - - expectFrameHeaderOnNetwork(Opcode.Text, 0, fin = false) - - val text1 = text.take(3) - val text1Bytes = ByteString(text1, "UTF-8") - val text2 = text.drop(3) - val text2Bytes = ByteString(text2, "UTF-8") - - sub.sendNext(text1) - expectFrameOnNetwork(Opcode.Continuation, text1Bytes, fin = false) - - sub.sendNext(text2) - expectFrameOnNetwork(Opcode.Continuation, text2Bytes, fin = false) - - sub.sendComplete() - expectFrameOnNetwork(Opcode.Continuation, ByteString.empty, fin = true) - } - "for a streamed message don't convert half surrogate pairs naively" in new ServerTestSetup { - val gclef = "𝄞" - gclef.length shouldEqual 2 - - // split up the code point - val half1 = gclef.take(1) - val half2 = gclef.drop(1) - - val pub = TestPublisher.manualProbe[String]() - val msg = TextMessage(Source.fromPublisher(pub)) - - pushMessage(msg) - val sub = pub.expectSubscription() - - expectFrameHeaderOnNetwork(Opcode.Text, 0, fin = false) - sub.sendNext(half1) - - expectNoNetworkData() - sub.sendNext(half2) - expectFrameOnNetwork(Opcode.Continuation, ByteString(gclef, "utf8"), fin = false) - } - "for a streamed message with a chunk being larger than configured maximum frame size" in pending - "and mask input on the client side" in new ClientTestSetup { - val text = "abcdefg" - val pub = TestPublisher.manualProbe[String]() - val msg = TextMessage(Source.fromPublisher(pub)) - pushMessage(msg) - val sub = pub.expectSubscription() - - expectFrameOnNetwork(Opcode.Text, ByteString.empty, fin = false) - - val text1 = text.take(3) - val text1Bytes = ByteString(text1, "UTF-8") - val text2 = text.drop(3) - val text2Bytes = ByteString(text2, "UTF-8") - - sub.sendNext(text1) - expectMaskedFrameOnNetwork(Opcode.Continuation, text1Bytes, fin = false) - - sub.sendNext(text2) - expectMaskedFrameOnNetwork(Opcode.Continuation, text2Bytes, fin = false) - - sub.sendComplete() - expectFrameOnNetwork(Opcode.Continuation, ByteString.empty, fin = true) - } - "and mask input on the client side for empty frame" in new ClientTestSetup { - pushMessage(TextMessage("")) - expectMaskedFrameOnNetwork(Opcode.Text, ByteString.empty, fin = true) - } - } - } - "supply automatic low-level websocket behavior" - { - "respond to ping frames unmasking them on the server side" in new ServerTestSetup { - val mask = Random.nextInt() - val input = frameHeader(Opcode.Ping, 6, fin = true, mask = Some(mask)) ++ maskedASCII("abcdef", mask)._1 - - pushInput(input) - expectFrameOnNetwork(Opcode.Pong, ByteString("abcdef"), fin = true) - } - "respond to ping frames masking them on the client side" in new ClientTestSetup { - val input = frameHeader(Opcode.Ping, 6, fin = true) ++ ByteString("abcdef") - - pushInput(input) - expectMaskedFrameOnNetwork(Opcode.Pong, ByteString("abcdef"), fin = true) - } - "respond to ping frames interleaved with data frames (without mixing frame data)" in new ServerTestSetup { - // receive multi-frame message - // receive and handle interleaved ping frame - // concurrently send out messages from handler - val mask1 = Random.nextInt() - val input1 = frameHeader(Opcode.Binary, 3, fin = false, mask = Some(mask1)) ++ maskedASCII("123", mask1)._1 - pushInput(input1) - - val dataSource = expectBinaryMessage().dataStream - val sub = TestSubscriber.manualProbe[ByteString]() - dataSource.runWith(Sink.fromSubscriber(sub)) - val s = sub.expectSubscription() - s.request(2) - sub.expectNext(ByteString("123", "ASCII")) - - val outPub = TestPublisher.manualProbe[ByteString]() - val msg = BinaryMessage(Source.fromPublisher(outPub)) - pushMessage(msg) - - expectFrameHeaderOnNetwork(Opcode.Binary, 0, fin = false) - - val outSub = outPub.expectSubscription() - val outData1 = ByteString("abc", "ASCII") - outSub.sendNext(outData1) - expectFrameOnNetwork(Opcode.Continuation, outData1, fin = false) - - val pingMask = Random.nextInt() - val pingData = maskedASCII("pling", pingMask)._1 - val pingData0 = pingData.take(3) - val pingData1 = pingData.drop(3) - pushInput(frameHeader(Opcode.Ping, 5, fin = true, mask = Some(pingMask)) ++ pingData0) - expectNoNetworkData() - pushInput(pingData1) - expectFrameOnNetwork(Opcode.Pong, ByteString("pling", "ASCII"), fin = true) - - val outData2 = ByteString("def", "ASCII") - outSub.sendNext(outData2) - expectFrameOnNetwork(Opcode.Continuation, outData2, fin = false) - - outSub.sendComplete() - expectFrameOnNetwork(Opcode.Continuation, ByteString.empty, fin = true) - - val mask2 = Random.nextInt() - val input2 = frameHeader(Opcode.Continuation, 3, fin = true, mask = Some(mask2)) ++ maskedASCII("456", mask2)._1 - pushInput(input2) - sub.expectNext(ByteString("456", "ASCII")) - s.request(1) - sub.expectComplete() - } - "don't respond to unsolicited pong frames" in new ClientTestSetup { - val data = frameHeader(Opcode.Pong, 6, fin = true) ++ ByteString("abcdef") - pushInput(data) - expectNoNetworkData() - } - } - "provide close behavior" - { - "after receiving regular close frame when idle (user closes immediately)" in new ServerTestSetup { - pushInput(closeFrame(Protocol.CloseCodes.Regular, mask = true)) - expectComplete(messageIn) - - netIn.expectNoMsg(100.millis) // especially the cancellation not yet - expectNoNetworkData() - messageOut.sendComplete() - - expectCloseCodeOnNetwork(Protocol.CloseCodes.Regular) - netOut.expectComplete() - netIn.expectCancellation() - } - "after receiving close frame without close code" in new ServerTestSetup { - pushInput(frameHeader(Opcode.Close, 0, fin = true, mask = Some(Random.nextInt()))) - expectComplete(messageIn) - - messageOut.sendComplete() - // especially mustn't be Protocol.CloseCodes.NoCodePresent - expectCloseCodeOnNetwork(Protocol.CloseCodes.Regular) - netOut.expectComplete() - netIn.expectCancellation() - } - "after receiving regular close frame when idle (but some data was exchanged before)" in new ServerTestSetup { - val msg = "äbcdef€\uffff" - val input = frame(Opcode.Text, ByteString(msg, "UTF-8"), fin = true, mask = true) - - // send at least one regular frame to trigger #19340 afterwards - pushInput(input) - expectMessage(TextMessage.Strict(msg)) - - pushInput(closeFrame(Protocol.CloseCodes.Regular, mask = true)) - expectComplete(messageIn) - - netIn.expectNoMsg(100.millis) // especially the cancellation not yet - expectNoNetworkData() - messageOut.sendComplete() - - expectCloseCodeOnNetwork(Protocol.CloseCodes.Regular) - netOut.expectComplete() - netIn.expectCancellation() - } - "after receiving regular close frame when idle (user still sends some data)" in new ServerTestSetup { - pushInput(closeFrame(Protocol.CloseCodes.Regular, mask = true)) - expectComplete(messageIn) - - // sending another message is allowed before closing (inherently racy) - val pub = TestPublisher.manualProbe[ByteString]() - val msg = BinaryMessage(Source.fromPublisher(pub)) - pushMessage(msg) - expectFrameOnNetwork(Opcode.Binary, ByteString.empty, fin = false) - - val data = ByteString("abc", "ASCII") - val dataSub = pub.expectSubscription() - dataSub.sendNext(data) - expectFrameOnNetwork(Opcode.Continuation, data, fin = false) - - dataSub.sendComplete() - expectFrameOnNetwork(Opcode.Continuation, ByteString.empty, fin = true) - - messageOut.sendComplete() - expectCloseCodeOnNetwork(Protocol.CloseCodes.Regular) - netOut.expectComplete() - } - "after receiving regular close frame when fragmented message is still open" in new ServerTestSetup { - pushInput(frameHeader(Protocol.Opcode.Binary, 0, fin = false, mask = Some(Random.nextInt()))) - val dataSource = expectBinaryMessage().dataStream - val inSubscriber = TestSubscriber.manualProbe[ByteString]() - dataSource.runWith(Sink.fromSubscriber(inSubscriber)) - val inSub = inSubscriber.expectSubscription() - - val outData = ByteString("def", "ASCII") - val mask = Random.nextInt() - pushInput(frameHeader(Protocol.Opcode.Continuation, 3, fin = false, mask = Some(mask)) ++ maskedBytes(outData, mask)._1) - inSub.request(5) - inSubscriber.expectNext(outData) - - pushInput(closeFrame(Protocol.CloseCodes.Regular, mask = true)) - - // This is arguable: we could also just fail the subStream but complete the main message stream regularly. - // However, truncating an ongoing message by closing without sending a `Continuation(fin = true)` first - // could be seen as something being amiss. - expectError(messageIn) - inSubscriber.expectError() - // truncation of open message - - // sending another message is allowed before closing (inherently racy) - - val pub = TestPublisher.manualProbe[ByteString]() - val msg = BinaryMessage(Source.fromPublisher(pub)) - pushMessage(msg) - expectFrameOnNetwork(Opcode.Binary, ByteString.empty, fin = false) - - val data = ByteString("abc", "ASCII") - val dataSub = pub.expectSubscription() - dataSub.sendNext(data) - expectFrameOnNetwork(Opcode.Continuation, data, fin = false) - - dataSub.sendComplete() - expectFrameOnNetwork(Opcode.Continuation, ByteString.empty, fin = true) - - messageOut.sendComplete() - expectCloseCodeOnNetwork(Protocol.CloseCodes.Regular) - netOut.expectComplete() - - } - "after receiving error close frame with close code and without reason" in new ServerTestSetup { - pushInput(closeFrame(Protocol.CloseCodes.UnexpectedCondition, mask = true)) - val error = expectError(messageIn).asInstanceOf[PeerClosedConnectionException] - error.closeCode shouldEqual Protocol.CloseCodes.UnexpectedCondition - error.closeReason shouldEqual "" - - expectCloseCodeOnNetwork(Protocol.CloseCodes.UnexpectedCondition) - messageOut.sendError(error) - netOut.expectComplete() - netIn.expectCancellation() - } - "after receiving error close frame with close code and with reason" in new ServerTestSetup { - pushInput(closeFrame(Protocol.CloseCodes.UnexpectedCondition, mask = true, - msg = "This alien landing came quite unexpected. Communication has been garbled.")) - val error = expectError(messageIn).asInstanceOf[PeerClosedConnectionException] - error.closeCode shouldEqual Protocol.CloseCodes.UnexpectedCondition - error.closeReason shouldEqual "This alien landing came quite unexpected. Communication has been garbled." - - expectCloseCodeOnNetwork(Protocol.CloseCodes.UnexpectedCondition) - messageOut.sendError(error) - netOut.expectComplete() - netIn.expectCancellation() - } - "after peer closes connection without sending a close frame" in new ServerTestSetup { - netIn.expectRequest() - netIn.sendComplete() - - expectComplete(messageIn) - messageOut.sendComplete() - - expectCloseCodeOnNetwork(Protocol.CloseCodes.Regular) - netOut.expectComplete() - } - "when user handler closes (simple)" in new ServerTestSetup { - messageOut.sendComplete() - expectCloseCodeOnNetwork(Protocol.CloseCodes.Regular) - - expectNoNetworkData() // wait for peer to close regularly - pushInput(closeFrame(Protocol.CloseCodes.Regular, mask = true)) - - expectComplete(messageIn) - netOut.expectComplete() - netIn.expectCancellation() - } - "when user handler closes main stream and substream only afterwards" in new ServerTestSetup { - // send half a message - val pub = TestPublisher.manualProbe[ByteString]() - val msg = BinaryMessage(Source.fromPublisher(pub)) - pushMessage(msg) - expectFrameOnNetwork(Opcode.Binary, ByteString.empty, fin = false) - - val data = ByteString("abc", "ASCII") - val dataSub = pub.expectSubscription() - dataSub.sendNext(data) - expectFrameOnNetwork(Opcode.Continuation, data, fin = false) - - messageOut.sendComplete() - expectNoNetworkData() // need to wait for substream to close - - dataSub.sendComplete() - expectFrameOnNetwork(Opcode.Continuation, ByteString.empty, fin = true) - expectCloseCodeOnNetwork(Protocol.CloseCodes.Regular) - expectNoNetworkData() // wait for peer to close regularly - - val mask = Random.nextInt() - pushInput(closeFrame(Protocol.CloseCodes.Regular, mask = true)) - - expectComplete(messageIn) - netOut.expectComplete() - netIn.expectCancellation() - } - "if user handler fails" in new ServerTestSetup { - EventFilter[RuntimeException](message = "Oops, user handler failed!", occurrences = 1) - .intercept { - messageOut.sendError(new RuntimeException("Oops, user handler failed!")) - expectCloseCodeOnNetwork(Protocol.CloseCodes.UnexpectedCondition) - - expectNoNetworkData() // wait for peer to close regularly - pushInput(closeFrame(Protocol.CloseCodes.Regular, mask = true)) - - expectComplete(messageIn) - netOut.expectComplete() - netIn.expectCancellation() - } - } - "if peer closes with invalid close frame" - { - "close code outside of the valid range" in new ServerTestSetup { - pushInput(closeFrame(5700, mask = true)) - - val error = expectError(messageIn).asInstanceOf[PeerClosedConnectionException] - error.closeCode shouldEqual Protocol.CloseCodes.ProtocolError - error.closeReason shouldEqual "Peer sent illegal close frame (invalid close code '5700')." - - expectCloseCodeOnNetwork(Protocol.CloseCodes.ProtocolError) - netOut.expectComplete() - netIn.expectCancellation() - } - "close data of size 1" in new ServerTestSetup { - pushInput(frameHeader(Opcode.Close, 1, mask = Some(Random.nextInt()), fin = true) ++ ByteString("x")) - - val error = expectError(messageIn).asInstanceOf[PeerClosedConnectionException] - error.closeCode shouldEqual Protocol.CloseCodes.ProtocolError - error.closeReason shouldEqual "Peer sent illegal close frame (close code must be length 2 but was 1)." - - expectCloseCodeOnNetwork(Protocol.CloseCodes.ProtocolError) - netOut.expectComplete() - netIn.expectCancellation() - } - "close message is invalid UTF8" in new ServerTestSetup { - pushInput(closeFrame(Protocol.CloseCodes.UnexpectedCondition, mask = true, msgBytes = InvalidUtf8TwoByteSequence)) - - val error = expectError(messageIn).asInstanceOf[PeerClosedConnectionException] - error.closeCode shouldEqual Protocol.CloseCodes.ProtocolError - error.closeReason shouldEqual "Peer sent illegal close frame (close reason message is invalid UTF8)." - - expectCloseCodeOnNetwork(Protocol.CloseCodes.ProtocolError) - netOut.expectComplete() - netIn.expectCancellation() - } - } - "timeout if user handler closes and peer doesn't send a close frame" in new ServerTestSetup { - override protected def closeTimeout: FiniteDuration = 100.millis - - messageOut.sendComplete() - expectCloseCodeOnNetwork(Protocol.CloseCodes.Regular) - - netOut.expectComplete() - netIn.expectCancellation() - } - "timeout after we close after error and peer doesn't send a close frame" in new ServerTestSetup { - override protected def closeTimeout: FiniteDuration = 100.millis - - pushInput(frameHeader(Opcode.Binary, 0, fin = true, rsv1 = true)) - expectProtocolErrorOnNetwork() - messageOut.sendComplete() - - netOut.expectComplete() - netIn.expectCancellation() - } - "ignore frames peer sends after close frame" in new ServerTestSetup { - pushInput(closeFrame(Protocol.CloseCodes.Regular, mask = true)) - - expectComplete(messageIn) - - pushInput(frameHeader(Opcode.Binary, 0, fin = true)) - messageOut.sendComplete() - expectCloseCodeOnNetwork(Protocol.CloseCodes.Regular) - - netOut.expectComplete() - netIn.expectCancellation() - } - } - "reject unexpected frames" - { - "reserved bits set" - { - "rsv1" in new ServerTestSetup { - pushInput(frameHeader(Opcode.Binary, 0, fin = true, rsv1 = true)) - expectProtocolErrorOnNetwork() - } - "rsv2" in new ServerTestSetup { - pushInput(frameHeader(Opcode.Binary, 0, fin = true, rsv2 = true)) - expectProtocolErrorOnNetwork() - } - "rsv3" in new ServerTestSetup { - pushInput(frameHeader(Opcode.Binary, 0, fin = true, rsv3 = true)) - expectProtocolErrorOnNetwork() - } - } - "highest bit of 64-bit length is set" in new ServerTestSetup { - import BitBuilder._ - - val header = - b"""0000 # flags - xxxx=1 # opcode - 1 # mask? - xxxxxxx=7f # length - xxxxxxxx - xxxxxxxx - xxxxxxxx - xxxxxxxx=ffffffff - xxxxxxxx - xxxxxxxx - xxxxxxxx - xxxxxxxx=ffffffff # length64 - 00000000 - 00000000 - 00000000 - 00000000 # empty mask - """ - - EventFilter[ProtocolException](occurrences = 1).intercept { - pushInput(header) - expectProtocolErrorOnNetwork() - } - } - "control frame bigger than 125 bytes" in new ServerTestSetup { - pushInput(frameHeader(Opcode.Ping, 126, fin = true, mask = Some(0))) - expectProtocolErrorOnNetwork() - } - "fragmented control frame" in new ServerTestSetup { - pushInput(frameHeader(Opcode.Ping, 0, fin = false, mask = Some(0))) - expectProtocolErrorOnNetwork() - } - "unexpected continuation frame" in new ServerTestSetup { - pushInput(frameHeader(Opcode.Continuation, 0, fin = false, mask = Some(0))) - expectProtocolErrorOnNetwork() - } - "unexpected data frame when waiting for continuation" in new ServerTestSetup { - pushInput(frameHeader(Opcode.Binary, 0, fin = false) ++ - frameHeader(Opcode.Binary, 0, fin = false)) - expectProtocolErrorOnNetwork() - } - "invalid utf8 encoding for single frame message" in new ClientTestSetup { - val data = InvalidUtf8TwoByteSequence - - pushInput(frameHeader(Opcode.Text, 2, fin = true) ++ data) - expectCloseCodeOnNetwork(Protocol.CloseCodes.InconsistentData) - } - "invalid utf8 encoding for streamed frame" in new ClientTestSetup { - val data = InvalidUtf8TwoByteSequence - - pushInput(frameHeader(Opcode.Text, 0, fin = false) ++ - frameHeader(Opcode.Continuation, 2, fin = true) ++ - data) - expectCloseCodeOnNetwork(Protocol.CloseCodes.InconsistentData) - } - "truncated utf8 encoding for single frame message" in new ClientTestSetup { - val data = ByteString("€", "UTF-8").take(1) // half a euro - pushInput(frameHeader(Opcode.Text, 1, fin = true) ++ data) - expectCloseCodeOnNetwork(Protocol.CloseCodes.InconsistentData) - } - "truncated utf8 encoding for streamed frame" in new ClientTestSetup { - val data = ByteString("€", "UTF-8").take(1) // half a euro - pushInput(frameHeader(Opcode.Text, 0, fin = false) ++ - frameHeader(Opcode.Continuation, 1, fin = true) ++ - data) - expectCloseCodeOnNetwork(Protocol.CloseCodes.InconsistentData) - } - "half a surrogate pair in utf8 encoding for a strict frame" in new ClientTestSetup { - val data = ByteString(0xed, 0xa0, 0x80) // not strictly supported by utf-8 - pushInput(frameHeader(Opcode.Text, 3, fin = true) ++ data) - expectCloseCodeOnNetwork(Protocol.CloseCodes.InconsistentData) - } - "half a surrogate pair in utf8 encoding for a streamed frame" in new ClientTestSetup { - val data = ByteString(0xed, 0xa0, 0x80) // not strictly supported by utf-8 - pushInput(frameHeader(Opcode.Text, 0, fin = false)) - pushInput(frameHeader(Opcode.Continuation, 3, fin = true) ++ data) - - // Kids, always drain your entities - messageIn.requestNext() match { - case b: TextMessage ⇒ - b.textStream.runWith(Sink.ignore) - case _ ⇒ - } - - expectError(messageIn) - - expectCloseCodeOnNetwork(Protocol.CloseCodes.InconsistentData) - } - "unmasked input on the server side" in new ServerTestSetup { - val data = ByteString("abcdef", "ASCII") - val input = frameHeader(Opcode.Binary, 6, fin = true) ++ data - - pushInput(input) - expectProtocolErrorOnNetwork() - } - "unmasked input on the server side for empty frame" in new ServerTestSetup { - val input = frameHeader(Opcode.Binary, 0, fin = true) - - pushInput(input) - expectProtocolErrorOnNetwork() - } - "masked input on the client side" in new ClientTestSetup { - val mask = Random.nextInt() - val input = frameHeader(Opcode.Binary, 6, fin = true, mask = Some(mask)) ++ maskedASCII("abcdef", mask)._1 - - pushInput(input) - expectProtocolErrorOnNetwork() - } - "masked input on the client side for empty frame" in new ClientTestSetup { - val mask = Random.nextInt() - val input = frameHeader(Opcode.Binary, 0, fin = true, mask = Some(mask)) - - pushInput(input) - expectProtocolErrorOnNetwork() - } - } - "support per-message-compression extension" in pending - } - - class ServerTestSetup extends TestSetup { - protected def serverSide: Boolean = true - } - class ClientTestSetup extends TestSetup { - protected def serverSide: Boolean = false - } - abstract class TestSetup { - protected def serverSide: Boolean - protected def closeTimeout: FiniteDuration = 1.second - - val netIn = TestPublisher.probe[ByteString]() - val netOut = ByteStringSinkProbe() - - val messageIn = TestSubscriber.probe[Message] - val messageOut = TestPublisher.probe[Message]() - - val messageHandler: Flow[Message, Message, NotUsed] = - Flow.fromSinkAndSource( - Flow[Message].buffer(1, OverflowStrategy.backpressure).to(Sink.fromSubscriber(messageIn)), // alternatively need to request(1) before expectComplete - Source.fromPublisher(messageOut)) - - Source.fromPublisher(netIn) - .via(printEvent("netIn")) - .via(FrameEventParser) - .via(WebSocket - .stack(serverSide, maskingRandomFactory = Randoms.SecureRandomInstances, closeTimeout = closeTimeout, log = system.log) - .join(messageHandler)) - .via(printEvent("frameRendererIn")) - .transform(() ⇒ new FrameEventRenderer) - .via(printEvent("frameRendererOut")) - .buffer(1, OverflowStrategy.backpressure) // alternatively need to request(1) before expectComplete - .to(netOut.sink) - .run() - - def pushInput(data: ByteString): Unit = netIn.sendNext(data) - def pushMessage(msg: Message): Unit = messageOut.sendNext(msg) - def expectMessage(message: Message): Unit = messageIn.requestNext(message) - def expectMessage(): Message = messageIn.requestNext() - def expectBinaryMessage(): BinaryMessage = expectMessage().asInstanceOf[BinaryMessage] - def expectBinaryMessage(message: BinaryMessage): Unit = expectBinaryMessage() shouldEqual message - def expectTextMessage(): TextMessage = expectMessage().asInstanceOf[TextMessage] - def expectTextMessage(message: TextMessage): Unit = expectTextMessage() shouldEqual message - final def expectNetworkData(bytes: Int): ByteString = netOut.expectBytes(bytes) - - def expectNetworkData(data: ByteString): Unit = expectNetworkData(data.size) shouldEqual data - - def expectFrameOnNetwork(opcode: Opcode, data: ByteString, fin: Boolean): Unit = { - expectFrameHeaderOnNetwork(opcode, data.size, fin) - expectNetworkData(data) - } - def expectMaskedFrameOnNetwork(opcode: Opcode, data: ByteString, fin: Boolean): Unit = { - val Some(mask) = expectFrameHeaderOnNetwork(opcode, data.size, fin) - val masked = maskedBytes(data, mask)._1 - expectNetworkData(masked) - } - - /** Returns the mask if any is available */ - def expectFrameHeaderOnNetwork(opcode: Opcode, length: Long, fin: Boolean): Option[Int] = { - val (op, l, f, m) = expectFrameHeaderOnNetwork() - op shouldEqual opcode - l shouldEqual length - f shouldEqual fin - m - } - def expectFrameHeaderOnNetwork(): (Opcode, Long, Boolean, Option[Int]) = { - val header = expectNetworkData(2) - - val fin = (header(0) & Protocol.FIN_MASK) != 0 - val op = header(0) & Protocol.OP_MASK - - val hasMask = (header(1) & Protocol.MASK_MASK) != 0 - val length7 = header(1) & Protocol.LENGTH_MASK - val length = length7 match { - case 126 ⇒ - val length16Bytes = expectNetworkData(2) - (length16Bytes(0) & 0xff) << 8 | (length16Bytes(1) & 0xff) << 0 - case 127 ⇒ - val length64Bytes = expectNetworkData(8) - (length64Bytes(0) & 0xff).toLong << 56 | - (length64Bytes(1) & 0xff).toLong << 48 | - (length64Bytes(2) & 0xff).toLong << 40 | - (length64Bytes(3) & 0xff).toLong << 32 | - (length64Bytes(4) & 0xff).toLong << 24 | - (length64Bytes(5) & 0xff).toLong << 16 | - (length64Bytes(6) & 0xff).toLong << 8 | - (length64Bytes(7) & 0xff).toLong << 0 - case x ⇒ x - } - val mask = - if (hasMask) { - val maskBytes = expectNetworkData(4) - val mask = - (maskBytes(0) & 0xff) << 24 | - (maskBytes(1) & 0xff) << 16 | - (maskBytes(2) & 0xff) << 8 | - (maskBytes(3) & 0xff) << 0 - Some(mask) - } else None - - (Opcode.forCode(op.toByte), length, fin, mask) - } - - def expectProtocolErrorOnNetwork(): Unit = expectCloseCodeOnNetwork(Protocol.CloseCodes.ProtocolError) - def expectCloseCodeOnNetwork(expectedCode: Int): Unit = { - val (opcode, length, true, mask) = expectFrameHeaderOnNetwork() - opcode shouldEqual Opcode.Close - length should be >= 2.toLong - - val rawData = expectNetworkData(length.toInt) - val data = mask match { - case Some(m) ⇒ FrameEventParser.mask(rawData, m)._1 - case None ⇒ rawData - } - - val code = ((data(0) & 0xff) << 8) | ((data(1) & 0xff) << 0) - code shouldEqual expectedCode - } - - def expectNoNetworkData(): Unit = netOut.expectNoBytes(100.millis) - - def expectComplete[T](probe: TestSubscriber.Probe[T]): Unit = { - probe.ensureSubscription() - probe.request(1) - probe.expectComplete() - } - def expectError[T](probe: TestSubscriber.Probe[T]): Throwable = { - probe.ensureSubscription() - probe.request(1) - probe.expectError() - } - } - - val trace = false // set to `true` for debugging purposes - def printEvent[T](marker: String): Flow[T, T, NotUsed] = - if (trace) Flow[T].log(marker) - else Flow[T] -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/ws/Utf8CodingSpecs.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/ws/Utf8CodingSpecs.scala deleted file mode 100644 index c15fe00969..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/ws/Utf8CodingSpecs.scala +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.ws - -import org.scalacheck.Gen - -import scala.concurrent.duration._ - -import akka.stream.scaladsl.Source -import akka.util.ByteString -import akka.http.impl.util._ -import org.scalatest.prop.PropertyChecks -import org.scalatest.{ FreeSpec, Matchers } - -class Utf8CodingSpecs extends FreeSpec with Matchers with PropertyChecks with WithMaterializerSpec { - "Utf8 decoding/encoding" - { - "work for all codepoints" in { - def isSurrogate(cp: Int): Boolean = - cp >= Utf8Encoder.SurrogateFirst && cp <= 0xdfff - - val cps = - Gen.choose(0, 0x10ffff) - .filter(!isSurrogate(_)) - - def codePointAsString(cp: Int): String = { - if (cp < 0x10000) new String(Array(cp.toChar)) - else { - val part0 = 0xd7c0 + (cp >> 10) // constant has 0x10000 subtracted already - val part1 = 0xdc00 + (cp & 0x3ff) - new String(Array(part0.toChar, part1.toChar)) - } - } - - forAll(cps) { (cp: Int) ⇒ - val utf16 = codePointAsString(cp) - decodeUtf8(encodeUtf8(utf16)) === utf16 - } - } - } - - def encodeUtf8(str: String): ByteString = - Source(str.map(ch ⇒ new String(Array(ch)))) // chunk in smallest chunks possible - .via(Utf8Encoder) - .runFold(ByteString.empty)(_ ++ _).awaitResult(1.second) - - def decodeUtf8(bytes: ByteString): String = { - val builder = new StringBuilder - val decoder = Utf8Decoder.create() - bytes - .map(b ⇒ ByteString(b)) // chunk in smallest chunks possible - .foreach { bs ⇒ - builder append decoder.decode(bs, endOfInput = false).get - } - - builder append decoder.decode(ByteString.empty, endOfInput = true).get - builder.toString() - } -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/ws/WSClientAutobahnTest.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/ws/WSClientAutobahnTest.scala deleted file mode 100644 index d249b73758..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/ws/WSClientAutobahnTest.scala +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.ws - -import scala.concurrent.{ Promise, Future } -import scala.util.{ Try, Failure, Success } - -import spray.json._ - -import akka.actor.ActorSystem - -import akka.stream.ActorMaterializer -import akka.stream.stage.{ TerminationDirective, Context, SyncDirective, PushStage } -import akka.stream.scaladsl._ - -import akka.http.scaladsl.Http -import akka.http.scaladsl.model.Uri -import akka.http.scaladsl.model.ws._ - -object WSClientAutobahnTest extends App { - implicit val system = ActorSystem() - import system.dispatcher - implicit val materializer = ActorMaterializer() - - val Agent = "akka-http" - val Parallelism = 4 - - val getCaseCountUri: Uri = - s"ws://localhost:9001/getCaseCount" - - def runCaseUri(caseIndex: Int, agent: String): Uri = - s"ws://localhost:9001/runCase?case=$caseIndex&agent=$agent" - - def getCaseStatusUri(caseIndex: Int, agent: String): Uri = - s"ws://localhost:9001/getCaseStatus?case=$caseIndex&agent=$agent" - - def getCaseInfoUri(caseIndex: Int): Uri = - s"ws://localhost:9001/getCaseInfo?case=$caseIndex" - - def updateReportsUri(agent: String): Uri = - s"ws://localhost:9001/updateReports?agent=$agent" - - def runCase(caseIndex: Int, agent: String = Agent): Future[CaseStatus] = - runWs(runCaseUri(caseIndex, agent), echo).recover { case _ ⇒ () }.flatMap { _ ⇒ - getCaseStatus(caseIndex, agent) - } - - def richRunCase(caseIndex: Int, agent: String = Agent): Future[CaseResult] = { - val info = getCaseInfo(caseIndex) - val startMillis = System.currentTimeMillis() - val status = runCase(caseIndex, agent).map { res ⇒ - val lastedMillis = System.currentTimeMillis() - startMillis - (res, lastedMillis) - } - import Console._ - info.flatMap { i ⇒ - val prefix = f"$YELLOW${i.caseInfo.id}%-7s$RESET - $RESET${i.caseInfo.description}$RESET ... " - - status.onComplete { - case Success((CaseStatus(status), millis)) ⇒ - val color = if (status == "OK") GREEN else RED - println(f"${color}$status%-15s$RESET$millis%5d ms $prefix") - case Failure(e) ⇒ - println(s"$prefix${RED}failed with '${e.getMessage}'$RESET") - } - - status.map(s ⇒ CaseResult(i.caseInfo, s._1)) - } - } - - def getCaseCount(): Future[Int] = - runToSingleText(getCaseCountUri).map(_.toInt) - - def getCaseInfo(caseId: Int): Future[IndexedCaseInfo] = - runToSingleJsonValue[CaseInfo](getCaseInfoUri(caseId)).map(IndexedCaseInfo(caseId, _)) - - def getCaseStatus(caseId: Int, agent: String = Agent): Future[CaseStatus] = - runToSingleJsonValue[CaseStatus](getCaseStatusUri(caseId, agent)) - - def updateReports(agent: String = Agent): Future[Unit] = - runToSingleText(updateReportsUri(agent)).map(_ ⇒ ()) - - /** - * Map from textual case ID (like 1.1.1) to IndexedCaseInfo - * @return - */ - def getCaseMap(): Future[Map[String, IndexedCaseInfo]] = { - val res = - getCaseCount().flatMap { count ⇒ - println(s"Retrieving case info for $count cases...") - Future.traverse(1 to count)(getCaseInfo).map(_.map(e ⇒ e.caseInfo.id → e).toMap) - } - res.foreach { res ⇒ - println(s"Received info for ${res.size} cases") - } - res - } - - def echo = Flow[Message].viaMat(completionSignal)(Keep.right) - - import Console._ - if (args.size >= 1) { - // run one - val testId = args(0) - println(s"Trying to run test $testId") - getCaseMap().flatMap { map ⇒ - val info = map(testId) - richRunCase(info.index) - }.onComplete { - case Success(res) ⇒ - println(s"[OK] Run successfully finished!") - updateReportsAndShutdown() - case Failure(e) ⇒ - println(s"[${RED}FAILED$RESET] Run failed with this exception: ") - e.printStackTrace() - updateReportsAndShutdown() - } - } else { - println("Running complete test suite") - getCaseCount().flatMap { count ⇒ - println(s"Found $count tests.") - Source(1 to count).mapAsyncUnordered(Parallelism)(richRunCase(_)).grouped(count).runWith(Sink.head) - }.map { results ⇒ - val grouped = - results.groupBy(_.status.behavior) - - println(s"${results.size} tests run.") - println() - println(s"${GREEN}OK$RESET: ${grouped.getOrElse("OK", Nil).size}") - val notOk = grouped.filterNot(_._1 == "OK") - notOk.toSeq.sortBy(_._2.size).foreach { - case (status, cases) ⇒ println(s"$RED$status$RESET: ${cases.size}") - } - println() - println("Not OK tests: ") - println() - results.filterNot(_.status.behavior == "OK").foreach { r ⇒ - println(f"$RED${r.status.behavior}%-20s$RESET $YELLOW${r.info.id}%-7s$RESET - $RESET${r.info.description}") - } - - () - } - .onComplete(completion) - } - - def completion[T]: Try[T] ⇒ Unit = { - case Success(res) ⇒ - println(s"Run successfully finished!") - updateReportsAndShutdown() - case Failure(e) ⇒ - println("Run failed with this exception") - e.printStackTrace() - updateReportsAndShutdown() - } - def updateReportsAndShutdown(): Unit = - updateReports().onComplete { res ⇒ - println("Reports should now be accessible at http://localhost:8080/cwd/reports/clients/index.html") - system.terminate() - } - - import scala.concurrent.duration._ - import system.dispatcher - system.scheduler.scheduleOnce(60.seconds)(system.terminate()) - - def runWs[T](uri: Uri, clientFlow: Flow[Message, Message, T]): T = - Http().singleWebSocketRequest(uri, clientFlow)._2 - - def completionSignal[T]: Flow[T, T, Future[Unit]] = - Flow[T].transformMaterializing { () ⇒ - val p = Promise[Unit]() - val stage = - new PushStage[T, T] { - def onPush(elem: T, ctx: Context[T]): SyncDirective = ctx.push(elem) - override def onUpstreamFinish(ctx: Context[T]): TerminationDirective = { - p.success(()) - super.onUpstreamFinish(ctx) - } - override def onDownstreamFinish(ctx: Context[T]): TerminationDirective = { - p.success(()) // should this be failure as well? - super.onDownstreamFinish(ctx) - } - override def onUpstreamFailure(cause: Throwable, ctx: Context[T]): TerminationDirective = { - p.failure(cause) - super.onUpstreamFailure(cause, ctx) - } - } - - (stage, p.future) - } - - /** - * The autobahn tests define a weird API where every request must be a WebSocket request and - * they will send a single websocket message with the result. WebSocket everywhere? Strange, - * but somewhat consistent. - */ - def runToSingleText(uri: Uri): Future[String] = { - val sink = Sink.head[Message] - runWs(uri, Flow.fromSinkAndSourceMat(sink, Source.maybe[Message])(Keep.left)).flatMap { - case tm: TextMessage ⇒ tm.textStream.runWith(Sink.fold("")(_ + _)) - case other ⇒ - throw new IllegalStateException(s"unexpected element of type ${other.getClass}") - } - } - def runToSingleJsonValue[T: JsonReader](uri: Uri): Future[T] = - runToSingleText(uri).map(_.parseJson.convertTo[T]) - - case class IndexedCaseInfo(index: Int, caseInfo: CaseInfo) - case class CaseResult(info: CaseInfo, status: CaseStatus) - - // {"behavior": "OK"} - case class CaseStatus(behavior: String) { - def isSuccessful: Boolean = behavior == "OK" - } - object CaseStatus { - import DefaultJsonProtocol._ - implicit def caseStatusFormat: JsonFormat[CaseStatus] = jsonFormat1(CaseStatus.apply) - } - - // {"id": "1.1.1", "description": "Send text message with payload 0."} - case class CaseInfo(id: String, description: String) - object CaseInfo { - import DefaultJsonProtocol._ - implicit def caseInfoFormat: JsonFormat[CaseInfo] = jsonFormat2(CaseInfo.apply) - } -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/ws/WSServerAutobahnTest.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/ws/WSServerAutobahnTest.scala deleted file mode 100644 index cf5bf0196b..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/ws/WSServerAutobahnTest.scala +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.ws - -import akka.NotUsed - -import scala.concurrent.Await -import scala.concurrent.duration._ - -import akka.actor.ActorSystem -import akka.http.scaladsl.Http -import akka.http.scaladsl.model.HttpMethods._ -import akka.http.scaladsl.model.ws.{ Message, UpgradeToWebSocket } -import akka.http.scaladsl.model._ -import akka.stream.ActorMaterializer -import akka.stream.scaladsl.Flow - -object WSServerAutobahnTest extends App { - implicit val system = ActorSystem("WSServerTest") - implicit val fm = ActorMaterializer() - - val props = sys.props - val host = props.getOrElse("akka.ws-host", "127.0.0.1") - val port = props.getOrElse("akka.ws-port", "9001").toInt - val mode = props.getOrElse("akka.ws-mode", "read") // read or sleep - - try { - val binding = Http().bindAndHandleSync( - { - case req @ HttpRequest(GET, Uri.Path("/"), _, _, _) if req.header[UpgradeToWebSocket].isDefined ⇒ - req.header[UpgradeToWebSocket] match { - case Some(upgrade) ⇒ upgrade.handleMessages(echoWebSocketService) // needed for running the autobahn test suite - case None ⇒ HttpResponse(400, entity = "Not a valid websocket request!") - } - case _: HttpRequest ⇒ HttpResponse(404, entity = "Unknown resource!") - }, - interface = host, // adapt to your docker host IP address if necessary - port = port) - - Await.result(binding, 3.second) // throws if binding fails - println(s"Server online at http://${host}:${port}") - mode match { - case "sleep" ⇒ while (true) Thread.sleep(1.minute.toMillis) - case "read" ⇒ Console.readLine("Press RETURN to stop...") - case _ ⇒ throw new Exception("akka.ws-mode MUST be sleep or read.") - } - } finally { - system.terminate() - } - - def echoWebSocketService: Flow[Message, Message, NotUsed] = - Flow[Message] // just let message flow directly to the output -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/ws/WSTestSetupBase.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/ws/WSTestSetupBase.scala deleted file mode 100644 index 9626741bbe..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/ws/WSTestSetupBase.scala +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.ws - -import akka.http.impl.engine.ws.Protocol.Opcode -import akka.http.impl.engine.ws.WSTestUtils._ -import akka.util.ByteString -import org.scalatest.Matchers - -import scala.util.Random - -trait WSTestSetupBase extends Matchers { - def send(bytes: ByteString): Unit - def expectBytes(length: Int): ByteString - def expectBytes(bytes: ByteString): Unit - - def sendWSFrame( - opcode: Opcode, - data: ByteString, - fin: Boolean, - mask: Boolean = false, - rsv1: Boolean = false, - rsv2: Boolean = false, - rsv3: Boolean = false): Unit = { - val (theMask, theData) = - if (mask) { - val m = Random.nextInt() - (Some(m), maskedBytes(data, m)._1) - } else (None, data) - send(frameHeader(opcode, data.length, fin, theMask, rsv1, rsv2, rsv3) ++ theData) - } - - def sendWSCloseFrame(closeCode: Int, mask: Boolean = false): Unit = - send(closeFrame(closeCode, mask)) - - def expectWSFrame( - opcode: Opcode, - data: ByteString, - fin: Boolean, - mask: Option[Int] = None, - rsv1: Boolean = false, - rsv2: Boolean = false, - rsv3: Boolean = false): Unit = - expectBytes(frameHeader(opcode, data.length, fin, mask, rsv1, rsv2, rsv3) ++ data) - - def expectWSCloseFrame(closeCode: Int, mask: Boolean = false): Unit = - expectBytes(closeFrame(closeCode, mask)) - - def expectNetworkData(length: Int): ByteString = expectBytes(length) - def expectNetworkData(data: ByteString): Unit = expectBytes(data) - - def expectFrameOnNetwork(opcode: Opcode, data: ByteString, fin: Boolean): Unit = { - expectFrameHeaderOnNetwork(opcode, data.size, fin) - expectNetworkData(data) - } - def expectMaskedFrameOnNetwork(opcode: Opcode, data: ByteString, fin: Boolean): Unit = { - val Some(mask) = expectFrameHeaderOnNetwork(opcode, data.size, fin) - val masked = maskedBytes(data, mask)._1 - expectNetworkData(masked) - } - - def expectMaskedCloseFrame(closeCode: Int): Unit = - expectMaskedFrameOnNetwork(Protocol.Opcode.Close, closeFrameData(closeCode), fin = true) - - /** Returns the mask if any is available */ - def expectFrameHeaderOnNetwork(opcode: Opcode, length: Long, fin: Boolean): Option[Int] = { - val (op, l, f, m) = expectFrameHeaderOnNetwork() - op shouldEqual opcode - l shouldEqual length - f shouldEqual fin - m - } - def expectFrameHeaderOnNetwork(): (Opcode, Long, Boolean, Option[Int]) = { - val header = expectNetworkData(2) - - val fin = (header(0) & Protocol.FIN_MASK) != 0 - val op = header(0) & Protocol.OP_MASK - - val hasMask = (header(1) & Protocol.MASK_MASK) != 0 - val length7 = header(1) & Protocol.LENGTH_MASK - val length = length7 match { - case 126 ⇒ - val length16Bytes = expectNetworkData(2) - (length16Bytes(0) & 0xff) << 8 | (length16Bytes(1) & 0xff) << 0 - case 127 ⇒ - val length64Bytes = expectNetworkData(8) - (length64Bytes(0) & 0xff).toLong << 56 | - (length64Bytes(1) & 0xff).toLong << 48 | - (length64Bytes(2) & 0xff).toLong << 40 | - (length64Bytes(3) & 0xff).toLong << 32 | - (length64Bytes(4) & 0xff).toLong << 24 | - (length64Bytes(5) & 0xff).toLong << 16 | - (length64Bytes(6) & 0xff).toLong << 8 | - (length64Bytes(7) & 0xff).toLong << 0 - case x ⇒ x - } - val mask = - if (hasMask) { - val maskBytes = expectNetworkData(4) - val mask = - (maskBytes(0) & 0xff) << 24 | - (maskBytes(1) & 0xff) << 16 | - (maskBytes(2) & 0xff) << 8 | - (maskBytes(3) & 0xff) << 0 - Some(mask) - } else None - - (Opcode.forCode(op.toByte), length, fin, mask) - } -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/ws/WSTestUtils.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/ws/WSTestUtils.scala deleted file mode 100644 index 3768c13755..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/ws/WSTestUtils.scala +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.ws - -import akka.http.impl.engine.ws.Protocol.Opcode -import akka.util.ByteString - -import scala.util.Random - -object WSTestUtils { - def frameHeader( - opcode: Opcode, - length: Long, - fin: Boolean, - mask: Option[Int] = None, - rsv1: Boolean = false, - rsv2: Boolean = false, - rsv3: Boolean = false): ByteString = { - def set(should: Boolean, mask: Int): Int = - if (should) mask else 0 - - val flags = - set(fin, Protocol.FIN_MASK) | - set(rsv1, Protocol.RSV1_MASK) | - set(rsv2, Protocol.RSV2_MASK) | - set(rsv3, Protocol.RSV3_MASK) - - val opcodeByte = opcode.code | flags - - require(length >= 0) - val (lengthByteComponent, lengthBytes) = - if (length < 126) (length.toByte, ByteString.empty) - else if (length < 65536) (126.toByte, shortBE(length.toInt)) - else throw new IllegalArgumentException("Only lengths < 65536 allowed in test") - - val maskMask = if (mask.isDefined) Protocol.MASK_MASK else 0 - val maskBytes = mask match { - case Some(mask) ⇒ intBE(mask) - case None ⇒ ByteString.empty - } - val lengthByte = lengthByteComponent | maskMask - ByteString(opcodeByte.toByte, lengthByte.toByte) ++ lengthBytes ++ maskBytes - } - def frame(opcode: Opcode, data: ByteString, fin: Boolean, mask: Boolean): ByteString = - if (mask) { - val mask = Random.nextInt() - frameHeader(opcode, data.size, fin, mask = Some(mask)) ++ - maskedBytes(data, mask)._1 - } else - frameHeader(opcode, data.size, fin, mask = None) ++ data - - def closeFrame(closeCode: Int, mask: Boolean, msg: String = ""): ByteString = - closeFrame(closeCode, mask, ByteString(msg, "UTF-8")) - - def closeFrame(closeCode: Int, mask: Boolean, msgBytes: ByteString): ByteString = - frame(Opcode.Close, closeFrameData(closeCode, msgBytes), fin = true, mask) - - def closeFrameData(closeCode: Int, msgBytes: ByteString = ByteString.empty): ByteString = - shortBE(closeCode) ++ msgBytes - - def maskedASCII(str: String, mask: Int): (ByteString, Int) = - FrameEventParser.mask(ByteString(str, "ASCII"), mask) - def maskedUTF8(str: String, mask: Int): (ByteString, Int) = - FrameEventParser.mask(ByteString(str, "UTF-8"), mask) - def maskedBytes(bytes: ByteString, mask: Int): (ByteString, Int) = - FrameEventParser.mask(bytes, mask) - - def shortBE(value: Int): ByteString = { - require(value >= 0 && value < 65536, s"Value wasn't in short range: $value") - ByteString( - ((value >> 8) & 0xff).toByte, - ((value >> 0) & 0xff).toByte) - } - def intBE(value: Int): ByteString = - ByteString( - ((value >> 24) & 0xff).toByte, - ((value >> 16) & 0xff).toByte, - ((value >> 8) & 0xff).toByte, - ((value >> 0) & 0xff).toByte) -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/ws/WebSocketClientSpec.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/ws/WebSocketClientSpec.scala deleted file mode 100644 index 8b9d1107e8..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/ws/WebSocketClientSpec.scala +++ /dev/null @@ -1,374 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.ws - -import java.util.Random - -import akka.NotUsed -import akka.http.scaladsl.model.ws.{ InvalidUpgradeResponse, WebSocketUpgradeResponse } -import akka.stream.ClosedShape -import akka.stream.TLSProtocol._ - -import scala.concurrent.duration._ - -import akka.http.scaladsl.settings.ClientConnectionSettings -import akka.http.scaladsl.Http -import akka.http.scaladsl.model.headers.{ ProductVersion, `User-Agent` } -import akka.http.scaladsl.model.ws._ -import akka.http.scaladsl.model.Uri -import akka.stream.scaladsl._ -import akka.stream.testkit.{ TestSubscriber, TestPublisher } -import akka.util.ByteString -import org.scalatest.{ Matchers, FreeSpec } - -import akka.http.impl.util._ - -class WebSocketClientSpec extends FreeSpec with Matchers with WithMaterializerSpec { - "The client-side WebSocket implementation should" - { - "establish a websocket connection when the user requests it" in new EstablishedConnectionSetup with ClientEchoes - "establish connection with case insensitive header values" in new TestSetup with ClientEchoes { - expectWireData(UpgradeRequestBytes) - sendWireData("""HTTP/1.1 101 Switching Protocols - |Upgrade: wEbSOckET - |Sec-WebSocket-Accept: ujmZX4KXZqjwy6vi1aQFH5p4Ygk= - |Server: akka-http/test - |Sec-WebSocket-Version: 13 - |Connection: upgrade - | - |""") - - sendWSFrame(Protocol.Opcode.Text, ByteString("Message 1"), fin = true) - expectMaskedFrameOnNetwork(Protocol.Opcode.Text, ByteString("Message 1"), fin = true) - } - "reject invalid handshakes" - { - "other status code" in new TestSetup with ClientEchoes { - expectWireData(UpgradeRequestBytes) - - sendWireData( - """HTTP/1.1 404 Not Found - |Server: akka-http/test - |Content-Length: 0 - | - |""") - - expectNetworkAbort() - expectInvalidUpgradeResponseCause("WebSocket server at ws://example.org/ws returned unexpected status code: 404 Not Found") - } - "missing Sec-WebSocket-Accept hash" in new TestSetup with ClientEchoes { - expectWireData(UpgradeRequestBytes) - - sendWireData( - """HTTP/1.1 101 Switching Protocols - |Upgrade: websocket - |Sec-WebSocket-Version: 13 - |Server: akka-http/test - |Connection: upgrade - | - |""") - - expectNetworkAbort() - expectInvalidUpgradeResponseCause("WebSocket server at ws://example.org/ws returned response that was missing required `Sec-WebSocket-Accept` header.") - } - "wrong Sec-WebSocket-Accept hash" in new TestSetup with ClientEchoes { - expectWireData(UpgradeRequestBytes) - - sendWireData( - """HTTP/1.1 101 Switching Protocols - |Upgrade: websocket - |Sec-WebSocket-Accept: s3pPLMBiTxhZRbK+xOo= - |Sec-WebSocket-Version: 13 - |Server: akka-http/test - |Connection: upgrade - | - |""") - - expectNetworkAbort() - expectInvalidUpgradeResponseCause("WebSocket server at ws://example.org/ws returned response with invalid `Sec-WebSocket-Accept` header.") - } - "missing `Upgrade` header" in new TestSetup with ClientEchoes { - expectWireData(UpgradeRequestBytes) - - sendWireData( - """HTTP/1.1 101 Switching Protocols - |Sec-WebSocket-Accept: ujmZX4KXZqjwy6vi1aQFH5p4Ygk= - |Sec-WebSocket-Version: 13 - |Server: akka-http/test - |Connection: upgrade - | - |""") - - expectNetworkAbort() - expectInvalidUpgradeResponseCause("WebSocket server at ws://example.org/ws returned response that was missing required `Upgrade` header.") - } - "missing `Connection: upgrade` header" in new TestSetup with ClientEchoes { - expectWireData(UpgradeRequestBytes) - - sendWireData( - """HTTP/1.1 101 Switching Protocols - |Upgrade: websocket - |Sec-WebSocket-Accept: ujmZX4KXZqjwy6vi1aQFH5p4Ygk= - |Sec-WebSocket-Version: 13 - |Server: akka-http/test - | - |""") - - expectNetworkAbort() - expectInvalidUpgradeResponseCause("WebSocket server at ws://example.org/ws returned response that was missing required `Connection` header.") - } - } - - "don't send out frames before handshake was finished successfully" in new TestSetup { - def clientImplementation: Flow[Message, Message, NotUsed] = - Flow.fromSinkAndSourceMat(Sink.ignore, Source.single(TextMessage("fast message")))(Keep.none) - - expectWireData(UpgradeRequestBytes) - expectNoWireData() - - sendWireData(UpgradeResponseBytes) - expectMaskedFrameOnNetwork(Protocol.Opcode.Text, ByteString("fast message"), fin = true) - - expectMaskedCloseFrame(Protocol.CloseCodes.Regular) - sendWSCloseFrame(Protocol.CloseCodes.Regular) - - closeNetworkInput() - expectNetworkClose() - } - "receive first frame in same chunk as HTTP upgrade response" in new TestSetup with ClientProbes { - expectWireData(UpgradeRequestBytes) - - val firstFrame = WSTestUtils.frame(Protocol.Opcode.Text, ByteString("fast"), fin = true, mask = false) - sendWireData(UpgradeResponseBytes ++ firstFrame) - - messagesIn.requestNext(TextMessage("fast")) - } - - "manual scenario client sends first" in new EstablishedConnectionSetup with ClientProbes { - messagesOut.sendNext(TextMessage("Message 1")) - - expectMaskedFrameOnNetwork(Protocol.Opcode.Text, ByteString("Message 1"), fin = true) - - sendWSFrame(Protocol.Opcode.Binary, ByteString("Response"), fin = true, mask = false) - - messagesIn.requestNext(BinaryMessage(ByteString("Response"))) - } - "client echoes scenario" in new EstablishedConnectionSetup with ClientEchoes { - sendWSFrame(Protocol.Opcode.Text, ByteString("Message 1"), fin = true) - expectMaskedFrameOnNetwork(Protocol.Opcode.Text, ByteString("Message 1"), fin = true) - sendWSFrame(Protocol.Opcode.Text, ByteString("Message 2"), fin = true) - expectMaskedFrameOnNetwork(Protocol.Opcode.Text, ByteString("Message 2"), fin = true) - sendWSFrame(Protocol.Opcode.Text, ByteString("Message 3"), fin = true) - expectMaskedFrameOnNetwork(Protocol.Opcode.Text, ByteString("Message 3"), fin = true) - sendWSFrame(Protocol.Opcode.Text, ByteString("Message 4"), fin = true) - expectMaskedFrameOnNetwork(Protocol.Opcode.Text, ByteString("Message 4"), fin = true) - sendWSFrame(Protocol.Opcode.Text, ByteString("Message 5"), fin = true) - expectMaskedFrameOnNetwork(Protocol.Opcode.Text, ByteString("Message 5"), fin = true) - - sendWSCloseFrame(Protocol.CloseCodes.Regular) - expectMaskedCloseFrame(Protocol.CloseCodes.Regular) - - closeNetworkInput() - expectNetworkClose() - } - "support subprotocols" - { - "accept if server supports subprotocol" in new TestSetup with ClientEchoes { - override protected def requestedSubProtocol: Option[String] = Some("v2") - - expectWireData( - """GET /ws HTTP/1.1 - |Upgrade: websocket - |Connection: upgrade - |Sec-WebSocket-Key: YLQguzhR2dR6y5M9vnA5mw== - |Sec-WebSocket-Version: 13 - |Sec-WebSocket-Protocol: v2 - |Host: example.org - |User-Agent: akka-http/test - | - |""") - sendWireData( - """HTTP/1.1 101 Switching Protocols - |Upgrade: websocket - |Sec-WebSocket-Accept: ujmZX4KXZqjwy6vi1aQFH5p4Ygk= - |Sec-WebSocket-Version: 13 - |Server: akka-http/test - |Connection: upgrade - |Sec-WebSocket-Protocol: v2 - | - |""") - - sendWSFrame(Protocol.Opcode.Text, ByteString("Message 1"), fin = true) - expectMaskedFrameOnNetwork(Protocol.Opcode.Text, ByteString("Message 1"), fin = true) - } - "send error on user flow if server doesn't support subprotocol" - { - "if no protocol was selected" in new TestSetup with ClientProbes { - override protected def requestedSubProtocol: Option[String] = Some("v2") - - expectWireData( - """GET /ws HTTP/1.1 - |Upgrade: websocket - |Connection: upgrade - |Sec-WebSocket-Key: YLQguzhR2dR6y5M9vnA5mw== - |Sec-WebSocket-Version: 13 - |Sec-WebSocket-Protocol: v2 - |Host: example.org - |User-Agent: akka-http/test - | - |""") - sendWireData( - """HTTP/1.1 101 Switching Protocols - |Upgrade: websocket - |Sec-WebSocket-Accept: ujmZX4KXZqjwy6vi1aQFH5p4Ygk= - |Sec-WebSocket-Version: 13 - |Server: akka-http/test - |Connection: upgrade - | - |""") - - expectNetworkAbort() - expectInvalidUpgradeResponseCause( - "WebSocket server at ws://example.org/ws returned response that indicated that the given subprotocol was not supported. (client supported: v2, server supported: None)") - } - "if different protocol was selected" in new TestSetup with ClientProbes { - override protected def requestedSubProtocol: Option[String] = Some("v2") - - expectWireData( - """GET /ws HTTP/1.1 - |Upgrade: websocket - |Connection: upgrade - |Sec-WebSocket-Key: YLQguzhR2dR6y5M9vnA5mw== - |Sec-WebSocket-Version: 13 - |Sec-WebSocket-Protocol: v2 - |Host: example.org - |User-Agent: akka-http/test - | - |""") - sendWireData( - """HTTP/1.1 101 Switching Protocols - |Upgrade: websocket - |Sec-WebSocket-Accept: ujmZX4KXZqjwy6vi1aQFH5p4Ygk= - |Sec-WebSocket-Protocol: v3 - |Sec-WebSocket-Version: 13 - |Server: akka-http/test - |Connection: upgrade - | - |""") - - expectNetworkAbort() - expectInvalidUpgradeResponseCause( - "WebSocket server at ws://example.org/ws returned response that indicated that the given subprotocol was not supported. (client supported: v2, server supported: Some(v3))") - } - } - } - } - - def UpgradeRequestBytes = ByteString { - """GET /ws HTTP/1.1 - |Upgrade: websocket - |Connection: upgrade - |Sec-WebSocket-Key: YLQguzhR2dR6y5M9vnA5mw== - |Sec-WebSocket-Version: 13 - |Host: example.org - |User-Agent: akka-http/test - | - |""".stripMarginWithNewline("\r\n") - } - - def UpgradeResponseBytes = ByteString { - """HTTP/1.1 101 Switching Protocols - |Upgrade: websocket - |Sec-WebSocket-Accept: ujmZX4KXZqjwy6vi1aQFH5p4Ygk= - |Server: akka-http/test - |Sec-WebSocket-Version: 13 - |Connection: upgrade - | - |""".stripMarginWithNewline("\r\n") - } - - abstract class EstablishedConnectionSetup extends TestSetup { - expectWireData(UpgradeRequestBytes) - sendWireData(UpgradeResponseBytes) - } - - abstract class TestSetup extends WSTestSetupBase { - protected def noMsgTimeout: FiniteDuration = 100.millis - protected def clientImplementation: Flow[Message, Message, NotUsed] - protected def requestedSubProtocol: Option[String] = None - - val random = new Random(0) - def settings = ClientConnectionSettings(system) - .withUserAgentHeader(Some(`User-Agent`(List(ProductVersion("akka-http", "test"))))) - .withWebsocketRandomFactory(() ⇒ random) - - def targetUri: Uri = "ws://example.org/ws" - - def clientLayer: Http.WebSocketClientLayer = - Http(system).webSocketClientLayer( - WebSocketRequest(targetUri, subprotocol = requestedSubProtocol), - settings = settings) - - val (netOut, netIn, response) = { - val netOut = ByteStringSinkProbe() - val netIn = TestPublisher.probe[ByteString]() - - val graph = - RunnableGraph.fromGraph(GraphDSL.create(clientLayer) { implicit b ⇒ client ⇒ - import GraphDSL.Implicits._ - Source.fromPublisher(netIn) ~> Flow[ByteString].map(SessionBytes(null, _)) ~> client.in2 - client.out1 ~> Flow[SslTlsOutbound].collect { case SendBytes(x) ⇒ x } ~> netOut.sink - client.out2 ~> clientImplementation ~> client.in1 - ClosedShape - }) - - val response = graph.run() - - (netOut, netIn, response) - } - def expectBytes(length: Int): ByteString = netOut.expectBytes(length) - def expectBytes(bytes: ByteString): Unit = netOut.expectBytes(bytes) - - def wipeDate(string: String) = - string.fastSplit('\n').map { - case s if s.startsWith("Date:") ⇒ "Date: XXXX\r" - case s ⇒ s - }.mkString("\n") - - def sendWireData(data: String): Unit = sendWireData(ByteString(data.stripMarginWithNewline("\r\n"), "ASCII")) - def sendWireData(data: ByteString): Unit = netIn.sendNext(data) - - def send(bytes: ByteString): Unit = sendWireData(bytes) - - def expectWireData(s: String) = - netOut.expectUtf8EncodedString(s.stripMarginWithNewline("\r\n")) - def expectWireData(bs: ByteString) = netOut.expectBytes(bs) - def expectNoWireData() = netOut.expectNoBytes(noMsgTimeout) - - def expectNetworkClose(): Unit = { - netOut.request(1) - netOut.expectComplete() - } - def expectNetworkAbort(): Unit = netOut.expectError() - def closeNetworkInput(): Unit = netIn.sendComplete() - - def expectResponse(response: WebSocketUpgradeResponse): Unit = - expectInvalidUpgradeResponse() shouldEqual response - def expectInvalidUpgradeResponseCause(expected: String): Unit = - expectInvalidUpgradeResponse().cause shouldEqual expected - - import akka.http.impl.util._ - def expectInvalidUpgradeResponse(): InvalidUpgradeResponse = - response.awaitResult(1.second).asInstanceOf[InvalidUpgradeResponse] - } - - trait ClientEchoes extends TestSetup { - override def clientImplementation: Flow[Message, Message, NotUsed] = echoServer - def echoServer: Flow[Message, Message, NotUsed] = Flow[Message] - } - trait ClientProbes extends TestSetup { - lazy val messagesOut = TestPublisher.probe[Message]() - lazy val messagesIn = TestSubscriber.probe[Message]() - - override def clientImplementation: Flow[Message, Message, NotUsed] = - Flow.fromSinkAndSourceMat(Sink.fromSubscriber(messagesIn), Source.fromPublisher(messagesOut))(Keep.none) - } -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/ws/WebSocketIntegrationSpec.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/ws/WebSocketIntegrationSpec.scala deleted file mode 100644 index 10241c6a11..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/ws/WebSocketIntegrationSpec.scala +++ /dev/null @@ -1,217 +0,0 @@ -/** - * Copyright (C) 2015-2016 Lightbend Inc. - */ -package akka.http.impl.engine.ws - -import scala.concurrent.{ Await, Future, Promise } -import scala.concurrent.duration.DurationInt -import org.scalactic.ConversionCheckedTripleEquals -import org.scalatest.concurrent.ScalaFutures -import org.scalatest.time.Span.convertDurationToSpan -import akka.http.scaladsl.Http -import akka.http.scaladsl.model.HttpRequest -import akka.http.scaladsl.model.Uri.apply -import akka.http.scaladsl.model.ws._ -import akka.stream._ -import akka.stream.scaladsl._ -import akka.stream.testkit._ -import akka.stream.scaladsl.GraphDSL.Implicits._ -import org.scalatest.concurrent.Eventually -import java.net.InetSocketAddress - -import akka.Done -import akka.http.scaladsl.settings.ClientConnectionSettings -import akka.stream.impl.fusing.GraphStages -import akka.stream.stage.{ GraphStageLogic, GraphStageWithMaterializedValue, InHandler, OutHandler } -import akka.util.ByteString -import akka.stream.testkit.scaladsl.TestSink -import akka.testkit.{ AkkaSpec, EventFilter } - -import scala.util.{ Failure, Success } - -class WebSocketIntegrationSpec extends AkkaSpec("akka.stream.materializer.debug.fuzzing-mode=off") - with Eventually { - - implicit val materializer = ActorMaterializer() - - "A WebSocket server" must { - - "not reset the connection when no data are flowing" in Utils.assertAllStagesStopped { - val source = TestPublisher.probe[Message]() - val bindingFuture = Http().bindAndHandleSync({ - case HttpRequest(_, _, headers, _, _) ⇒ - val upgrade = headers.collectFirst { case u: UpgradeToWebSocket ⇒ u }.get - upgrade.handleMessages(Flow.fromSinkAndSource(Sink.ignore, Source.fromPublisher(source)), None) - }, interface = "localhost", port = 0) - val binding = Await.result(bindingFuture, 3.seconds) - val myPort = binding.localAddress.getPort - - val (response, sink) = Http().singleWebSocketRequest( - WebSocketRequest("ws://127.0.0.1:" + myPort), - Flow.fromSinkAndSourceMat(TestSink.probe[Message], Source.empty)(Keep.left)) - - response.futureValue.response.status.isSuccess should ===(true) - sink - .request(10) - .expectNoMsg(500.millis) - - source - .sendNext(TextMessage("hello")) - .sendComplete() - sink - .expectNext(TextMessage("hello")) - .expectComplete() - - binding.unbind() - } - - "not reset the connection when no data are flowing and the connection is closed from the client" in Utils.assertAllStagesStopped { - val source = TestPublisher.probe[Message]() - val bindingFuture = Http().bindAndHandleSync({ - case HttpRequest(_, _, headers, _, _) ⇒ - val upgrade = headers.collectFirst { case u: UpgradeToWebSocket ⇒ u }.get - upgrade.handleMessages(Flow.fromSinkAndSource(Sink.ignore, Source.fromPublisher(source)), None) - }, interface = "localhost", port = 0) - val binding = Await.result(bindingFuture, 3.seconds) - val myPort = binding.localAddress.getPort - - val completeOnlySwitch: Flow[ByteString, ByteString, Promise[Done]] = Flow.fromGraph( - new GraphStageWithMaterializedValue[FlowShape[ByteString, ByteString], Promise[Done]] { - override val shape: FlowShape[ByteString, ByteString] = - FlowShape(Inlet("completeOnlySwitch.in"), Outlet("completeOnlySwitch.out")) - - override def createLogicAndMaterializedValue(inheritedAttributes: Attributes): (GraphStageLogic, Promise[Done]) = { - val promise = Promise[Done] - - val logic = new GraphStageLogic(shape) with InHandler with OutHandler { - override def onPush(): Unit = push(shape.out, grab(shape.in)) - override def onPull(): Unit = pull(shape.in) - - override def preStart(): Unit = { - promise.future.foreach(_ ⇒ getAsyncCallback[Done](_ ⇒ complete(shape.out)).invoke(Done))(akka.dispatch.ExecutionContexts.sameThreadExecutionContext) - } - - setHandlers(shape.in, shape.out, this) - } - - (logic, promise) - } - }) - - val ((response, breaker), sink) = - Source.empty - .viaMat { - Http().webSocketClientLayer(WebSocketRequest("ws://localhost:" + myPort)) - .atop(TLSPlacebo()) - .joinMat(completeOnlySwitch.via( - Tcp().outgoingConnection(new InetSocketAddress("localhost", myPort), halfClose = true)))(Keep.both) - }(Keep.right) - .toMat(TestSink.probe[Message])(Keep.both) - .run() - - response.futureValue.response.status.isSuccess should ===(true) - sink - .request(10) - .expectNoMsg(1500.millis) - - breaker.trySuccess(Done) - - source - .sendNext(TextMessage("hello")) - .sendComplete() - sink - .expectNext(TextMessage("hello")) - .expectComplete() - - binding.unbind() - } - - "echo 100 elements and then shut down without error" in Utils.assertAllStagesStopped { - - val bindingFuture = Http().bindAndHandleSync({ - case HttpRequest(_, _, headers, _, _) ⇒ - val upgrade = headers.collectFirst { case u: UpgradeToWebSocket ⇒ u }.get - upgrade.handleMessages(Flow.apply, None) - }, interface = "localhost", port = 0) - val binding = Await.result(bindingFuture, 3.seconds) - val myPort = binding.localAddress.getPort - - val N = 100 - - EventFilter.warning(pattern = "HTTP header .* is not allowed in responses", occurrences = 0) intercept { - val (response, count) = Http().singleWebSocketRequest( - WebSocketRequest("ws://127.0.0.1:" + myPort), - Flow.fromSinkAndSourceMat( - Sink.fold(0)((n, _: Message) ⇒ n + 1), - Source.repeat(TextMessage("hello")).take(N))(Keep.left)) - count.futureValue should ===(N) - } - - binding.unbind() - } - - "send back 100 elements and then terminate without error even when not ordinarily closed" in Utils.assertAllStagesStopped { - val N = 100 - - val handler = Flow.fromGraph(GraphDSL.create() { implicit b ⇒ - val merge = b.add(Merge[Int](2)) - - // convert to int so we can connect to merge - val mapMsgToInt = b.add(Flow[Message].map(_ ⇒ -1)) - val mapIntToMsg = b.add(Flow[Int].map(x ⇒ TextMessage.Strict(s"Sending: $x"))) - - // source we want to use to send message to the connected websocket sink - val rangeSource = b.add(Source(1 to N)) - - mapMsgToInt ~> merge // this part of the merge will never provide msgs - rangeSource ~> merge ~> mapIntToMsg - - FlowShape(mapMsgToInt.in, mapIntToMsg.out) - }) - - val bindingFuture = Http().bindAndHandleSync({ - case HttpRequest(_, _, headers, _, _) ⇒ - val upgrade = headers.collectFirst { case u: UpgradeToWebSocket ⇒ u }.get - upgrade.handleMessages(handler, None) - }, interface = "localhost", port = 0) - val binding = Await.result(bindingFuture, 3.seconds) - val myPort = binding.localAddress.getPort - - @volatile var messages = 0 - val (switch, completion) = - Source.maybe - .viaMat { - Http().webSocketClientLayer(WebSocketRequest("ws://localhost:" + myPort)) - .atop(TLSPlacebo()) - // the resource leak of #19398 existed only for severed websocket connections - .atopMat(KillSwitches.singleBidi[ByteString, ByteString])(Keep.right) - .join(Tcp().outgoingConnection(new InetSocketAddress("localhost", myPort), halfClose = true)) - }(Keep.right) - .toMat(Sink.foreach(_ ⇒ messages += 1))(Keep.both) - .run() - eventually(messages should ===(N)) - // breaker should have been fulfilled long ago - switch.shutdown() - completion.futureValue - - binding.unbind() - } - - } - - "A websocket client" should { - "fail the materialized future if the request fails" in { - val flow = Http().webSocketClientFlow( - WebSocketRequest("ws://127.0.0.1:65535/no/server/here"), - settings = ClientConnectionSettings(system).withConnectingTimeout(250.millis)) - - val future = Source.maybe[Message].viaMat(flow)(Keep.right).toMat(Sink.ignore)(Keep.left).run() - import system.dispatcher - whenReady(future.map(r ⇒ Success(r)).recover { case ex ⇒ Failure(ex) }) { resTry ⇒ - resTry.isFailure should ===(true) - resTry.failed.get.getMessage should ===("Connection failed.") - } - } - } - -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/ws/WebSocketServerSpec.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/ws/WebSocketServerSpec.scala deleted file mode 100644 index 225ee3f1ec..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/ws/WebSocketServerSpec.scala +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.ws - -import akka.http.scaladsl.model.ws._ -import akka.stream.scaladsl.{ Keep, Sink, Flow, Source } -import akka.stream.testkit.Utils -import akka.util.ByteString -import org.scalatest.{ Matchers, FreeSpec } - -import akka.http.impl.engine.server.HttpServerTestSetupBase - -class WebSocketServerSpec extends FreeSpec with Matchers with WithMaterializerSpec { spec ⇒ - - "The server-side WebSocket integration should" - { - "establish a websocket connection when the user requests it" - { - "when user handler instantly tries to send messages" in Utils.assertAllStagesStopped { - new TestSetup { - send( - """GET /chat HTTP/1.1 - |Host: server.example.com - |Upgrade: websocket - |Connection: Upgrade - |Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== - |Origin: http://example.com - |Sec-WebSocket-Version: 13 - | - |""") - - val request = expectRequest() - val upgrade = request.header[UpgradeToWebSocket] - upgrade.isDefined shouldBe true - - val source = - Source(List(1, 2, 3, 4, 5)).map(num ⇒ TextMessage.Strict(s"Message $num")) - val handler = Flow.fromSinkAndSourceMat(Sink.ignore, source)(Keep.none) - val response = upgrade.get.handleMessages(handler) - responses.sendNext(response) - - expectResponseWithWipedDate( - """HTTP/1.1 101 Switching Protocols - |Upgrade: websocket - |Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= - |Server: akka-http/test - |Date: XXXX - |Connection: upgrade - | - |""") - - expectWSFrame(Protocol.Opcode.Text, ByteString("Message 1"), fin = true) - expectWSFrame(Protocol.Opcode.Text, ByteString("Message 2"), fin = true) - expectWSFrame(Protocol.Opcode.Text, ByteString("Message 3"), fin = true) - expectWSFrame(Protocol.Opcode.Text, ByteString("Message 4"), fin = true) - expectWSFrame(Protocol.Opcode.Text, ByteString("Message 5"), fin = true) - expectWSCloseFrame(Protocol.CloseCodes.Regular) - - sendWSCloseFrame(Protocol.CloseCodes.Regular, mask = true) - closeNetworkInput() - expectNetworkClose() - } - } - "for echoing user handler" in Utils.assertAllStagesStopped { - new TestSetup { - - send( - """GET /echo HTTP/1.1 - |Host: server.example.com - |Upgrade: websocket - |Connection: Upgrade - |Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== - |Origin: http://example.com - |Sec-WebSocket-Version: 13 - | - |""") - - val request = expectRequest() - val upgrade = request.header[UpgradeToWebSocket] - upgrade.isDefined shouldBe true - - val response = upgrade.get.handleMessages(Flow[Message]) // simple echoing - responses.sendNext(response) - - expectResponseWithWipedDate( - """HTTP/1.1 101 Switching Protocols - |Upgrade: websocket - |Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= - |Server: akka-http/test - |Date: XXXX - |Connection: upgrade - | - |""") - - sendWSFrame(Protocol.Opcode.Text, ByteString("Message 1"), fin = true, mask = true) - expectWSFrame(Protocol.Opcode.Text, ByteString("Message 1"), fin = true) - sendWSFrame(Protocol.Opcode.Text, ByteString("Message 2"), fin = true, mask = true) - expectWSFrame(Protocol.Opcode.Text, ByteString("Message 2"), fin = true) - sendWSFrame(Protocol.Opcode.Text, ByteString("Message 3"), fin = true, mask = true) - expectWSFrame(Protocol.Opcode.Text, ByteString("Message 3"), fin = true) - sendWSFrame(Protocol.Opcode.Text, ByteString("Message 4"), fin = true, mask = true) - expectWSFrame(Protocol.Opcode.Text, ByteString("Message 4"), fin = true) - sendWSFrame(Protocol.Opcode.Text, ByteString("Message 5"), fin = true, mask = true) - expectWSFrame(Protocol.Opcode.Text, ByteString("Message 5"), fin = true) - - sendWSCloseFrame(Protocol.CloseCodes.Regular, mask = true) - expectWSCloseFrame(Protocol.CloseCodes.Regular) - - closeNetworkInput() - expectNetworkClose() - } - } - } - "prevent the selection of an unavailable subprotocol" in pending - "reject invalid WebSocket handshakes" - { - "missing `Upgrade: websocket` header" in pending - "missing `Connection: upgrade` header" in pending - "missing `Sec-WebSocket-Key header" in pending - "`Sec-WebSocket-Key` with wrong amount of base64 encoded data" in pending - "missing `Sec-WebSocket-Version` header" in pending - "unsupported `Sec-WebSocket-Version`" in pending - } - } - - class TestSetup extends HttpServerTestSetupBase with WSTestSetupBase { - implicit def system = spec.system - implicit def materializer = spec.materializer - - def expectBytes(length: Int): ByteString = netOut.expectBytes(length) - def expectBytes(bytes: ByteString): Unit = netOut.expectBytes(bytes) - } -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/ws/WithMaterializerSpec.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/ws/WithMaterializerSpec.scala deleted file mode 100644 index e6b7627fe8..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/engine/ws/WithMaterializerSpec.scala +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.engine.ws - -import com.typesafe.config.{ ConfigFactory, Config } -import org.scalatest.{ Suite, BeforeAndAfterAll } -import akka.actor.ActorSystem -import akka.stream.ActorMaterializer - -trait WithMaterializerSpec extends BeforeAndAfterAll { _: Suite ⇒ - lazy val testConf: Config = ConfigFactory.parseString(""" - akka.event-handlers = ["akka.testkit.TestEventListener"] - akka.loglevel = WARNING""") - implicit lazy val system = ActorSystem(getClass.getSimpleName, testConf) - - implicit lazy val materializer = ActorMaterializer() - override def afterAll() = system.terminate() -} \ No newline at end of file diff --git a/akka-http-core/src/test/scala/akka/http/impl/model/parser/HttpHeaderSpec.scala b/akka-http-core/src/test/scala/akka/http/impl/model/parser/HttpHeaderSpec.scala deleted file mode 100644 index 38393a7f38..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/model/parser/HttpHeaderSpec.scala +++ /dev/null @@ -1,734 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.model.parser - -import akka.http.scaladsl.settings.ParserSettings.CookieParsingMode -import akka.http.impl.model.parser.HeaderParser.Settings -import org.scalatest.{ Matchers, FreeSpec } -import org.scalatest.matchers.{ Matcher, MatchResult } -import akka.http.impl.util._ -import akka.http.scaladsl.model._ -import headers._ -import CacheDirectives._ -import MediaTypes._ -import MediaRanges._ -import HttpCharsets._ -import HttpEncodings._ -import HttpMethods._ -import java.net.InetAddress - -class HttpHeaderSpec extends FreeSpec with Matchers { - val `application/vnd.spray` = MediaType.applicationBinary("vnd.spray", MediaType.Compressible) - val PROPFIND = HttpMethod.custom("PROPFIND") - - "The HTTP header model must correctly parse and render the headers" - { - - "Accept" in { - "Accept: audio/midi;q=0.2, audio/basic" =!= - Accept(`audio/midi` withQValue 0.2, `audio/basic`) - "Accept: text/plain;q=0.5, text/html,\r\n text/css;q=0.8" =!= - Accept(`text/plain` withQValue 0.5, `text/html`, `text/css` withQValue 0.8).renderedTo( - "text/plain;q=0.5, text/html, text/css;q=0.8") - "Accept: text/html, image/gif, image/jpeg, *;q=.2, */*;q=.2" =!= - Accept(`text/html`, `image/gif`, `image/jpeg`, `*/*` withQValue 0.2, `*/*` withQValue 0.2).renderedTo( - "text/html, image/gif, image/jpeg, */*;q=0.2, */*;q=0.2") - "Accept: application/vnd.spray" =!= - Accept(`application/vnd.spray`) - "Accept: */*, text/*; foo=bar, custom/custom; bar=\"b>az\"" =!= - Accept( - `*/*`, - MediaRange.custom("text", Map("foo" → "bar")), - MediaType.customBinary("custom", "custom", MediaType.Compressible, params = Map("bar" → "b>az"))) - "Accept: application/*+xml; version=2" =!= - Accept(MediaType.customBinary("application", "*+xml", MediaType.Compressible, params = Map("version" → "2"))) - } - - "Accept-Charset" in { - "Accept-Charset: *" =!= `Accept-Charset`(HttpCharsetRange.`*`) - "Accept-Charset: UTF-8" =!= `Accept-Charset`(`UTF-8`) - "Accept-Charset: utf16;q=1" =!= `Accept-Charset`(`UTF-16`).renderedTo("UTF-16") - "Accept-Charset: utf-8; q=0.5, *" =!= `Accept-Charset`(`UTF-8` withQValue 0.5, HttpCharsetRange.`*`).renderedTo("UTF-8;q=0.5, *") - "Accept-Charset: latin1, UTf-16; q=0, *;q=0.8" =!= - `Accept-Charset`(`ISO-8859-1`, `UTF-16` withQValue 0, HttpCharsetRange.`*` withQValue 0.8).renderedTo( - "ISO-8859-1, UTF-16;q=0.0, *;q=0.8") - `Accept-Charset`(`UTF-16` withQValue 0.234567).toString shouldEqual "Accept-Charset: UTF-16;q=0.235" - "Accept-Charset: UTF-16, unsupported42" =!= `Accept-Charset`(`UTF-16`, HttpCharset.custom("unsupported42")) - } - - "Access-Control-Allow-Credentials" in { - "Access-Control-Allow-Credentials: true" =!= `Access-Control-Allow-Credentials`(allow = true) - } - - "Access-Control-Allow-Headers" in { - "Access-Control-Allow-Headers: Accept, X-My-Header" =!= `Access-Control-Allow-Headers`("Accept", "X-My-Header") - } - - "Access-Control-Allow-Methods" in { - "Access-Control-Allow-Methods: GET, POST" =!= `Access-Control-Allow-Methods`(GET, POST) - "Access-Control-Allow-Methods: GET, PROPFIND, POST" =!= `Access-Control-Allow-Methods`(GET, PROPFIND, POST) - } - - "Access-Control-Allow-Origin" in { - "Access-Control-Allow-Origin: *" =!= `Access-Control-Allow-Origin`.`*` - "Access-Control-Allow-Origin: null" =!= `Access-Control-Allow-Origin`.`null` - "Access-Control-Allow-Origin: http://spray.io" =!= `Access-Control-Allow-Origin`("http://spray.io") - "Access-Control-Allow-Origin: http://akka.io http://spray.io" =!= - `Access-Control-Allow-Origin`.forRange(HttpOriginRange("http://akka.io", "http://spray.io")) - } - - "Access-Control-Expose-Headers" in { - "Access-Control-Expose-Headers: Accept, X-My-Header" =!= `Access-Control-Expose-Headers`("Accept", "X-My-Header") - } - - "Access-Control-Max-Age" in { - "Access-Control-Max-Age: 3600" =!= `Access-Control-Max-Age`(3600) - } - - "Access-Control-Request-Headers" in { - "Access-Control-Request-Headers: Accept, X-My-Header" =!= `Access-Control-Request-Headers`("Accept", "X-My-Header") - } - - "Access-Control-Request-Method" in { - "Access-Control-Request-Method: POST" =!= `Access-Control-Request-Method`(POST) - "Access-Control-Request-Method: PROPFIND" =!= `Access-Control-Request-Method`(PROPFIND) - } - - "Accept-Ranges" in { - "Accept-Ranges: bytes" =!= `Accept-Ranges`(RangeUnits.Bytes) - "Accept-Ranges: bytes, sausages" =!= `Accept-Ranges`(RangeUnits.Bytes, RangeUnits.Other("sausages")) - "Accept-Ranges: none" =!= `Accept-Ranges`() - } - - "Accept-Encoding" in { - "Accept-Encoding: compress, gzip, fancy" =!= - `Accept-Encoding`(compress, gzip, HttpEncoding.custom("fancy")) - "Accept-Encoding: gzip, identity;q=0.5, *;q=0.0" =!= - `Accept-Encoding`(gzip, identity withQValue 0.5, HttpEncodingRange.`*` withQValue 0) - .renderedTo("gzip, identity;q=0.5, *;q=0.0") - "Accept-Encoding: " =!= `Accept-Encoding`() - } - - "Accept-Language" in { - "Accept-Language: da, en-gb;q=0.8, en;q=0.7" =!= - `Accept-Language`(Language("da"), Language("en", "gb") withQValue 0.8f, Language("en") withQValue 0.7f) - "Accept-Language: de-CH-1901, *;q=0.0" =!= - `Accept-Language`(Language("de", "CH", "1901"), LanguageRange.`*` withQValue 0f) - "Accept-Language: es-419, es" =!= `Accept-Language`(Language("es", "419"), Language("es")) - } - - "Age" in { - "Age: 3600" =!= Age(3600) - } - - "Allow" in { - "Allow: " =!= Allow() - "Allow: GET, PUT" =!= Allow(GET, PUT) - "Allow: GET, PROPFIND, PUT" =!= Allow(GET, PROPFIND, PUT) - } - - "Authorization" in { - BasicHttpCredentials("Aladdin", "open sesame").token shouldEqual "QWxhZGRpbjpvcGVuIHNlc2FtZQ==" - "Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" =!= - Authorization(BasicHttpCredentials("Aladdin", "open sesame")) - "Authorization: bAsIc QWxhZGRpbjpvcGVuIHNlc2FtZQ==" =!= - Authorization(BasicHttpCredentials("Aladdin", "open sesame")).renderedTo( - "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==") - """Authorization: Fancy yes="n:o", nonce=42""" =!= - Authorization(GenericHttpCredentials("Fancy", Map("yes" → "n:o", "nonce" → "42"))).renderedTo( - """Fancy yes="n:o",nonce=42""") - """Authorization: Fancy yes=no,nonce="4\\2"""" =!= - Authorization(GenericHttpCredentials("Fancy", Map("yes" → "no", "nonce" → """4\2"""))) - """Authorization: Other yes=no,empty=""""" =!= - Authorization(GenericHttpCredentials("Other", Map("yes" → "no", "empty" → ""))) - "Authorization: Basic Qm9iOg==" =!= - Authorization(BasicHttpCredentials("Bob", "")) - """Authorization: Digest name=Bob""" =!= - Authorization(GenericHttpCredentials("Digest", Map("name" → "Bob"))) - """Authorization: Bearer mF_9.B5f-4.1JqM/""" =!= - Authorization(OAuth2BearerToken("mF_9.B5f-4.1JqM/")) - "Authorization: NoParamScheme" =!= - Authorization(GenericHttpCredentials("NoParamScheme", Map.empty[String, String])) - "Authorization: QVFJQzV3TTJMWTRTZmN3Zk=" =!= - ErrorInfo( - "Illegal HTTP header 'Authorization': Invalid input '=', expected auth-param, OWS, token68, 'EOI' or tchar (line 1, column 23)", - """QVFJQzV3TTJMWTRTZmN3Zk= - | ^""".stripMarginWithNewline("\n")) - } - - "Cache-Control" in { - "Cache-Control: no-cache, max-age=0" =!= - `Cache-Control`(`no-cache`, `max-age`(0)) - "Cache-Control: private=\"Some-Field\"" =!= - `Cache-Control`(`private`("Some-Field")) - "Cache-Control: private, community=\"\"" =!= - `Cache-Control`(`private`(), CacheDirective.custom("community", Some(""))) - "Cache-Control: max-age=1234567890123456789" =!= - `Cache-Control`(`max-age`(Int.MaxValue)).renderedTo("max-age=2147483647") - "Cache-Control: private, no-cache, no-cache=Set-Cookie, proxy-revalidate" =!= - `Cache-Control`(`private`(), `no-cache`, `no-cache`("Set-Cookie"), `proxy-revalidate`).renderedTo( - "private, no-cache, no-cache=\"Set-Cookie\", proxy-revalidate") - "Cache-Control: no-cache=Set-Cookie" =!= - `Cache-Control`(`no-cache`("Set-Cookie")).renderedTo("no-cache=\"Set-Cookie\"") - "Cache-Control: private=\"a,b\", no-cache" =!= - `Cache-Control`(`private`("a", "b"), `no-cache`) - } - - "Connection" in { - "Connection: close" =!= Connection("close") - "Connection: pipapo, close" =!= Connection("pipapo", "close") - } - - "Content-Disposition" in { - "Content-Disposition: form-data" =!= `Content-Disposition`(ContentDispositionTypes.`form-data`) - "Content-Disposition: attachment; name=field1; filename=\"file/txt\"" =!= - `Content-Disposition`(ContentDispositionTypes.attachment, Map("name" → "field1", "filename" → "file/txt")) - "Content-Disposition: attachment; name=field1; other=\"\"" =!= - `Content-Disposition`(ContentDispositionTypes.attachment, Map("name" → "field1", "other" → "")) - } - - "Content-Encoding" in { - "Content-Encoding: gzip" =!= `Content-Encoding`(gzip) - "Content-Encoding: compress, pipapo" =!= `Content-Encoding`(compress, HttpEncoding.custom("pipapo")) - } - - "Content-Length" in { - "Content-Length: 42" =!= `Content-Length`(42) - "Content-Length: 12345678901234567890123456789" =!= `Content-Length`(999999999999999999L) - .renderedTo("999999999999999999") - } - - "Content-Type" in { - "Content-Type: application/pdf" =!= - `Content-Type`(`application/pdf`) - "Content-Type: application/json" =!= - `Content-Type`(`application/json`) - "Content-Type: text/plain; charset=utf8" =!= - `Content-Type`(ContentType(`text/plain`, `UTF-8`)).renderedTo("text/plain; charset=UTF-8") - "Content-Type: text/xml2; version=3; charset=windows-1252" =!= - `Content-Type`(MediaType.customWithOpenCharset("text", "xml2", params = Map("version" → "3")) - withCharset HttpCharsets.getForKey("windows-1252").get) - "Content-Type: text/plain; charset=fancy-pants" =!= - `Content-Type`(`text/plain` withCharset HttpCharset.custom("fancy-pants")) - "Content-Type: multipart/mixed; boundary=ABC123" =!= - `Content-Type`(`multipart/mixed` withBoundary "ABC123" withCharset `UTF-8`) - .renderedTo("multipart/mixed; boundary=ABC123; charset=UTF-8") - "Content-Type: multipart/mixed; boundary=\"ABC/123\"" =!= - `Content-Type`(`multipart/mixed` withBoundary "ABC/123" withCharset `UTF-8`) - .renderedTo("""multipart/mixed; boundary="ABC/123"; charset=UTF-8""") - "Content-Type: application/*" =!= - `Content-Type`(MediaType.customBinary("application", "*", MediaType.Compressible, allowArbitrarySubtypes = true)) - } - - "Content-Range" in { - "Content-Range: bytes 42-1233/1234" =!= `Content-Range`(ContentRange(42, 1233, 1234)) - "Content-Range: bytes 42-1233/*" =!= `Content-Range`(ContentRange(42, 1233)) - "Content-Range: bytes */1234" =!= `Content-Range`(ContentRange.Unsatisfiable(1234)) - "Content-Range: bytes */12345678901234567890123456789" =!= `Content-Range`(ContentRange.Unsatisfiable(999999999999999999L)) - .renderedTo("bytes */999999999999999999") - } - - "Cookie (RFC 6265)" in { - "Cookie: SID=31d4d96e407aad42" =!= Cookie("SID" → "31d4d96e407aad42") - "Cookie: SID=31d4d96e407aad42; lang=en>US" =!= Cookie("SID" → "31d4d96e407aad42", "lang" → "en>US") - "Cookie: a=1; b=2" =!= Cookie("a" → "1", "b" → "2") - "Cookie: a=1;b=2" =!= Cookie("a" → "1", "b" → "2").renderedTo("a=1; b=2") - "Cookie: a=1 ;b=2" =!= Cookie("a" → "1", "b" → "2").renderedTo("a=1; b=2") - - "Cookie: z=0;a=1,b=2" =!= Cookie("z" → "0").renderedTo("z=0") - """Cookie: a=1;b="test"""" =!= Cookie("a" → "1", "b" → "test").renderedTo("a=1; b=test") - - "Cookie: a=1; b=f\"d\"c\"; c=xyz" =!= Cookie("a" → "1", "c" → "xyz").renderedTo("a=1; c=xyz") - "Cookie: a=1; b=ä; c=d" =!= Cookie("a" → "1", "c" → "d").renderedTo("a=1; c=d") - - "Cookie: a=1,2" =!= - ErrorInfo( - "Illegal HTTP header 'Cookie'", - "Cookie header contained no parsable cookie values.") - } - - "Cookie (Raw)" in { - "Cookie: SID=31d4d96e407aad42" =!= Cookie("SID" → "31d4d96e407aad42").withCookieParsingMode(CookieParsingMode.Raw) - "Cookie: SID=31d4d96e407aad42; lang=en>US" =!= Cookie("SID" → "31d4d96e407aad42", "lang" → "en>US").withCookieParsingMode(CookieParsingMode.Raw) - "Cookie: a=1; b=2" =!= Cookie("a" → "1", "b" → "2").withCookieParsingMode(CookieParsingMode.Raw) - "Cookie: a=1;b=2" =!= Cookie("a" → "1", "b" → "2").renderedTo("a=1; b=2").withCookieParsingMode(CookieParsingMode.Raw) - "Cookie: a=1 ;b=2" =!= Cookie(List(HttpCookiePair.raw("a" → "1 "), HttpCookiePair("b" → "2"))).renderedTo("a=1 ; b=2").withCookieParsingMode(CookieParsingMode.Raw) - - "Cookie: z=0; a=1,b=2" =!= Cookie(List(HttpCookiePair("z" → "0"), HttpCookiePair.raw("a" → "1,b=2"))).withCookieParsingMode(CookieParsingMode.Raw) - """Cookie: a=1;b="test"""" =!= Cookie(List(HttpCookiePair("a" → "1"), HttpCookiePair.raw("b" → "\"test\""))).renderedTo("a=1; b=\"test\"").withCookieParsingMode(CookieParsingMode.Raw) - "Cookie: a=1; b=f\"d\"c\"; c=xyz" =!= Cookie(List(HttpCookiePair("a" → "1"), HttpCookiePair.raw("b" → "f\"d\"c\""), HttpCookiePair("c" → "xyz"))).withCookieParsingMode(CookieParsingMode.Raw) - "Cookie: a=1; b=ä; c=d" =!= Cookie(List(HttpCookiePair("a" → "1"), HttpCookiePair.raw("b" → "ä"), HttpCookiePair("c" → "d"))).withCookieParsingMode(CookieParsingMode.Raw) - } - - "Date" in { - "Date: Wed, 13 Jul 2011 08:12:31 GMT" =!= Date(DateTime(2011, 7, 13, 8, 12, 31)) - "Date: Wed, 13-Jul-2011 08:12:31 GMT" =!= Date(DateTime(2011, 7, 13, 8, 12, 31)).renderedTo( - "Wed, 13 Jul 2011 08:12:31 GMT") - "Date: Wed, 13-Jul-11 08:12:31 GMT" =!= Date(DateTime(2011, 7, 13, 8, 12, 31)).renderedTo( - "Wed, 13 Jul 2011 08:12:31 GMT") - "Date: Mon, 13-Jul-70 08:12:31 GMT" =!= Date(DateTime(1970, 7, 13, 8, 12, 31)).renderedTo( - "Mon, 13 Jul 1970 08:12:31 GMT") - "Date: Fri, 23 Mar 1804 12:11:10 UTC" =!= Date(DateTime(1804, 3, 23, 12, 11, 10)).renderedTo( - "Fri, 23 Mar 1804 12:11:10 GMT") - } - - "ETag" in { - """ETag: "938fz3f83z3z38z"""" =!= ETag("938fz3f83z3z38z", weak = false) - """ETag: W/"938fz3f83z3z38z"""" =!= ETag("938fz3f83z3z38z", weak = true) - } - - "Expect" in { - "Expect: 100-continue" =!= Expect.`100-continue` - } - - "Expires" in { - "Expires: Wed, 13 Jul 2011 08:12:31 GMT" =!= Expires(DateTime(2011, 7, 13, 8, 12, 31)) - "Expires: 0" =!= Expires(DateTime.MinValue).renderedTo("Wed, 01 Jan 1800 00:00:00 GMT") - "Expires: -1" =!= Expires(DateTime.MinValue).renderedTo("Wed, 01 Jan 1800 00:00:00 GMT") - "Expires: " =!= Expires(DateTime.MinValue).renderedTo("Wed, 01 Jan 1800 00:00:00 GMT") - "Expires: batman" =!= Expires(DateTime.MinValue).renderedTo("Wed, 01 Jan 1800 00:00:00 GMT") - } - - "Host" in { - "Host: www.spray.io:8080" =!= Host("www.spray.io", 8080) - "Host: spray.io" =!= Host("spray.io") - "Host: [2001:db8::1]:8080" =!= Host("[2001:db8::1]", 8080) - "Host: [2001:db8::1]" =!= Host("[2001:db8::1]") - "Host: [::FFFF:129.144.52.38]" =!= Host("[::FFFF:129.144.52.38]") - "Host: spray.io:80000" =!= ErrorInfo("Illegal HTTP header 'Host': requirement failed", "Illegal port: 80000") - "Host: 127.0.0.1:9000" =!= Host("127.0.0.1", 9000) - "Host: 127.0.0.1" =!= Host("127.0.0.1") - } - - "If-Match" in { - """If-Match: *""" =!= `If-Match`.`*` - """If-Match: "938fz3f83z3z38z"""" =!= `If-Match`(EntityTag("938fz3f83z3z38z")) - """If-Match: "938fz3f83z3z38z", "0293f34hhv0nc"""" =!= - `If-Match`(EntityTag("938fz3f83z3z38z"), EntityTag("0293f34hhv0nc")) - } - - "If-Modified-Since" in { - "If-Modified-Since: Wed, 13 Jul 2011 08:12:31 GMT" =!= `If-Modified-Since`(DateTime(2011, 7, 13, 8, 12, 31)) - "If-Modified-Since: 0" =!= `If-Modified-Since`(DateTime.MinValue).renderedTo("Wed, 01 Jan 1800 00:00:00 GMT") - } - - "If-None-Match" in { - """If-None-Match: *""" =!= `If-None-Match`.`*` - """If-None-Match: "938fz3f83z3z38z"""" =!= `If-None-Match`(EntityTag("938fz3f83z3z38z")) - """If-None-Match: "938fz3f83z3z38z", "0293f34hhv0nc"""" =!= - `If-None-Match`(EntityTag("938fz3f83z3z38z"), EntityTag("0293f34hhv0nc")) - """If-None-Match: W/"938fz3f83z3z38z"""" =!= `If-None-Match`(EntityTag("938fz3f83z3z38z", weak = true)) - } - - "If-Range" in { - """If-Range: "abcdefg"""" =!= `If-Range`(Left(EntityTag("abcdefg"))) - """If-Range: Wed, 13 Jul 2011 08:12:31 GMT""" =!= `If-Range`(Right(DateTime(2011, 7, 13, 8, 12, 31))) - } - - "If-Unmodified-Since" in { - "If-Unmodified-Since: Wed, 13 Jul 2011 08:12:31 GMT" =!= `If-Unmodified-Since`(DateTime(2011, 7, 13, 8, 12, 31)) - } - - "Last-Modified" in { - "Last-Modified: Wed, 13 Jul 2011 08:12:31 GMT" =!= `Last-Modified`(DateTime(2011, 7, 13, 8, 12, 31)) - } - - "Location" in { - "Location: https://spray.io/secure" =!= Location(Uri("https://spray.io/secure")) - "Location: /en-us/default.aspx" =!= Location(Uri("/en-us/default.aspx")) - "Location: https://spray.io/{sec}" =!= Location(Uri("https://spray.io/{sec}")).renderedTo( - "https://spray.io/%7Bsec%7D") - "Location: https://spray.io/ sec" =!= ErrorInfo("Illegal HTTP header 'Location': Invalid input ' ', " + - "expected '/', 'EOI', '#', segment or '?' (line 1, column 18)", "https://spray.io/ sec\n ^") - } - - "Link" in { - "Link: ; rel=next" =!= Link(Uri("/?page=2"), LinkParams.next) - "Link: ; rel=next" =!= Link(Uri("https://spray.io"), LinkParams.next) - """Link: ; rel=prev, ; rel="next"""" =!= - Link(LinkValue(Uri("/"), LinkParams.prev), LinkValue(Uri("/page/2"), LinkParams.next)).renderedTo("; rel=prev, ; rel=next") - - """Link: ; rel="x.y-z http://spray.io"""" =!= Link(Uri("/"), LinkParams.rel("x.y-z http://spray.io")) - """Link: ; title="My Title"""" =!= Link(Uri("/"), LinkParams.title("My Title")) - """Link: ; rel=next; title="My Title"""" =!= Link(Uri("/"), LinkParams.next, LinkParams.title("My Title")) - """Link: ; anchor="http://example.com"""" =!= Link(Uri("/"), LinkParams.anchor(Uri("http://example.com"))) - """Link: ; rev=foo; hreflang=de-de; media=print; type=application/json""" =!= - Link(Uri("/"), LinkParams.rev("foo"), LinkParams.hreflang(Language("de", "de")), LinkParams.media("print"), LinkParams.`type`(`application/json`)) - - /* RFC 5988 examples */ - """Link: ; rel="previous"; title="previous chapter"""" =!= - Link(Uri("http://example.com/TheBook/chapter2"), LinkParams.rel("previous"), LinkParams.title("previous chapter")) - .renderedTo("""; rel=previous; title="previous chapter"""") - - """Link: ; rel="http://example.net/foo"""" =!= Link(Uri("/"), LinkParams.rel("http://example.net/foo")) - .renderedTo("; rel=http://example.net/foo") - - """Link: ; rel="start http://example.net/relation/other"""" =!= Link( - Uri("http://example.org/"), - LinkParams.rel("start http://example.net/relation/other")) - - // only one 'rel=' is allowed, http://tools.ietf.org/html/rfc5988#section-5.3 requires any subsequent ones to be skipped - "Link: ; rel=prev; rel=next" =!=> "; rel=prev" - } - - "Origin" in { - "Origin: null" =!= Origin(Nil) - "Origin: http://spray.io" =!= Origin("http://spray.io") - } - - "Proxy-Authenticate" in { - "Proxy-Authenticate: Basic realm=\"WallyWorld\",attr=\"val>ue\", Fancy realm=\"yeah\"" =!= - `Proxy-Authenticate`(HttpChallenge("Basic", Some("WallyWorld"), Map("attr" → "val>ue")), HttpChallenge("Fancy", Some("yeah"))) - } - - "Proxy-Authorization" in { - """Proxy-Authorization: Fancy yes=no,nonce="4\\2"""" =!= - `Proxy-Authorization`(GenericHttpCredentials("Fancy", Map("yes" → "no", "nonce" → """4\2"""))) - } - - "Referer" in { - "Referer: https://spray.io/secure" =!= Referer(Uri("https://spray.io/secure")) - "Referer: /en-us/default.aspx?foo=bar" =!= Referer(Uri("/en-us/default.aspx?foo=bar")) - "Referer: https://akka.io/#sec" =!= ErrorInfo( - "Illegal HTTP header 'Referer': requirement failed", - "Referer header URI must not contain a fragment") - } - - "Server" in { - "Server: as fghf.fdf/xx" =!= `Server`(Vector(ProductVersion("as"), ProductVersion("fghf.fdf", "xx"))) - } - - "Strict-Transport-Security" in { - "Strict-Transport-Security: max-age=31536000" =!= `Strict-Transport-Security`(maxAge = 31536000) - "Strict-Transport-Security: max-age=31536000" =!= `Strict-Transport-Security`(maxAge = 31536000, includeSubDomains = false) - "Strict-Transport-Security: max-age=31536000; includeSubDomains" =!= `Strict-Transport-Security`(maxAge = 31536000, includeSubDomains = true) - } - - "Transfer-Encoding" in { - "Transfer-Encoding: chunked" =!= `Transfer-Encoding`(TransferEncodings.chunked) - "Transfer-Encoding: gzip" =!= `Transfer-Encoding`(TransferEncodings.gzip) - } - - "Range" in { - "Range: bytes=0-1" =!= Range(ByteRange(0, 1)) - "Range: bytes=0-" =!= Range(ByteRange.fromOffset(0)) - "Range: bytes=-1" =!= Range(ByteRange.suffix(1)) - "Range: bytes=0-1, 2-3, -99" =!= Range(ByteRange(0, 1), ByteRange(2, 3), ByteRange.suffix(99)) - } - - "Sec-WebSocket-Accept" in { - "Sec-WebSocket-Accept: ZGgwOTM0Z2owcmViamRvcGcK" =!= `Sec-WebSocket-Accept`("ZGgwOTM0Z2owcmViamRvcGcK") - } - "Sec-WebSocket-Extensions" in { - "Sec-WebSocket-Extensions: abc" =!= - `Sec-WebSocket-Extensions`(Vector(WebSocketExtension("abc"))) - "Sec-WebSocket-Extensions: abc, def" =!= - `Sec-WebSocket-Extensions`(Vector(WebSocketExtension("abc"), WebSocketExtension("def"))) - "Sec-WebSocket-Extensions: abc; param=2; use_y, def" =!= - `Sec-WebSocket-Extensions`(Vector(WebSocketExtension("abc", Map("param" → "2", "use_y" → "")), WebSocketExtension("def"))) - "Sec-WebSocket-Extensions: abc; param=\",xyz\", def" =!= - `Sec-WebSocket-Extensions`(Vector(WebSocketExtension("abc", Map("param" → ",xyz")), WebSocketExtension("def"))) - - // real examples from https://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-19 - "Sec-WebSocket-Extensions: permessage-deflate" =!= - `Sec-WebSocket-Extensions`(Vector(WebSocketExtension("permessage-deflate"))) - "Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits; server_max_window_bits=10" =!= - `Sec-WebSocket-Extensions`(Vector(WebSocketExtension("permessage-deflate", Map("client_max_window_bits" → "", "server_max_window_bits" → "10")))) - "Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits; server_max_window_bits=10, permessage-deflate; client_max_window_bits" =!= - `Sec-WebSocket-Extensions`(Vector( - WebSocketExtension("permessage-deflate", Map("client_max_window_bits" → "", "server_max_window_bits" → "10")), - WebSocketExtension("permessage-deflate", Map("client_max_window_bits" → "")))) - } - "Sec-WebSocket-Key" in { - "Sec-WebSocket-Key: c2Zxb3JpbmgyMzA5dGpoMDIzOWdlcm5vZ2luCg==" =!= `Sec-WebSocket-Key`("c2Zxb3JpbmgyMzA5dGpoMDIzOWdlcm5vZ2luCg==") - } - "Sec-WebSocket-Protocol" in { - "Sec-WebSocket-Protocol: chat" =!= `Sec-WebSocket-Protocol`(Vector("chat")) - "Sec-WebSocket-Protocol: chat, superchat" =!= `Sec-WebSocket-Protocol`(Vector("chat", "superchat")) - } - "Sec-WebSocket-Version" in { - "Sec-WebSocket-Version: 25" =!= `Sec-WebSocket-Version`(Vector(25)) - "Sec-WebSocket-Version: 13, 8, 7" =!= `Sec-WebSocket-Version`(Vector(13, 8, 7)) - - "Sec-WebSocket-Version: 255" =!= `Sec-WebSocket-Version`(Vector(255)) - "Sec-WebSocket-Version: 0" =!= `Sec-WebSocket-Version`(Vector(0)) - } - - "Set-Cookie" in { - "Set-Cookie: SID=\"31d4d96e407aad42\"" =!= - `Set-Cookie`(HttpCookie("SID", "31d4d96e407aad42")).renderedTo("SID=31d4d96e407aad42") - "Set-Cookie: SID=31d4d96e407aad42; Domain=example.com; Path=/" =!= - `Set-Cookie`(HttpCookie("SID", "31d4d96e407aad42", path = Some("/"), domain = Some("example.com"))) - "Set-Cookie: lang=en-US; Expires=Wed, 09 Jun 2021 10:18:14 GMT; Path=/hello" =!= - `Set-Cookie`(HttpCookie("lang", "en-US", expires = Some(DateTime(2021, 6, 9, 10, 18, 14)), path = Some("/hello"))) - "Set-Cookie: name=123; Max-Age=12345; Secure" =!= - `Set-Cookie`(HttpCookie("name", "123", maxAge = Some(12345), secure = true)) - "Set-Cookie: name=123; HttpOnly; fancyPants" =!= - `Set-Cookie`(HttpCookie("name", "123", httpOnly = true, extension = Some("fancyPants"))) - "Set-Cookie: foo=bar; domain=example.com; Path=/this is a path with blanks; extension with blanks" =!= - `Set-Cookie`(HttpCookie("foo", "bar", domain = Some("example.com"), path = Some("/this is a path with blanks"), - extension = Some("extension with blanks"))).renderedTo( - "foo=bar; Domain=example.com; Path=/this is a path with blanks; extension with blanks") - - // test all weekdays - "Set-Cookie: lang=; Expires=Sun, 07 Dec 2014 00:42:55 GMT; Max-Age=12345" =!= - `Set-Cookie`(HttpCookie("lang", "", expires = Some(DateTime(2014, 12, 7, 0, 42, 55)), maxAge = Some(12345))) - "Set-Cookie: lang=; Expires=Sunday, 07 Dec 2014 00:42:55 GMT; Max-Age=12345" =!= - `Set-Cookie`(HttpCookie("lang", "", expires = Some(DateTime(2014, 12, 7, 0, 42, 55)), maxAge = Some(12345))) - .renderedTo("lang=; Expires=Sun, 07 Dec 2014 00:42:55 GMT; Max-Age=12345") - - "Set-Cookie: lang=; Expires=Mon, 08 Dec 2014 00:42:55 GMT; Max-Age=12345" =!= - `Set-Cookie`(HttpCookie("lang", "", expires = Some(DateTime(2014, 12, 8, 0, 42, 55)), maxAge = Some(12345))) - "Set-Cookie: lang=; Expires=Monday, 08 Dec 2014 00:42:55 GMT; Max-Age=12345" =!= - `Set-Cookie`(HttpCookie("lang", "", expires = Some(DateTime(2014, 12, 8, 0, 42, 55)), maxAge = Some(12345))) - .renderedTo("lang=; Expires=Mon, 08 Dec 2014 00:42:55 GMT; Max-Age=12345") - - "Set-Cookie: lang=; Expires=Tue, 09 Dec 2014 00:42:55 GMT; Max-Age=12345" =!= - `Set-Cookie`(HttpCookie("lang", "", expires = Some(DateTime(2014, 12, 9, 0, 42, 55)), maxAge = Some(12345))) - "Set-Cookie: lang=; Expires=Tuesday, 09 Dec 2014 00:42:55 GMT; Max-Age=12345" =!= - `Set-Cookie`(HttpCookie("lang", "", expires = Some(DateTime(2014, 12, 9, 0, 42, 55)), maxAge = Some(12345))) - .renderedTo("lang=; Expires=Tue, 09 Dec 2014 00:42:55 GMT; Max-Age=12345") - - "Set-Cookie: lang=; Expires=Wed, 10 Dec 2014 00:42:55 GMT; Max-Age=12345" =!= - `Set-Cookie`(HttpCookie("lang", "", expires = Some(DateTime(2014, 12, 10, 0, 42, 55)), maxAge = Some(12345))) - "Set-Cookie: lang=; Expires=Wednesday, 10 Dec 2014 00:42:55 GMT; Max-Age=12345" =!= - `Set-Cookie`(HttpCookie("lang", "", expires = Some(DateTime(2014, 12, 10, 0, 42, 55)), maxAge = Some(12345))) - .renderedTo("lang=; Expires=Wed, 10 Dec 2014 00:42:55 GMT; Max-Age=12345") - - "Set-Cookie: lang=; Expires=Thu, 11 Dec 2014 00:42:55 GMT; Max-Age=12345" =!= - `Set-Cookie`(HttpCookie("lang", "", expires = Some(DateTime(2014, 12, 11, 0, 42, 55)), maxAge = Some(12345))) - "Set-Cookie: lang=; Expires=Thursday, 11 Dec 2014 00:42:55 GMT; Max-Age=12345" =!= - `Set-Cookie`(HttpCookie("lang", "", expires = Some(DateTime(2014, 12, 11, 0, 42, 55)), maxAge = Some(12345))) - .renderedTo("lang=; Expires=Thu, 11 Dec 2014 00:42:55 GMT; Max-Age=12345") - - "Set-Cookie: lang=; Expires=Fri, 12 Dec 2014 00:42:55 GMT; Max-Age=12345" =!= - `Set-Cookie`(HttpCookie("lang", "", expires = Some(DateTime(2014, 12, 12, 0, 42, 55)), maxAge = Some(12345))) - "Set-Cookie: lang=; Expires=Friday, 12 Dec 2014 00:42:55 GMT; Max-Age=12345" =!= - `Set-Cookie`(HttpCookie("lang", "", expires = Some(DateTime(2014, 12, 12, 0, 42, 55)), maxAge = Some(12345))) - .renderedTo("lang=; Expires=Fri, 12 Dec 2014 00:42:55 GMT; Max-Age=12345") - - "Set-Cookie: lang=; Expires=Sat, 13 Dec 2014 00:42:55 GMT; Max-Age=12345" =!= - `Set-Cookie`(HttpCookie("lang", "", expires = Some(DateTime(2014, 12, 13, 0, 42, 55)), maxAge = Some(12345))) - "Set-Cookie: lang=; Expires=Saturday, 13 Dec 2014 00:42:55 GMT; Max-Age=12345" =!= - `Set-Cookie`(HttpCookie("lang", "", expires = Some(DateTime(2014, 12, 13, 0, 42, 55)), maxAge = Some(12345))) - .renderedTo("lang=; Expires=Sat, 13 Dec 2014 00:42:55 GMT; Max-Age=12345") - - "Set-Cookie: lang=; Expires=Mon, 13 Dec 2014 00:42:55 GMT; Max-Age=12345" =!= - ErrorInfo("Illegal HTTP header 'Set-Cookie': Illegal weekday in date 2014-12-13T00:42:55", "is 'Mon' but should be 'Sat'") - - "Set-Cookie: lang=; Expires=xxxx" =!= - `Set-Cookie`(HttpCookie("lang", "", expires = Some(DateTime.MinValue))) - .renderedTo("lang=; Expires=Wed, 01 Jan 1800 00:00:00 GMT") - - "Set-Cookie: lang=; domain=----" =!= - ErrorInfo( - "Illegal HTTP header 'Set-Cookie': Invalid input '-', expected OWS or domain-value (line 1, column 15)", - "lang=; domain=----\n ^") - - // extra examples from play - "Set-Cookie: PLAY_FLASH=\"success=found\"; Path=/; HTTPOnly" =!= - `Set-Cookie`(HttpCookie("PLAY_FLASH", "success=found", path = Some("/"), httpOnly = true)) - .renderedTo("PLAY_FLASH=success=found; Path=/; HttpOnly") - "Set-Cookie: PLAY_FLASH=; Expires=Sun, 07 Dec 2014 22:48:47 GMT; Path=/; HTTPOnly" =!= - `Set-Cookie`(HttpCookie("PLAY_FLASH", "", expires = Some(DateTime(2014, 12, 7, 22, 48, 47)), path = Some("/"), httpOnly = true)) - .renderedTo("PLAY_FLASH=; Expires=Sun, 07 Dec 2014 22:48:47 GMT; Path=/; HttpOnly") - } - - "Upgrade" in { - "Upgrade: abc, def" =!= Upgrade(Vector(UpgradeProtocol("abc"), UpgradeProtocol("def"))) - "Upgrade: abc, def/38.1" =!= Upgrade(Vector(UpgradeProtocol("abc"), UpgradeProtocol("def", Some("38.1")))) - - "Upgrade: websocket" =!= Upgrade(Vector(UpgradeProtocol("websocket"))) - } - - "User-Agent" in { - "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_3) AppleWebKit/537.31" =!= - `User-Agent`(ProductVersion("Mozilla", "5.0", "Macintosh; Intel Mac OS X 10_8_3"), ProductVersion("AppleWebKit", "537.31")) - "User-Agent: foo(bar)(baz)" =!= - `User-Agent`(ProductVersion("foo", "", "bar"), ProductVersion(comment = "baz")).renderedTo("foo (bar) (baz)") - } - - "WWW-Authenticate" in { - "WWW-Authenticate: Basic" =!= - `WWW-Authenticate`(HttpChallenge("Basic", None)) - "WWW-Authenticate: Basic realm=\"WallyWorld\"" =!= - `WWW-Authenticate`(HttpChallenge("Basic", Some("WallyWorld"))) - "WWW-Authenticate: BaSiC rEaLm=WallyWorld" =!= - `WWW-Authenticate`(HttpChallenge("BaSiC", "WallyWorld")).renderedTo("BaSiC realm=\"WallyWorld\"") - "WWW-Authenticate: Basic realm=\"fooue\", Fancy realm=\"yeah\"" =!= - `WWW-Authenticate`(HttpChallenge("Basic", Some("WallyWorld"), Map("attr" → "val>ue")), HttpChallenge("Fancy", Some("yeah"))) - """WWW-Authenticate: Fancy realm="Secure Area",nonce=42""" =!= - `WWW-Authenticate`(HttpChallenge("Fancy", Some("Secure Area"), Map("nonce" → "42"))) - } - - "X-Forwarded-For" in { - "X-Forwarded-For: 1.2.3.4" =!= `X-Forwarded-For`(remoteAddress("1.2.3.4")) - "X-Forwarded-For: 234.123.5.6, 8.8.8.8" =!= `X-Forwarded-For`(remoteAddress("234.123.5.6"), remoteAddress("8.8.8.8")) - "X-Forwarded-For: 1.2.3.4, unknown" =!= `X-Forwarded-For`(remoteAddress("1.2.3.4"), RemoteAddress.Unknown) - "X-Forwarded-For: 192.0.2.43, 2001:db8:cafe:0:0:0:0:17" =!= `X-Forwarded-For`(remoteAddress("192.0.2.43"), remoteAddress("2001:db8:cafe::17")) - "X-Forwarded-For: 1234:5678:9abc:def1:2345:6789:abcd:ef00" =!= `X-Forwarded-For`(remoteAddress("1234:5678:9abc:def1:2345:6789:abcd:ef00")) - "X-Forwarded-For: 1234:567:9a:d:2:67:abc:ef00" =!= `X-Forwarded-For`(remoteAddress("1234:567:9a:d:2:67:abc:ef00")) - "X-Forwarded-For: 2001:db8:85a3::8a2e:370:7334" =!=> "2001:db8:85a3:0:0:8a2e:370:7334" - "X-Forwarded-For: 1:2:3:4:5:6:7:8" =!= `X-Forwarded-For`(remoteAddress("1:2:3:4:5:6:7:8")) - "X-Forwarded-For: ::2:3:4:5:6:7:8" =!=> "0:2:3:4:5:6:7:8" - "X-Forwarded-For: ::3:4:5:6:7:8" =!=> "0:0:3:4:5:6:7:8" - "X-Forwarded-For: ::4:5:6:7:8" =!=> "0:0:0:4:5:6:7:8" - "X-Forwarded-For: ::5:6:7:8" =!=> "0:0:0:0:5:6:7:8" - "X-Forwarded-For: ::6:7:8" =!=> "0:0:0:0:0:6:7:8" - "X-Forwarded-For: ::7:8" =!=> "0:0:0:0:0:0:7:8" - "X-Forwarded-For: ::8" =!=> "0:0:0:0:0:0:0:8" - "X-Forwarded-For: 1:2:3:4:5:6:7::" =!=> "1:2:3:4:5:6:7:0" - "X-Forwarded-For: 1:2:3:4:5:6::" =!=> "1:2:3:4:5:6:0:0" - "X-Forwarded-For: 1:2:3:4:5::" =!=> "1:2:3:4:5:0:0:0" - "X-Forwarded-For: 1:2:3:4::" =!=> "1:2:3:4:0:0:0:0" - "X-Forwarded-For: 1:2:3::" =!=> "1:2:3:0:0:0:0:0" - "X-Forwarded-For: 1:2::" =!=> "1:2:0:0:0:0:0:0" - "X-Forwarded-For: 1::" =!=> "1:0:0:0:0:0:0:0" - "X-Forwarded-For: 1::3:4:5:6:7:8" =!=> "1:0:3:4:5:6:7:8" - "X-Forwarded-For: 1:2::4:5:6:7:8" =!=> "1:2:0:4:5:6:7:8" - "X-Forwarded-For: 1:2:3::5:6:7:8" =!=> "1:2:3:0:5:6:7:8" - "X-Forwarded-For: 1:2:3:4::6:7:8" =!=> "1:2:3:4:0:6:7:8" - "X-Forwarded-For: 1:2:3:4:5::7:8" =!=> "1:2:3:4:5:0:7:8" - "X-Forwarded-For: 1:2:3:4:5:6::8" =!=> "1:2:3:4:5:6:0:8" - "X-Forwarded-For: ::" =!=> "0:0:0:0:0:0:0:0" - "X-Forwarded-For: 1.2.3.4, akka.io" =!= - ErrorInfo( - "Illegal HTTP header 'X-Forwarded-For': Invalid input 'k', expected HEXDIG, h8, ':', ch16o or cc (line 1, column 11)", - "1.2.3.4, akka.io\n ^") - } - - "X-Real-Ip" in { - "X-Real-Ip: 1.2.3.4" =!= `X-Real-Ip`(remoteAddress("1.2.3.4")) - "X-Real-Ip: 2001:db8:cafe:0:0:0:0:17" =!= `X-Real-Ip`(remoteAddress("2001:db8:cafe:0:0:0:0:17")) - "X-Real-Ip: 1234:5678:9abc:def1:2345:6789:abcd:ef00" =!= `X-Real-Ip`(remoteAddress("1234:5678:9abc:def1:2345:6789:abcd:ef00")) - "X-Real-Ip: 1234:567:9a:d:2:67:abc:ef00" =!= `X-Real-Ip`(remoteAddress("1234:567:9a:d:2:67:abc:ef00")) - "X-Real-Ip: 2001:db8:85a3::8a2e:370:7334" =!=> "2001:db8:85a3:0:0:8a2e:370:7334" - "X-Real-Ip: 1:2:3:4:5:6:7:8" =!= `X-Real-Ip`(remoteAddress("1:2:3:4:5:6:7:8")) - "X-Real-Ip: ::2:3:4:5:6:7:8" =!=> "0:2:3:4:5:6:7:8" - "X-Real-Ip: ::3:4:5:6:7:8" =!=> "0:0:3:4:5:6:7:8" - "X-Real-Ip: ::4:5:6:7:8" =!=> "0:0:0:4:5:6:7:8" - "X-Real-Ip: ::5:6:7:8" =!=> "0:0:0:0:5:6:7:8" - "X-Real-Ip: ::6:7:8" =!=> "0:0:0:0:0:6:7:8" - "X-Real-Ip: ::7:8" =!=> "0:0:0:0:0:0:7:8" - "X-Real-Ip: ::8" =!=> "0:0:0:0:0:0:0:8" - "X-Real-Ip: 1:2:3:4:5:6:7::" =!=> "1:2:3:4:5:6:7:0" - "X-Real-Ip: 1:2:3:4:5:6::" =!=> "1:2:3:4:5:6:0:0" - "X-Real-Ip: 1:2:3:4:5::" =!=> "1:2:3:4:5:0:0:0" - "X-Real-Ip: 1:2:3:4::" =!=> "1:2:3:4:0:0:0:0" - "X-Real-Ip: 1:2:3::" =!=> "1:2:3:0:0:0:0:0" - "X-Real-Ip: 1:2::" =!=> "1:2:0:0:0:0:0:0" - "X-Real-Ip: 1::" =!=> "1:0:0:0:0:0:0:0" - "X-Real-Ip: 1::3:4:5:6:7:8" =!=> "1:0:3:4:5:6:7:8" - "X-Real-Ip: 1:2::4:5:6:7:8" =!=> "1:2:0:4:5:6:7:8" - "X-Real-Ip: 1:2:3::5:6:7:8" =!=> "1:2:3:0:5:6:7:8" - "X-Real-Ip: 1:2:3:4::6:7:8" =!=> "1:2:3:4:0:6:7:8" - "X-Real-Ip: 1:2:3:4:5::7:8" =!=> "1:2:3:4:5:0:7:8" - "X-Real-Ip: 1:2:3:4:5:6::8" =!=> "1:2:3:4:5:6:0:8" - "X-Real-Ip: ::" =!=> "0:0:0:0:0:0:0:0" - "X-Real-Ip: akka.io" =!= - ErrorInfo( - "Illegal HTTP header 'X-Real-Ip': Invalid input 'k', expected HEXDIG, h8, ':', ch16o or cc (line 1, column 2)", - "akka.io\n ^") - } - - "RawHeader" in { - "X-Space-Ranger: no, this rock!" =!= RawHeader("X-Space-Ranger", "no, this rock!") - } - } - - "The header parser should" - { - import HttpHeader._ - "not accept illegal header names" in { - parse("X:", "a") shouldEqual ParsingResult.Error(ErrorInfo("Illegal HTTP header name", "X:")) - parse(" X", "a") shouldEqual ParsingResult.Error(ErrorInfo("Illegal HTTP header name", " X")) - } - "not accept illegal header values" in { - parse("Foo", "ba\u0000r") shouldEqual ParsingResult.Error(ErrorInfo( - "Illegal HTTP header value: Invalid input '\\u0000', expected field-value-char, FWS or 'EOI' (line 1, column 3)", - "ba\u0000r\n ^")) - } - "allow UTF8 characters in RawHeaders" in { - parse("Flood-Resistant-Hammerdrill", "árvíztűrő ütvefúrógép") shouldEqual - ParsingResult.Ok(RawHeader("Flood-Resistant-Hammerdrill", "árvíztűrő ütvefúrógép"), Nil) - } - "compress value whitespace into single spaces and trim" in { - parse("Foo", " b a \tr\t") shouldEqual ParsingResult.Ok(RawHeader("Foo", "b a r"), Nil) - } - "resolve obs-fold occurrences" in { - parse("Foo", "b\r\n\ta \r\n r") shouldEqual ParsingResult.Ok(RawHeader("Foo", "b a r"), Nil) - } - - "parse with custom uri parsing mode" in { - val targetUri = Uri("http://example.org/?abc=def=ghi", Uri.ParsingMode.Relaxed) - HeaderParser.parseFull("location", "http://example.org/?abc=def=ghi", HeaderParser.Settings(uriParsingMode = Uri.ParsingMode.Relaxed)) shouldEqual - Right(Location(targetUri)) - } - } - - implicit class TestLine(line: String) { - def =!=(testHeader: TestExample) = testHeader(line) - def =!=>(expectedRendering: String) = { - val Array(name, value) = line.split(": ", 2) - val HttpHeader.ParsingResult.Ok(header, Nil) = HttpHeader.parse(name, value) - header.toString shouldEqual header.renderedTo(expectedRendering).rendering("") - } - } - sealed trait TestExample extends (String ⇒ Unit) - implicit class TestHeader(val header: HttpHeader) extends TestExample { outer ⇒ - def apply(line: String) = { - val Array(name, value) = line.split(": ", 2) - HttpHeader.parse(name, value, settings) should (equal(HttpHeader.ParsingResult.Ok(header, Nil)) and renderFromHeaderTo(this, line)) - } - def rendering(line: String): String = line - def settings: HeaderParser.Settings = HeaderParser.DefaultSettings - def renderedTo(expectedRendering: String): TestHeader = - new TestHeader(header) { - override def rendering(line: String): String = - header match { - case x: ModeledHeader ⇒ x.name + ": " + expectedRendering - case _ ⇒ expectedRendering - } - - override def settings: Settings = outer.settings - } - def withCookieParsingMode(mode: CookieParsingMode): TestHeader = - withParserSettings(Settings(settings.uriParsingMode, mode)) - - def withParserSettings(newSettings: HeaderParser.Settings): TestHeader = - new TestHeader(header) { - override def rendering(line: String): String = outer.rendering(line) - override def settings = newSettings - } - } - implicit class TestError(expectedError: ErrorInfo) extends TestExample { - def apply(line: String) = { - val Array(name, value) = line.split(": ", 2) - val HttpHeader.ParsingResult.Ok(_, error :: Nil) = HttpHeader.parse(name, value) - error shouldEqual expectedError - } - } - - def renderFromHeaderTo(header: TestHeader, line: String): Matcher[HttpHeader.ParsingResult] = - Matcher { - case HttpHeader.ParsingResult.Ok(h, Nil) ⇒ - MatchResult( - h.toString === header.rendering(line), - s"doesn't render to '${header.rendering(line)}' but '${h.toString}'", "XXX") - case result ⇒ - val info = result.errors.head - fail(s"Input `${header.header}` failed to parse:\n${info.summary}\n${info.detail}") - } - - def remoteAddress(ip: String) = RemoteAddress(InetAddress.getByName(ip)) -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/util/ByteStringParserInputSpec.scala b/akka-http-core/src/test/scala/akka/http/impl/util/ByteStringParserInputSpec.scala deleted file mode 100644 index cc3229e6f5..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/util/ByteStringParserInputSpec.scala +++ /dev/null @@ -1,34 +0,0 @@ -package akka.http.impl.util - -import akka.util.ByteString -import org.scalatest.{ Matchers, WordSpec } - -class ByteStringParserInputSpec extends WordSpec with Matchers { - - "The ByteStringParserInput" should { - val parser = new ByteStringParserInput(ByteString("abcde", "ISO-8859-1")) - "return the correct character for index" in { - parser.charAt(0) should ===('a') - parser.charAt(4) should ===('e') - } - - "return the correct length" in { - parser.length should ===(5) - } - - "slice the bytes correctly into a string" in { - parser.sliceString(0, 3) should ===("abc") - parser.sliceString(3, 5) should ===("de") - } - - "slice the bytes correctly into a char array" in { - val array = parser.sliceCharArray(0, 3) - array(0) should ===('a') - array(1) should ===('b') - array(2) should ===('c') - array.length should ===(3) - } - - } - -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/util/ExampleHttpContexts.scala b/akka-http-core/src/test/scala/akka/http/impl/util/ExampleHttpContexts.scala deleted file mode 100644 index 95bb2f4bc9..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/util/ExampleHttpContexts.scala +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.util - -import java.io.InputStream -import java.security.{ SecureRandom, KeyStore } -import java.security.cert.{ CertificateFactory, Certificate } -import javax.net.ssl.{ SSLParameters, SSLContext, TrustManagerFactory, KeyManagerFactory } - -import akka.http.scaladsl.HttpsConnectionContext - -/** - * These are HTTPS example configurations that take key material from the resources/key folder. - */ -object ExampleHttpContexts { - - // TODO show example how to obtain pre-configured context from ssl-config - - val exampleServerContext = { - // never put passwords into code! - val password = "abcdef".toCharArray - - val ks = KeyStore.getInstance("PKCS12") - ks.load(resourceStream("keys/server.p12"), password) - - val keyManagerFactory = KeyManagerFactory.getInstance("SunX509") - keyManagerFactory.init(ks, password) - - val context = SSLContext.getInstance("TLS") - context.init(keyManagerFactory.getKeyManagers, null, new SecureRandom) - - new HttpsConnectionContext(context) - } - - val exampleClientContext = { - val certStore = KeyStore.getInstance(KeyStore.getDefaultType) - certStore.load(null, null) - // only do this if you want to accept a custom root CA. Understand what you are doing! - certStore.setCertificateEntry("ca", loadX509Certificate("keys/rootCA.crt")) - - val certManagerFactory = TrustManagerFactory.getInstance("SunX509") - certManagerFactory.init(certStore) - - val context = SSLContext.getInstance("TLS") - context.init(null, certManagerFactory.getTrustManagers, new SecureRandom) - - val params = new SSLParameters() - params.setEndpointIdentificationAlgorithm("https") - new HttpsConnectionContext(context, sslParameters = Some(params)) - } - - def resourceStream(resourceName: String): InputStream = { - val is = getClass.getClassLoader.getResourceAsStream(resourceName) - require(is ne null, s"Resource $resourceName not found") - is - } - - def loadX509Certificate(resourceName: String): Certificate = - CertificateFactory.getInstance("X.509").generateCertificate(resourceStream(resourceName)) -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/util/One2OneBidiFlowSpec.scala b/akka-http-core/src/test/scala/akka/http/impl/util/One2OneBidiFlowSpec.scala deleted file mode 100644 index 75d5739c41..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/util/One2OneBidiFlowSpec.scala +++ /dev/null @@ -1,154 +0,0 @@ -/** - * Copyright (C) 2015-2016 Lightbend Inc. - */ -package akka.http.impl.util - -import java.util.concurrent.atomic.AtomicInteger -import akka.NotUsed -import akka.stream.ActorMaterializer -import akka.stream.scaladsl.{ Flow, Keep, Sink, Source } -import akka.stream.testkit.Utils._ -import akka.stream.testkit._ -import org.scalactic.ConversionCheckedTripleEquals -import scala.concurrent.Await -import scala.concurrent.duration._ -import akka.testkit.AkkaSpec - -class One2OneBidiFlowSpec extends AkkaSpec { - implicit val materializer = ActorMaterializer() - - "A One2OneBidiFlow" must { - - def test(flow: Flow[Int, Int, NotUsed]) = - Source(List(1, 2, 3)).via(flow).grouped(10).runWith(Sink.head) - - "be fully transparent for valid one-to-one streams" in assertAllStagesStopped { - val f = One2OneBidiFlow[Int, Int](-1) join Flow[Int].map(_ * 2) - Await.result(test(f), 1.second) should ===(Seq(2, 4, 6)) - } - - "be fully transparent to errors" in { - val f = One2OneBidiFlow[Int, Int](-1) join Flow[Int].map(x ⇒ 10 / (x - 2)) - an[ArithmeticException] should be thrownBy Await.result(test(f), 1.second) - } - - "trigger an `OutputTruncationException` if the wrapped stream completes early" in assertAllStagesStopped { - val flowInProbe = TestSubscriber.probe[Int]() - val flowOutProbe = TestPublisher.probe[Int]() - - val testSetup = One2OneBidiFlow[Int, Int](-1) join Flow.fromSinkAndSource( - Sink.fromSubscriber(flowInProbe), - Source.fromPublisher(flowOutProbe)) - - val upstreamProbe = TestPublisher.probe[Int]() - val downstreamProbe = TestSubscriber.probe[Int]() - - Source.fromPublisher(upstreamProbe).via(testSetup).runWith(Sink.fromSubscriber(downstreamProbe)) - - upstreamProbe.ensureSubscription() - downstreamProbe.ensureSubscription() - flowInProbe.ensureSubscription() - flowOutProbe.ensureSubscription() - - downstreamProbe.request(1) - flowInProbe.request(1) - - upstreamProbe.sendNext(1) - flowInProbe.expectNext(1) - - // Request is now in the wrapped flow but no reply has been returned at this point, this is a clear truncation - - flowOutProbe.sendComplete() - upstreamProbe.expectCancellation() - flowInProbe.expectError(One2OneBidiFlow.OutputTruncationException) - downstreamProbe.expectError(One2OneBidiFlow.OutputTruncationException) - } - - "trigger an `UnexpectedOutputException` if the wrapped stream produces out-of-order elements" in assertAllStagesStopped { - new Test() { - inIn.sendNext(1) - inOut.requestNext() should ===(1) - - outIn.sendNext(2) - outOut.requestNext() should ===(2) - - outOut.request(1) - outIn.sendNext(3) - outOut.expectError(new One2OneBidiFlow.UnexpectedOutputException(3)) - } - } - - "fully propagate cancellation" in assertAllStagesStopped { - new Test() { - inIn.sendNext(1) - inOut.requestNext() should ===(1) - - outIn.sendNext(2) - outOut.requestNext() should ===(2) - - outOut.cancel() - outIn.expectCancellation() - - inOut.cancel() - inIn.expectCancellation() - } - } - - "backpressure the input side if the maximum number of pending output elements has been reached" in assertAllStagesStopped { - val MAX_PENDING = 24 - - val out = TestPublisher.probe[Int]() - val seen = new AtomicInteger - - Source(1 to 1000) - .log("", seen.set) - .via(One2OneBidiFlow[Int, Int](MAX_PENDING) join Flow.fromSinkAndSourceMat(Sink.ignore, Source.fromPublisher(out))(Keep.left)) - .runWith(Sink.ignore) - - Thread.sleep(50) - val x = seen.get() - (1 to 8) foreach out.sendNext - Thread.sleep(50) - seen.get should ===(x + 8) - - out.sendComplete() // To please assertAllStagesStopped - } - - "not pull when input is closed before surpressed pull can be acted on" in assertAllStagesStopped { - val in = TestPublisher.probe[Int]() - val out = TestSubscriber.probe[Int]() - val wrappedIn = TestSubscriber.probe[Int]() - val wrappedOut = TestPublisher.probe[Int]() - - Source.fromPublisher(in).via( - One2OneBidiFlow(maxPending = 1) join Flow.fromSinkAndSource( - Sink.fromSubscriber(wrappedIn), - Source.fromPublisher(wrappedOut)) - ).runWith(Sink.fromSubscriber(out)) - - out.request(2) - wrappedOut.expectRequest() - wrappedIn.request(2) - in.expectRequest() - in.sendNext(1) - wrappedIn.expectNext(1) - // now we have reached the maxPending limit - in.sendComplete() - wrappedOut.sendNext(1) - out.expectNext(1) - wrappedIn.expectComplete() - wrappedOut.sendComplete() - out.expectComplete() - - } - } - - class Test(maxPending: Int = -1) { - val inIn = TestPublisher.probe[Int]() - val inOut = TestSubscriber.probe[Int]() - val outIn = TestPublisher.probe[Int]() - val outOut = TestSubscriber.probe[Int]() - - Source.fromPublisher(inIn).via(One2OneBidiFlow[Int, Int](maxPending) join Flow.fromSinkAndSourceMat(Sink.fromSubscriber(inOut), Source.fromPublisher(outIn))(Keep.left)).runWith(Sink.fromSubscriber(outOut)) - } -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/util/RenderingSpec.scala b/akka-http-core/src/test/scala/akka/http/impl/util/RenderingSpec.scala deleted file mode 100644 index bd4812db7e..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/util/RenderingSpec.scala +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.impl.util - -import org.scalatest.{ Matchers, WordSpec } - -class RenderingSpec extends WordSpec with Matchers { - - "The StringRendering" should { - - "correctly render Ints and Longs to decimal" in { - (new StringRendering ~~ 0).get shouldEqual "0" - (new StringRendering ~~ 123456789).get shouldEqual "123456789" - (new StringRendering ~~ -123456789L).get shouldEqual "-123456789" - } - - "correctly render Ints and Longs to hex" in { - (new StringRendering ~~% 0).get shouldEqual "0" - (new StringRendering ~~% 65535).get shouldEqual "ffff" - (new StringRendering ~~% 65537).get shouldEqual "10001" - (new StringRendering ~~% -10L).get shouldEqual "fffffffffffffff6" - } - - "correctly render plain Strings" in { - (new StringRendering ~~ "").get shouldEqual "" - (new StringRendering ~~ "hello").get shouldEqual "hello" - } - - "correctly render escaped Strings" in { - (new StringRendering ~~# "").get shouldEqual "\"\"" - (new StringRendering ~~# "hello").get shouldEqual "hello" - (new StringRendering ~~# """hel"lo""").get shouldEqual """"hel\"lo"""" - } - } -} diff --git a/akka-http-core/src/test/scala/akka/http/impl/util/StreamUtilsSpec.scala b/akka-http-core/src/test/scala/akka/http/impl/util/StreamUtilsSpec.scala deleted file mode 100644 index f9174f98c5..0000000000 --- a/akka-http-core/src/test/scala/akka/http/impl/util/StreamUtilsSpec.scala +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.impl.util - -import akka.stream.ActorMaterializer -import akka.stream.scaladsl.{ Sink, Source } -import akka.testkit.AkkaSpec - -import scala.concurrent.Await -import scala.concurrent.duration._ -import scala.util.Failure - -class StreamUtilsSpec extends AkkaSpec { - implicit val materializer = ActorMaterializer() - - "captureTermination" should { - "signal completion" when { - "upstream terminates" in { - val (newSource, whenCompleted) = StreamUtils.captureTermination(Source(List(1, 2, 3))) - - newSource.runWith(Sink.ignore) - - Await.result(whenCompleted, 3.seconds) shouldBe () - } - - "upstream fails" in { - val ex = new RuntimeException("ex") - val (newSource, whenCompleted) = StreamUtils.captureTermination(Source.failed[Int](ex)) - intercept[RuntimeException] { - Await.result(newSource.runWith(Sink.head), 3.second) - } should be theSameInstanceAs ex - - Await.ready(whenCompleted, 3.seconds).value shouldBe Some(Failure(ex)) - } - - "downstream cancels" in { - val (newSource, whenCompleted) = StreamUtils.captureTermination(Source(List(1, 2, 3))) - - newSource.runWith(Sink.head) - - Await.result(whenCompleted, 3.seconds) shouldBe () - } - } - } - -} diff --git a/akka-http-core/src/test/scala/akka/http/javadsl/ConnectHttpSpec.scala b/akka-http-core/src/test/scala/akka/http/javadsl/ConnectHttpSpec.scala deleted file mode 100644 index 25ef03e272..0000000000 --- a/akka-http-core/src/test/scala/akka/http/javadsl/ConnectHttpSpec.scala +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Copyright (C) 2016 Typesafe Inc. - */ -package akka.http.javadsl - -import akka.http.javadsl.model._ -import org.scalatest.concurrent.ScalaFutures -import org.scalatest.{ BeforeAndAfterAll, Matchers, WordSpec } - -class ConnectHttpSpec extends WordSpec with Matchers with BeforeAndAfterAll { - - val httpContext = ConnectionContext.noEncryption() - val httpsContext = ConnectionContext.https(null) - - val successResponse = HttpResponse.create().withStatus(200) - - "HttpConnect" should { - - "connect toHost HTTP:80 by default" in { - val connect = ConnectHttp.toHost("127.0.0.1") - connect.isHttps should ===(false) - connect.connectionContext.isPresent should equal(false) - connect.host should ===("127.0.0.1") - connect.port should ===(80) - } - "connect toHost HTTPS:443 when https prefix given in host" in { - val connect = ConnectHttp.toHost("https://127.0.0.1") - connect.isHttps should ===(true) - connect.connectionContext.isPresent should equal(false) - connect.host should ===("127.0.0.1") - connect.port should ===(443) - } - "connect toHostHttps HTTPS:8080 when https prefix and port given in host" in { - val connect = ConnectHttp.toHostHttps("https://127.0.0.1:8080") - connect.isHttps should ===(true) - connect.connectionContext.isPresent should equal(false) - connect.host should ===("127.0.0.1") - connect.port should ===(8080) - } - "connect toHostHttps HTTPS:9999 when https prefix and port given" in { - val connect = ConnectHttp.toHostHttps("https://127.0.0.1:8080", 9999) - connect.isHttps should ===(true) - connect.connectionContext.isPresent should equal(false) - connect.host should ===("127.0.0.1") - connect.port should ===(9999) - } - "connect toHost HTTPS:8080 when https prefix and port given" in { - val connect = ConnectHttp.toHost("http://127.0.0.1:8080", 9999) - connect.isHttps should ===(false) - connect.connectionContext.isPresent should equal(false) - connect.host should ===("127.0.0.1") - connect.port should ===(9999) - } - "connect toHostHttps HTTPS:443 when no port given" in { - val connect = ConnectHttp.toHostHttps("https://127.0.0.1") - connect.isHttps should ===(true) - connect.connectionContext.isPresent should equal(false) - connect.host should ===("127.0.0.1") - connect.port should ===(443) - } - "connect toHostHttps HTTPS:443 using custom https context" in { - val connect = ConnectHttp.toHostHttps("https://127.0.0.1").withCustomHttpsContext(httpsContext) - connect.isHttps should ===(true) - connect.connectionContext.isPresent should equal(true) - connect.host should ===("127.0.0.1") - connect.port should ===(443) - } - "throw when toHostHttps used but http:// prefix found" in { - val ex = intercept[IllegalArgumentException] { - ConnectHttp.toHostHttps("http://127.0.0.1", 8080) - } - ex.getMessage should include("non https scheme!") - } - } -} diff --git a/akka-http-core/src/test/scala/akka/http/javadsl/ConnectionContextSpec.scala b/akka-http-core/src/test/scala/akka/http/javadsl/ConnectionContextSpec.scala deleted file mode 100644 index 4c6856cdc3..0000000000 --- a/akka-http-core/src/test/scala/akka/http/javadsl/ConnectionContextSpec.scala +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.javadsl - -import java.util -import java.util.{ Collections, Optional } -import javax.net.ssl.{ SSLContext, SSLSessionContext } - -import akka.http.javadsl.model._ -import akka.stream.TLSClientAuth -import org.scalatest.{ BeforeAndAfterAll, Matchers, WordSpec } - -class ConnectionContextSpec extends WordSpec with Matchers { - - "ConnectionContext.https" should { - - "pass through all parameters" in { - val sslContext = SSLContext.getDefault - val ciphers: Optional[java.util.Collection[String]] = Optional.of(Collections.singletonList("A")) - val protocols: Optional[java.util.Collection[String]] = Optional.of(Collections.singletonList("B")) - val clientAuth = Optional.of(TLSClientAuth.need) - val parameters = Optional.of(sslContext.getDefaultSSLParameters) - - val httpsContext = akka.http.javadsl.ConnectionContext.https(sslContext, ciphers, protocols, clientAuth, parameters) - httpsContext.getSslContext should ===(sslContext) - httpsContext.getEnabledCipherSuites.get.toArray.toList shouldBe (ciphers.get.toArray.toList) - httpsContext.getEnabledProtocols.get.toArray.toList shouldBe (protocols.get.toArray.toList) - httpsContext.getClientAuth should ===(clientAuth) - httpsContext.getSslParameters should ===(parameters) - } - } -} diff --git a/akka-http-core/src/test/scala/akka/http/javadsl/HttpExtensionApiSpec.scala b/akka-http-core/src/test/scala/akka/http/javadsl/HttpExtensionApiSpec.scala deleted file mode 100644 index b86308eca1..0000000000 --- a/akka-http-core/src/test/scala/akka/http/javadsl/HttpExtensionApiSpec.scala +++ /dev/null @@ -1,477 +0,0 @@ -/** - * Copyright (C) 2016 Lightbend Inc. - */ -package akka.http.javadsl - -import java.net.InetSocketAddress -import java.util.Optional -import java.util.concurrent.{ CompletionStage, TimeUnit, CompletableFuture } - -import akka.NotUsed -import akka.http.javadsl.ConnectHttp._ -import akka.http.javadsl.model.ws._ -import akka.http.javadsl.settings.{ ClientConnectionSettings, ConnectionPoolSettings, ServerSettings } -import akka.japi.Pair -import akka.actor.ActorSystem -import akka.event.NoLogging -import akka.http.javadsl.model._ -import akka.http.scaladsl.TestUtils -import akka.japi.Function -import akka.stream.{ ActorMaterializer } -import akka.stream.javadsl.{ Source, Flow, Sink, Keep } -import akka.stream.testkit.TestSubscriber -import com.typesafe.config.{ ConfigFactory } -import org.scalatest.{ WordSpec, Matchers, BeforeAndAfterAll } -import org.scalatest.concurrent.ScalaFutures -import scala.util.Try - -class HttpExtensionApiSpec extends WordSpec with Matchers with BeforeAndAfterAll { - - // tries to cover all surface area of javadsl.Http - - type Host = String - type Port = Int - - implicit val system = { - val testConf = ConfigFactory.parseString(""" - akka.loggers = ["akka.testkit.TestEventListener"] - akka.loglevel = ERROR - akka.stdout-loglevel = ERROR - windows-connection-abort-workaround-enabled = auto - akka.log-dead-letters = OFF - akka.http.server.request-timeout = infinite""") - - ActorSystem(getClass.getSimpleName, testConf) - } - implicit val materializer = ActorMaterializer() - - val http = Http.get(system) - val connectionContext = ConnectionContext.noEncryption() - val serverSettings = ServerSettings.create(system) - val poolSettings = ConnectionPoolSettings.create(system) - val loggingAdapter = NoLogging - - val successResponse = HttpResponse.create().withStatus(200) - - val httpSuccessFunction = new Function[HttpRequest, HttpResponse] { - @throws(classOf[Exception]) - override def apply(param: HttpRequest): HttpResponse = successResponse - } - - val asyncHttpSuccessFunction = new Function[HttpRequest, CompletionStage[HttpResponse]] { - @throws(classOf[Exception]) - override def apply(param: HttpRequest): CompletionStage[HttpResponse] = - CompletableFuture.completedFuture(successResponse) - } - - "The Java HTTP extension" should { - - // all four bind method overloads - "properly bind a server (with three parameters)" in { - val (_, host, port) = TestUtils.temporaryServerHostnameAndPort() - val probe = TestSubscriber.manualProbe[IncomingConnection]() - val binding = http.bind(toHost(host, port), materializer) - .toMat(Sink.fromSubscriber(probe), Keep.left) - .run(materializer) - val sub = probe.expectSubscription() - waitFor(binding).unbind() - sub.cancel() - } - - "properly bind a server (with four parameters)" in { - val (_, host, port) = TestUtils.temporaryServerHostnameAndPort() - val probe = TestSubscriber.manualProbe[IncomingConnection]() - val binding = http.bind(toHost(host, port), materializer) - .toMat(Sink.fromSubscriber(probe), Keep.left) - .run(materializer) - val sub = probe.expectSubscription() - waitFor(binding).unbind() - sub.cancel() - } - - "properly bind a server (with five parameters)" in { - val (_, host, port) = TestUtils.temporaryServerHostnameAndPort() - val probe = TestSubscriber.manualProbe[IncomingConnection]() - val binding = http.bind(toHost(host, port), serverSettings, materializer) - .toMat(Sink.fromSubscriber(probe), Keep.left) - .run(materializer) - val sub = probe.expectSubscription() - waitFor(binding).unbind() - sub.cancel() - } - - "properly bind a server (with six parameters)" in { - val (_, host, port) = TestUtils.temporaryServerHostnameAndPort() - val probe = TestSubscriber.manualProbe[IncomingConnection]() - val binding = http.bind(toHost(host, port), serverSettings, loggingAdapter, materializer) - .toMat(Sink.fromSubscriber(probe), Keep.left) - .run(materializer) - val sub = probe.expectSubscription() - waitFor(binding).unbind() - sub.cancel() - } - - // this cover both bind and single request - "properly bind and handle a server with a flow (with four parameters)" in { - val (_, host, port) = TestUtils.temporaryServerHostnameAndPort() - val flow: Flow[HttpRequest, HttpResponse, NotUsed] = akka.stream.scaladsl.Flow[HttpRequest] - .map(req ⇒ HttpResponse.create()) - .asJava - val binding = http.bindAndHandle(flow, toHost(host, port), materializer) - - val (_, completion) = http.outgoingConnection(toHost(host, port)) - .runWith(Source.single(HttpRequest.create("/abc")), Sink.head(), materializer).toScala - - waitFor(completion) - waitFor(binding).unbind() - } - - "properly bind and handle a server with a flow (with five parameters)" in { - val (_, host, port) = TestUtils.temporaryServerHostnameAndPort() - val flow: Flow[HttpRequest, HttpResponse, NotUsed] = akka.stream.scaladsl.Flow[HttpRequest] - .map(req ⇒ HttpResponse.create()) - .asJava - val binding = http.bindAndHandle(flow, toHost(host, port), materializer) - - val (_, completion) = http.outgoingConnection(toHost(host, port)) - .runWith(Source.single(HttpRequest.create("/abc")), Sink.head(), materializer).toScala - - waitFor(completion) - waitFor(binding).unbind() - } - - "properly bind and handle a server with a flow (with seven parameters)" in { - val (_, host, port) = TestUtils.temporaryServerHostnameAndPort() - val flow: Flow[HttpRequest, HttpResponse, NotUsed] = akka.stream.scaladsl.Flow[HttpRequest] - .map(req ⇒ HttpResponse.create()) - .asJava - val binding = http.bindAndHandle(flow, toHost(host, port), serverSettings, loggingAdapter, materializer) - - val (_, completion) = http.outgoingConnection(toHost(host, port)) - .runWith(Source.single(HttpRequest.create("/abc")), Sink.head(), materializer).toScala - - waitFor(completion) - waitFor(binding).unbind() - } - - "properly bind and handle a server with a synchronous function (with four parameters)" in { - val (_, host, port) = TestUtils.temporaryServerHostnameAndPort() - val binding = http.bindAndHandleSync(httpSuccessFunction, toHost(host, port), materializer) - - val response = http.singleRequest(HttpRequest.create(s"http://$host:$port/").withMethod(HttpMethods.GET), materializer) - - waitFor(response) - waitFor(binding).unbind() - } - - "properly bind and handle a server with a synchronous function (with five parameters)" in { - val (_, host, port) = TestUtils.temporaryServerHostnameAndPort() - val binding = http.bindAndHandleSync(httpSuccessFunction, toHost(host, port), materializer) - - val response = http.singleRequest(HttpRequest.create(s"http://$host:$port/").withMethod(HttpMethods.GET), materializer) - - waitFor(response) - waitFor(binding).unbind() - } - - "properly bind and handle a server with a synchronous (with seven parameters)" in { - val (_, host, port) = TestUtils.temporaryServerHostnameAndPort() - val binding = http.bindAndHandleSync(httpSuccessFunction, toHost(host, port), serverSettings, loggingAdapter, materializer) - - val response = http.singleRequest(HttpRequest.create(s"http://$host:$port/").withMethod(HttpMethods.GET), materializer) - - waitFor(response) - waitFor(binding).unbind() - } - - "properly bind and handle a server with an asynchronous function (with four parameters)" in { - val (_, host, port) = TestUtils.temporaryServerHostnameAndPort() - val binding = http.bindAndHandleAsync(asyncHttpSuccessFunction, toHost(host, port), materializer) - - val response = http.singleRequest(HttpRequest.create(s"http://$host:$port/").withMethod(HttpMethods.GET), materializer) - - waitFor(response) - waitFor(binding).unbind() - } - - "properly bind and handle a server with an asynchronous function (with five parameters)" in { - val (_, host, port) = TestUtils.temporaryServerHostnameAndPort() - val binding = http.bindAndHandleAsync(asyncHttpSuccessFunction, toHost(host, port), materializer) - - val response = http.singleRequest(HttpRequest.create(s"http://$host:$port/").withMethod(HttpMethods.GET), materializer) - - waitFor(response) - waitFor(binding).unbind() - } - - "properly bind and handle a server with an asynchronous function (with eight parameters)" in { - val (_, host, port) = TestUtils.temporaryServerHostnameAndPort() - val binding = http.bindAndHandleAsync(asyncHttpSuccessFunction, toHost(host, port), serverSettings, 1, loggingAdapter, materializer) - - val response = http.singleRequest(HttpRequest.create(s"http://$host:$port/").withMethod(HttpMethods.GET), materializer) - - waitFor(response) - waitFor(binding).unbind() - } - - "have serverLayer methods" in { - // TODO actually cover these with runtime tests, compile only for now - pending - - http.serverLayer(materializer) - - val serverSettings = ServerSettings.create(system) - http.serverLayer(serverSettings, materializer) - - val remoteAddress = Optional.empty[InetSocketAddress]() - http.serverLayer(serverSettings, remoteAddress, materializer) - - val loggingAdapter = NoLogging - http.serverLayer(serverSettings, remoteAddress, loggingAdapter, materializer) - } - - "create a cached connection pool (with a ConnectToHttp and a materializer)" in { - val (host, port, binding) = runServer() - - val poolFlow: Flow[Pair[HttpRequest, NotUsed], Pair[Try[HttpResponse], NotUsed], HostConnectionPool] = - http.cachedHostConnectionPool[NotUsed](toHost(host, port), materializer) - - val pair: Pair[HostConnectionPool, CompletionStage[Pair[Try[HttpResponse], NotUsed]]] = - Source.single(new Pair(HttpRequest.GET(s"http://$host:$port/"), NotUsed.getInstance())) - .viaMat(poolFlow, Keep.right[NotUsed, HostConnectionPool]) - .toMat( - Sink.head(), - Keep.both[HostConnectionPool, CompletionStage[Pair[Try[HttpResponse], NotUsed]]]) - .run(materializer) - - waitFor(pair.second).first.isSuccess should be(true) - binding.unbind() - } - - "create a cached connection pool to a https server (with four parameters)" in { - // requires https - pending - val (host, port, binding) = runServer() - - val poolFlow: Flow[Pair[HttpRequest, NotUsed], Pair[Try[HttpResponse], NotUsed], HostConnectionPool] = - http.cachedHostConnectionPool[NotUsed](toHost(host, port), poolSettings, loggingAdapter, materializer) - - val pair: Pair[HostConnectionPool, CompletionStage[Pair[Try[HttpResponse], NotUsed]]] = - Source.single(new Pair(HttpRequest.GET(s"http://$host:$port/"), NotUsed.getInstance())) - .viaMat(poolFlow, Keep.right[NotUsed, HostConnectionPool]) - .toMat( - Sink.head(), - Keep.both[HostConnectionPool, CompletionStage[Pair[Try[HttpResponse], NotUsed]]]) - .run(materializer) - - waitFor(pair.second).first.get.status() should be(StatusCodes.OK) - binding.unbind() - } - - "create a cached connection pool (with a String and a Materializer)" in { - val (host, port, binding) = runServer() - - val poolFlow: Flow[Pair[HttpRequest, NotUsed], Pair[Try[HttpResponse], NotUsed], HostConnectionPool] = - http.cachedHostConnectionPool[NotUsed](s"http://$host:$port", materializer) - - val pair: Pair[HostConnectionPool, CompletionStage[Pair[Try[HttpResponse], NotUsed]]] = - Source.single(new Pair(HttpRequest.GET(s"http://$host:$port/"), NotUsed.getInstance())) - .viaMat(poolFlow, Keep.right[NotUsed, HostConnectionPool]) - .toMat( - Sink.head(), - Keep.both[HostConnectionPool, CompletionStage[Pair[Try[HttpResponse], NotUsed]]]) - .run(materializer) - - waitFor(pair.second).first.get.status() should be(StatusCodes.OK) - binding.unbind() - } - - "create a host connection pool (with a ConnectHttp and a Materializer)" in { - val (host, port, binding) = runServer() - - val poolFlow = http.newHostConnectionPool[NotUsed](toHost(host, port), materializer) - - val pair: Pair[HostConnectionPool, CompletionStage[Pair[Try[HttpResponse], NotUsed]]] = - Source.single(new Pair(get(host, port), NotUsed.getInstance())) - .viaMat(poolFlow, Keep.right[NotUsed, HostConnectionPool]) - .toMat( - Sink.head(), - Keep.both[HostConnectionPool, CompletionStage[Pair[Try[HttpResponse], NotUsed]]]) - .run(materializer) - - waitFor(pair.second).first.get.status() should be(StatusCodes.OK) - pair.first.shutdown(system.dispatcher) - binding.unbind() - } - - "create a host connection pool to a https server (with four parameters)" in { - // requires https - pending - val (host, port, binding) = runServer() - - val poolFlow = http.newHostConnectionPool[NotUsed](toHost(host, port), poolSettings, loggingAdapter, materializer) - - val pair: Pair[HostConnectionPool, CompletionStage[Pair[Try[HttpResponse], NotUsed]]] = - Source.single(new Pair(get(host, port), NotUsed.getInstance())) - .viaMat(poolFlow, Keep.right[NotUsed, HostConnectionPool]) - .toMat( - Sink.head(), - Keep.both[HostConnectionPool, CompletionStage[Pair[Try[HttpResponse], NotUsed]]]) - .run(materializer) - - waitFor(pair.second).first.get.status() should be(StatusCodes.OK) - pair.first.shutdown(system.dispatcher) - binding.unbind() - } - - "create a host connection pool (with a String and a Materializer)" in { - val (host, port, binding) = runServer() - - val poolFlow = http.newHostConnectionPool[NotUsed](s"http://$host:$port", materializer) - - val pair: Pair[HostConnectionPool, CompletionStage[Pair[Try[HttpResponse], NotUsed]]] = - Source.single(new Pair(get(host, port), NotUsed.getInstance())) - .viaMat(poolFlow, Keep.right[NotUsed, HostConnectionPool]) - .toMat( - Sink.head(), - Keep.both[HostConnectionPool, CompletionStage[Pair[Try[HttpResponse], NotUsed]]]) - .run(materializer) - - waitFor(pair.second).first.get.status() should be(StatusCodes.OK) - pair.first.shutdown(system.dispatcher) - binding.unbind() - } - - "allow access to the default client https context" in { - http.defaultClientHttpsContext.isSecure should equal(true) - } - - "allow access to the default server https context" in { - http.defaultServerHttpContext.isSecure should equal(false) - } - - "have client layer methods" in { - // TODO actually cover these with runtime tests, compile only for now - pending - val connectionSettings = ClientConnectionSettings.create(system) - http.clientLayer(headers.Host.create("example.com")) - http.clientLayer(headers.Host.create("example.com"), connectionSettings) - http.clientLayer(headers.Host.create("example.com"), connectionSettings, loggingAdapter) - } - - "create an outgoing connection (with a string)" in { - // this one cannot be tested because it wants to run on port 80 - pending - http.outgoingConnection("example.com"): Flow[HttpRequest, HttpResponse, CompletionStage[OutgoingConnection]] - } - - "create an outgoing connection (with a ConnectHttp)" in { - val (host, port, binding) = runServer() - val flow = http.outgoingConnection(toHost(host, port)) - - val response = Source.single(get(host, port)) - .via(flow) - .toMat(Sink.head(), Keep.right[NotUsed, CompletionStage[HttpResponse]]) - .run(materializer) - - waitFor(response).status() should be(StatusCodes.OK) - binding.unbind() - } - - "create an outgoing connection (with 6 parameters)" in { - val (host, port, binding) = runServer() - val flow = http.outgoingConnection( - toHost(host, port), - Optional.empty(), - ClientConnectionSettings.create(system), - NoLogging) - - val response = Source.single(get(host, port)) - .via(flow) - .toMat(Sink.head(), Keep.right[NotUsed, CompletionStage[HttpResponse]]) - .run(materializer) - - waitFor(response).status() should be(StatusCodes.OK) - binding.unbind() - } - - "allow a single request (with two parameters)" in { - val (host, port, binding) = runServer() - val response = http.singleRequest(HttpRequest.GET(s"http://$host:$port/"), materializer) - - waitFor(response).status() should be(StatusCodes.OK) - binding.unbind() - } - - "allow a single request (with three parameters)" in { - val (host, port, binding) = runServer() - val response = http.singleRequest(HttpRequest.GET(s"http://$host:$port/"), http.defaultClientHttpsContext, materializer) - - waitFor(response).status() should be(StatusCodes.OK) - binding.unbind() - } - - "allow a single request (with five parameters)" in { - val (host, port, binding) = runServer() - val response = http.singleRequest(HttpRequest.GET(s"http://$host:$port/"), http.defaultClientHttpsContext, poolSettings, loggingAdapter, materializer) - - waitFor(response).status() should be(StatusCodes.OK) - binding.unbind() - } - - "interact with a websocket through a flow (with with one parameter)" in { - val (host, port, binding) = runWebsocketServer() - val flow = http.webSocketClientFlow(WebSocketRequest.create(s"ws://$host:$port")) - val pair = Source.single(TextMessage.create("hello")) - .viaMat(flow, Keep.right[NotUsed, CompletionStage[WebSocketUpgradeResponse]]) - .toMat(Sink.head[Message](), Keep.both[CompletionStage[WebSocketUpgradeResponse], CompletionStage[Message]]) - .run(materializer) - - waitFor(pair.first).response.status() should be(StatusCodes.SWITCHING_PROTOCOLS) - waitFor(pair.second).asTextMessage.getStrictText should be("hello") - binding.unbind() - } - - "interact with a websocket through a flow (with five parameters)" in { - val (host, port, binding) = runWebsocketServer() - val flow = http.webSocketClientFlow(WebSocketRequest.create(s"ws://$host:$port"), connectionContext, Optional.empty(), ClientConnectionSettings.create(system), loggingAdapter) - val pair = Source.single(TextMessage.create("hello")) - .viaMat(flow, Keep.right[NotUsed, CompletionStage[WebSocketUpgradeResponse]]) - .toMat(Sink.head[Message](), Keep.both[CompletionStage[WebSocketUpgradeResponse], CompletionStage[Message]]) - .run(materializer) - - waitFor(pair.first).response.status() should be(StatusCodes.SWITCHING_PROTOCOLS) - waitFor(pair.second).asTextMessage.getStrictText should be("hello") - binding.unbind() - } - - } - - def get(host: Host, port: Port) = HttpRequest.GET("/").addHeader(headers.Host.create(host, port)) - - def runServer(): (Host, Port, ServerBinding) = { - val (_, host, port) = TestUtils.temporaryServerHostnameAndPort() - val server = http.bindAndHandleSync(httpSuccessFunction, toHost(host, port), materializer) - - (host, port, waitFor(server)) - } - - def runWebsocketServer(): (Host, Port, ServerBinding) = { - val (_, host, port) = TestUtils.temporaryServerHostnameAndPort() - val server = http.bindAndHandleSync(new Function[HttpRequest, HttpResponse] { - - override def apply(request: HttpRequest): HttpResponse = { - WebSocket.handleWebSocketRequestWith(request, Flow.create[Message]()) - } - }, toHost(host, port), materializer) - - (host, port, waitFor(server)) - } - - private def waitFor[T](completionStage: CompletionStage[T]): T = - completionStage.toCompletableFuture.get(1, TimeUnit.SECONDS) - - override protected def afterAll(): Unit = { - system.terminate() - } -} diff --git a/akka-http-core/src/test/scala/akka/http/javadsl/JavaInitializationSpec.scala b/akka-http-core/src/test/scala/akka/http/javadsl/JavaInitializationSpec.scala deleted file mode 100644 index fbed941271..0000000000 --- a/akka-http-core/src/test/scala/akka/http/javadsl/JavaInitializationSpec.scala +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl - -import org.scalatest.{ Matchers, WordSpec } - -class JavaInitializationSpec extends WordSpec with Matchers { - - "LanguageRange" should { - - "initializes the right field" in { - akka.http.scaladsl.model.headers.LanguageRange.`*` // first we touch the scala one, it should force init the Java one - akka.http.javadsl.model.headers.LanguageRange.ALL // touching this one should not fail - akka.http.javadsl.model.headers.LanguageRanges.ALL // this is recommended and should work well too - } - } -} diff --git a/akka-http-core/src/test/scala/akka/http/javadsl/model/JavaApiSpec.scala b/akka-http-core/src/test/scala/akka/http/javadsl/model/JavaApiSpec.scala deleted file mode 100644 index 19044fb929..0000000000 --- a/akka-http-core/src/test/scala/akka/http/javadsl/model/JavaApiSpec.scala +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model - -import java.util.Optional - -import akka.japi.Pair - -import org.scalatest.{ FreeSpec, MustMatchers } - -import scala.collection.JavaConverters._ - -class JavaApiSpec extends FreeSpec with MustMatchers { - "The Java API should work for" - { - "work with Uris" - { - "query" in { - Uri.create("/abc") - .query(Query.create(Pair.create("name", "paul"))) must be(Uri.create("/abc?name=paul")) - } - "query(Iterable)" in { - Uri.create("/abc") - .query(Query.create(Iterable(Pair.create("name", "tom")).asJava)) must be(Uri.create("/abc?name=tom")) - } - "addSegment" in { - Uri.create("/abc") - .addPathSegment("def") must be(Uri.create("/abc/def")) - - Uri.create("/abc/") - .addPathSegment("def") must be(Uri.create("/abc/def")) - } - "scheme/host/port" in { - Uri.create("/abc") - .scheme("http") - .host("example.com") - .port(8258) must be(Uri.create("http://example.com:8258/abc")) - } - "toRelative" in { - Uri.create("http://example.com/abc") - .toRelative must be(Uri.create("/abc")) - } - "pathSegments" in { - Uri.create("/abc/def/ghi/jkl") - .pathSegments().asScala.toSeq must contain inOrderOnly ("abc", "def", "ghi", "jkl") - } - "access parameterMap" in { - Uri.create("/abc?name=blub&age=28") - .query().toMap.asScala must contain allOf ("name" → "blub", "age" → "28") - } - "access parameters" in { - val Seq(param1, param2, param3) = - Uri.create("/abc?name=blub&age=28&name=blub2") - .query().toList.asScala.map(_.toScala) - - param1 must be("name" → "blub") - param2 must be("age" → "28") - param3 must be("name" → "blub2") - } - "access single parameter" in { - val query = Uri.create("/abc?name=blub").query() - query.get("name") must be(Optional.of("blub")) - query.get("age") must be(Optional.empty()) - - Uri.create("/abc?name=blub&name=blib").query.get("name") must be(Optional.of("blub")) - } - } - } -} diff --git a/akka-http-core/src/test/scala/akka/http/javadsl/model/JavaApiTestCaseSpecs.scala b/akka-http-core/src/test/scala/akka/http/javadsl/model/JavaApiTestCaseSpecs.scala deleted file mode 100644 index 6cf8e71254..0000000000 --- a/akka-http-core/src/test/scala/akka/http/javadsl/model/JavaApiTestCaseSpecs.scala +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model - -import java.util.Optional -import javax.net.ssl.{ SSLParameters, SSLContext } - -import akka.http.javadsl.model.headers.Cookie -import akka.http.scaladsl.model -import akka.http.scaladsl.model.headers.BasicHttpCredentials -import akka.stream.TLSClientAuth -import org.scalatest.{ FreeSpec, MustMatchers } - -import scala.collection.immutable - -class JavaApiTestCaseSpecs extends FreeSpec with MustMatchers { - "JavaApiTestCases should work as intended" - { - "buildRequest" in { - JavaApiTestCases.buildRequest() must be( - model.HttpRequest( - model.HttpMethods.POST, - uri = "/send")) - } - "handleRequest" - { - "wrong method" in { - JavaApiTestCases.handleRequest(model.HttpRequest(model.HttpMethods.HEAD)) must be( - model.HttpResponse(model.StatusCodes.MethodNotAllowed, entity = "Unsupported method")) - } - "missing path" in { - JavaApiTestCases.handleRequest(model.HttpRequest(uri = "/blubber")) must be( - model.HttpResponse(model.StatusCodes.NotFound, entity = "Not found")) - } - "happy path" - { - "with name parameter" in { - JavaApiTestCases.handleRequest(model.HttpRequest(uri = "/hello?name=Peter")) must be( - model.HttpResponse(entity = "Hello Peter!")) - } - "without name parameter" in { - JavaApiTestCases.handleRequest(model.HttpRequest(uri = "/hello")) must be( - model.HttpResponse(entity = "Hello Mister X!")) - } - } - } - "addAuthentication" in { - JavaApiTestCases.addAuthentication(model.HttpRequest()) must be( - model.HttpRequest(headers = immutable.Seq(model.headers.Authorization(BasicHttpCredentials("username", "password"))))) - } - "removeCookies" in { - val testRequest = model.HttpRequest(headers = immutable.Seq(Cookie.create("test", "blub"))) - JavaApiTestCases.removeCookies(testRequest) must be( - model.HttpRequest()) - } - "createUriForOrder" in { - JavaApiTestCases.createUriForOrder("123", "149", "42") must be( - Uri.create("/order?orderId=123&price=149&amount=42")) - } - "addSessionId" in { - val orderId = Query.create("orderId=123") - Uri.create("/order").query(JavaApiTestCases.addSessionId(orderId)) must be(Uri.create("/order?orderId=123&session=abcdefghijkl")) - } - "create HttpsContext" in { - akka.http.javadsl.ConnectionContext.https( - SSLContext.getDefault, - Optional.empty[java.util.Collection[String]], - Optional.empty[java.util.Collection[String]], - Optional.empty[TLSClientAuth], - Optional.empty[SSLParameters]) mustNot be(null) - } - } -} \ No newline at end of file diff --git a/akka-http-core/src/test/scala/akka/http/javadsl/model/MultipartsSpec.scala b/akka-http-core/src/test/scala/akka/http/javadsl/model/MultipartsSpec.scala deleted file mode 100644 index 227822b570..0000000000 --- a/akka-http-core/src/test/scala/akka/http/javadsl/model/MultipartsSpec.scala +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2016-2016 Lightbend Inc. - */ - -package akka.http.javadsl.model - -import java.util -import com.typesafe.config.{ Config, ConfigFactory } -import scala.concurrent.Await -import scala.concurrent.duration._ -import org.scalatest.{ BeforeAndAfterAll, Inside, Matchers, WordSpec } -import akka.stream.ActorMaterializer -import akka.actor.ActorSystem -import scala.compat.java8.FutureConverters - -class MultipartsSpec extends WordSpec with Matchers with Inside with BeforeAndAfterAll { - - val testConf: Config = ConfigFactory.parseString(""" - akka.event-handlers = ["akka.testkit.TestEventListener"] - akka.loglevel = WARNING""") - implicit val system = ActorSystem(getClass.getSimpleName, testConf) - implicit val materializer = ActorMaterializer() - override def afterAll() = system.terminate() - - "Multiparts.createFormDataFromParts" should { - "create a model from Multiparts.createFormDataBodyPartparts" in { - val streamed = Multiparts.createFormDataFromParts( - Multiparts.createFormDataBodyPart("foo", HttpEntities.create("FOO")), - Multiparts.createFormDataBodyPart("bar", HttpEntities.create("BAR"))) - val strictCS = streamed.toStrict(1000, materializer) - val strict = Await.result(FutureConverters.toScala(strictCS), 1.second) - - strict shouldEqual akka.http.scaladsl.model.Multipart.FormData( - Map("foo" → akka.http.scaladsl.model.HttpEntity("FOO"), "bar" → akka.http.scaladsl.model.HttpEntity("BAR"))) - } - } - - "Multiparts.createFormDataFromFields" should { - "create a model from a map of fields" in { - val fields = new util.HashMap[String, HttpEntity.Strict] - fields.put("foo", HttpEntities.create("FOO")) - val streamed = Multiparts.createFormDataFromFields(fields) - val strictCS = streamed.toStrict(1000, materializer) - val strict = Await.result(FutureConverters.toScala(strictCS), 1.second) - - strict shouldEqual akka.http.scaladsl.model.Multipart.FormData( - Map("foo" → akka.http.scaladsl.model.HttpEntity("FOO"))) - } - } - - "Multiparts.createStrictFormDataFromParts" should { - "create a strict model from Multiparts.createFormDataBodyPartStrict parts" in { - val streamed = Multiparts.createStrictFormDataFromParts( - Multiparts.createFormDataBodyPartStrict("foo", HttpEntities.create("FOO")), - Multiparts.createFormDataBodyPartStrict("bar", HttpEntities.create("BAR"))) - val strict = streamed - - strict shouldEqual akka.http.scaladsl.model.Multipart.FormData( - Map("foo" → akka.http.scaladsl.model.HttpEntity("FOO"), "bar" → akka.http.scaladsl.model.HttpEntity("BAR"))) - } - } -} diff --git a/akka-http-core/src/test/scala/akka/http/scaladsl/ClientServerSpec.scala b/akka-http-core/src/test/scala/akka/http/scaladsl/ClientServerSpec.scala deleted file mode 100644 index 60869305d6..0000000000 --- a/akka-http-core/src/test/scala/akka/http/scaladsl/ClientServerSpec.scala +++ /dev/null @@ -1,579 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl - -import java.io.{ BufferedReader, BufferedWriter, InputStreamReader, OutputStreamWriter } -import java.net.{ BindException, Socket } -import java.util.concurrent.TimeoutException -import java.util.concurrent.atomic.AtomicLong - -import scala.annotation.tailrec -import scala.concurrent.duration._ -import scala.concurrent.{ Await, Future, Promise } -import scala.util.{ Try, Success } -import akka.actor.ActorSystem -import akka.http.impl.util._ -import akka.http.scaladsl.Http.ServerBinding -import akka.http.scaladsl.model.HttpEntity._ -import akka.http.scaladsl.model.HttpMethods._ -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers.{ Accept, Age, Date, Host, Server, `User-Agent` } -import akka.http.scaladsl.settings.{ ConnectionPoolSettings, ClientConnectionSettings, ServerSettings } -import akka.stream.scaladsl._ -import akka.stream.stage.{ GraphStage, GraphStageLogic, InHandler, OutHandler } -import akka.stream.testkit._ -import akka.stream._ -import akka.testkit.EventFilter -import akka.util.ByteString -import com.typesafe.config.{ Config, ConfigFactory } -import org.scalatest.{ BeforeAndAfterAll, Matchers, WordSpec } -import org.scalatest.concurrent.ScalaFutures -import org.scalatest.concurrent.Eventually.eventually - -class ClientServerSpec extends WordSpec with Matchers with BeforeAndAfterAll with ScalaFutures { - val testConf: Config = ConfigFactory.parseString(""" - akka.loggers = ["akka.testkit.TestEventListener"] - akka.loglevel = ERROR - akka.stdout-loglevel = ERROR - windows-connection-abort-workaround-enabled = auto - akka.log-dead-letters = OFF - akka.http.server.request-timeout = infinite""") - implicit val system = ActorSystem(getClass.getSimpleName, testConf) - import system.dispatcher - implicit val materializer = ActorMaterializer() - implicit val patience = PatienceConfig(3.seconds) - - val testConf2: Config = - ConfigFactory.parseString("akka.stream.materializer.subscription-timeout.timeout = 1 s") - .withFallback(testConf) - val system2 = ActorSystem(getClass.getSimpleName, testConf2) - val materializer2 = ActorMaterializer.create(system2) - - "The low-level HTTP infrastructure" should { - - "properly bind a server" in { - val (_, hostname, port) = TestUtils.temporaryServerHostnameAndPort() - val probe = TestSubscriber.manualProbe[Http.IncomingConnection]() - val binding = Http().bind(hostname, port).toMat(Sink.fromSubscriber(probe))(Keep.left).run() - val sub = probe.expectSubscription() // if we get it we are bound - val address = Await.result(binding, 1.second).localAddress - sub.cancel() - } - - "report failure if bind fails" in EventFilter[BindException](occurrences = 2).intercept { - val (_, hostname, port) = TestUtils.temporaryServerHostnameAndPort() - val binding = Http().bind(hostname, port) - val probe1 = TestSubscriber.manualProbe[Http.IncomingConnection]() - // Bind succeeded, we have a local address - val b1 = Await.result(binding.to(Sink.fromSubscriber(probe1)).run(), 3.seconds) - probe1.expectSubscription() - - val probe2 = TestSubscriber.manualProbe[Http.IncomingConnection]() - an[BindFailedException] shouldBe thrownBy { Await.result(binding.to(Sink.fromSubscriber(probe2)).run(), 3.seconds) } - probe2.expectSubscriptionAndError() - - val probe3 = TestSubscriber.manualProbe[Http.IncomingConnection]() - an[BindFailedException] shouldBe thrownBy { Await.result(binding.to(Sink.fromSubscriber(probe3)).run(), 3.seconds) } - probe3.expectSubscriptionAndError() - - // Now unbind the first - Await.result(b1.unbind(), 1.second) - probe1.expectComplete() - - if (!akka.util.Helpers.isWindows) { - val probe4 = TestSubscriber.manualProbe[Http.IncomingConnection]() - // Bind succeeded, we have a local address - val b2 = Await.result(binding.to(Sink.fromSubscriber(probe4)).run(), 3.seconds) - probe4.expectSubscription() - - // clean up - Await.result(b2.unbind(), 1.second) - } - } - - "properly terminate client when server is not running" in Utils.assertAllStagesStopped { - for (i ← 1 to 10) - withClue(s"iterator $i: ") { - Source.single(HttpRequest(HttpMethods.POST, "/test", List.empty, HttpEntity(MediaTypes.`text/plain`.withCharset(HttpCharsets.`UTF-8`), "buh"))) - .via(Http(actorSystem).outgoingConnection("localhost", 7777)) - .runWith(Sink.head) - .failed - .futureValue shouldBe a[StreamTcpException] - } - } - - "run with bindAndHandleSync" in { - val (_, hostname, port) = TestUtils.temporaryServerHostnameAndPort() - val binding = Http().bindAndHandleSync(_ ⇒ HttpResponse(), hostname, port) - val b1 = Await.result(binding, 3.seconds) - - val (_, f) = Http().outgoingConnection(hostname, port) - .runWith(Source.single(HttpRequest(uri = "/abc")), Sink.head) - - Await.result(f, 1.second) - Await.result(b1.unbind(), 1.second) - } - - "prevent more than the configured number of max-connections with bindAndHandle" in { - val (_, hostname, port) = TestUtils.temporaryServerHostnameAndPort() - val settings = ServerSettings(system).withMaxConnections(1) - - val receivedSlow = Promise[Long]() - val receivedFast = Promise[Long]() - - def handle(req: HttpRequest): Future[HttpResponse] = { - req.uri.path.toString match { - case "/slow" ⇒ - receivedSlow.complete(Success(System.nanoTime())) - akka.pattern.after(1.seconds, system.scheduler)(Future.successful(HttpResponse())) - case "/fast" ⇒ - receivedFast.complete(Success(System.nanoTime())) - Future.successful(HttpResponse()) - } - } - - val binding = Http().bindAndHandleAsync(handle, hostname, port, settings = settings) - val b1 = Await.result(binding, 3.seconds) - - def runRequest(uri: Uri): Unit = - Http().outgoingConnection(hostname, port) - .runWith(Source.single(HttpRequest(uri = uri)), Sink.head) - - runRequest("/slow") - - // wait until first request was received (but not yet answered) - val slowTime = Await.result(receivedSlow.future, 2.second) - - // should be blocked by the slow connection still being open - runRequest("/fast") - - val fastTime = Await.result(receivedFast.future, 2.second) - val diff = fastTime - slowTime - diff should be > 1000000000L // the diff must be at least the time to complete the first request and to close the first connection - - Await.result(b1.unbind(), 1.second) - } - - "timeouts" should { - def bindServer(hostname: String, port: Int, serverTimeout: FiniteDuration): (Promise[Long], ServerBinding) = { - val s = ServerSettings(system) - val settings = s.withTimeouts(s.timeouts.withIdleTimeout(serverTimeout)) - - val receivedRequest = Promise[Long]() - - def handle(req: HttpRequest): Future[HttpResponse] = { - receivedRequest.complete(Success(System.nanoTime())) - Promise().future // never complete the request with a response; we're waiting for the timeout to happen, nothing else - } - - val binding = Http().bindAndHandleAsync(handle, hostname, port, settings = settings) - val b1 = Await.result(binding, 3.seconds) - (receivedRequest, b1) - } - - "support server timeouts" should { - "close connection with idle client after idleTimeout" in { - val serverTimeout = 300.millis - val (_, hostname, port) = TestUtils.temporaryServerHostnameAndPort() - val (receivedRequest: Promise[Long], b1: ServerBinding) = bindServer(hostname, port, serverTimeout) - - try { - def runIdleRequest(uri: Uri): Future[HttpResponse] = { - val itNeverEnds = Chunked.fromData(ContentTypes.`text/plain(UTF-8)`, Source.maybe[ByteString]) - Http().outgoingConnection(hostname, port) - .runWith(Source.single(HttpRequest(PUT, uri, entity = itNeverEnds)), Sink.head) - ._2 - } - - val clientsResponseFuture = runIdleRequest("/") - - // await for the server to get the request - val serverReceivedRequestAtNanos = Await.result(receivedRequest.future, 2.seconds) - - // waiting for the timeout to happen on the client - intercept[StreamTcpException] { - Await.result(clientsResponseFuture, 2.second) - } - - (System.nanoTime() - serverReceivedRequestAtNanos).millis should be >= serverTimeout - } finally Await.result(b1.unbind(), 1.second) - } - } - - "support client timeouts" should { - "close connection with idle server after idleTimeout (using connection level client API)" in { - val serverTimeout = 10.seconds - - val cs = ClientConnectionSettings(system) - val clientTimeout = 345.millis - val clientSettings = cs.withIdleTimeout(clientTimeout) - - val (_, hostname, port) = TestUtils.temporaryServerHostnameAndPort() - val (receivedRequest: Promise[Long], b1: ServerBinding) = bindServer(hostname, port, serverTimeout) - - try { - def runRequest(uri: Uri): Future[HttpResponse] = { - val itNeverSends = Chunked.fromData(ContentTypes.`text/plain(UTF-8)`, Source.maybe[ByteString]) - Http().outgoingConnection(hostname, port, settings = clientSettings) - .runWith(Source.single(HttpRequest(POST, uri, entity = itNeverSends)), Sink.head) - ._2 - } - - val clientsResponseFuture = runRequest("/") - - // await for the server to get the request - val serverReceivedRequestAtNanos = Await.result(receivedRequest.future, 2.seconds) - - // waiting for the timeout to happen on the client - intercept[TimeoutException] { - Await.result(clientsResponseFuture, 2.second) - } - val actualTimeout = System.nanoTime() - serverReceivedRequestAtNanos - actualTimeout.nanos should be >= clientTimeout - actualTimeout.nanos should be < serverTimeout - } finally Await.result(b1.unbind(), 1.second) - } - - "close connection with idle server after idleTimeout (using pool level client API)" in { - val serverTimeout = 10.seconds - - val cs = ConnectionPoolSettings(system) - val clientTimeout = 345.millis - val clientPoolSettings = cs.withIdleTimeout(clientTimeout) - - val (_, hostname, port) = TestUtils.temporaryServerHostnameAndPort() - val (receivedRequest: Promise[Long], b1: ServerBinding) = bindServer(hostname, port, serverTimeout) - - try { - val pool = Http().cachedHostConnectionPool[Int](hostname, port, clientPoolSettings) - - def runRequest(uri: Uri): Future[(Try[HttpResponse], Int)] = { - val itNeverSends = Chunked.fromData(ContentTypes.`text/plain(UTF-8)`, Source.maybe[ByteString]) - Source.single(HttpRequest(POST, uri, entity = itNeverSends) → 1) - .via(pool) - .runWith(Sink.head) - } - - val clientsResponseFuture = runRequest("/") - - // await for the server to get the request - val serverReceivedRequestAtNanos = Await.result(receivedRequest.future, 2.seconds) - - // waiting for the timeout to happen on the client - intercept[TimeoutException] { - Await.result(clientsResponseFuture, 2.second) - } - val actualTimeout = System.nanoTime() - serverReceivedRequestAtNanos - actualTimeout.nanos should be >= clientTimeout - actualTimeout.nanos should be < serverTimeout - } finally Await.result(b1.unbind(), 1.second) - } - - "close connection with idle server after idleTimeout (using request level client API)" in { - val serverTimeout = 10.seconds - - val cs = ConnectionPoolSettings(system) - val clientTimeout = 345.millis - val clientPoolSettings = cs.withIdleTimeout(clientTimeout) - - val (_, hostname, port) = TestUtils.temporaryServerHostnameAndPort() - val (receivedRequest: Promise[Long], b1: ServerBinding) = bindServer(hostname, port, serverTimeout) - - try { - def runRequest(uri: Uri): Future[HttpResponse] = { - val itNeverSends = Chunked.fromData(ContentTypes.`text/plain(UTF-8)`, Source.maybe[ByteString]) - Http().singleRequest(HttpRequest(POST, uri, entity = itNeverSends), settings = clientPoolSettings) - } - - val clientsResponseFuture = runRequest(s"http://$hostname:$port/") - - // await for the server to get the request - val serverReceivedRequestAtNanos = Await.result(receivedRequest.future, 2.seconds) - - // waiting for the timeout to happen on the client - intercept[TimeoutException] { - Await.result(clientsResponseFuture, 3.second) - } - val actualTimeout = System.nanoTime() - serverReceivedRequestAtNanos - actualTimeout.nanos should be >= clientTimeout - actualTimeout.nanos should be < serverTimeout - } finally Await.result(b1.unbind(), 1.second) - } - } - } - - "log materialization errors in `bindAndHandle`" which { - - "are triggered in `transform`" in Utils.assertAllStagesStopped { - // FIXME racy feature, needs https://github.com/akka/akka/issues/17849 to be fixed - pending - val (_, hostname, port) = TestUtils.temporaryServerHostnameAndPort() - val flow = Flow[HttpRequest].transform[HttpResponse](() ⇒ sys.error("BOOM")) - val binding = Http(system2).bindAndHandle(flow, hostname, port)(materializer2) - val b1 = Await.result(binding, 3.seconds) - - EventFilter[RuntimeException](message = "BOOM", occurrences = 1).intercept { - val (_, responseFuture) = - Http(system2).outgoingConnection(hostname, port).runWith(Source.single(HttpRequest()), Sink.head)(materializer2) - try Await.result(responseFuture, 5.second).status should ===(StatusCodes.InternalServerError) - catch { - case _: StreamTcpException ⇒ - // Also fine, depends on the race between abort and 500, caused by materialization panic which - // tries to tear down everything, but the order is nondeterministic - } - }(system2) - Await.result(b1.unbind(), 1.second) - }(materializer2) - - "are triggered in `mapMaterialized`" in Utils.assertAllStagesStopped { - // FIXME racy feature, needs https://github.com/akka/akka/issues/17849 to be fixed - pending - val (_, hostname, port) = TestUtils.temporaryServerHostnameAndPort() - val flow = Flow[HttpRequest].map(_ ⇒ HttpResponse()).mapMaterializedValue(_ ⇒ sys.error("BOOM")) - val binding = Http(system2).bindAndHandle(flow, hostname, port)(materializer2) - val b1 = Await.result(binding, 1.seconds) - - EventFilter[RuntimeException](message = "BOOM", occurrences = 1).intercept { - val (_, responseFuture) = - Http(system2).outgoingConnection(hostname, port).runWith(Source.single(HttpRequest()), Sink.head)(materializer2) - try Await.result(responseFuture, 5.seconds).status should ===(StatusCodes.InternalServerError) - catch { - case _: StreamTcpException ⇒ - // Also fine, depends on the race between abort and 500, caused by materialization panic which - // tries to tear down everything, but the order is nondeterministic - } - }(system2) - Await.result(b1.unbind(), 1.second) - }(materializer2) - - "stop stages on failure" in Utils.assertAllStagesStopped { - val (_, hostname, port) = TestUtils.temporaryServerHostnameAndPort() - val stageCounter = new AtomicLong(0) - val cancelCounter = new AtomicLong(0) - val stage: GraphStage[FlowShape[HttpRequest, HttpResponse]] = new GraphStage[FlowShape[HttpRequest, HttpResponse]] { - val in = Inlet[HttpRequest]("request.in") - val out = Outlet[HttpResponse]("response.out") - - override def shape: FlowShape[HttpRequest, HttpResponse] = FlowShape.of(in, out) - - override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = new GraphStageLogic(shape) with InHandler with OutHandler { - override def preStart(): Unit = stageCounter.incrementAndGet() - override def postStop(): Unit = stageCounter.decrementAndGet() - override def onPush(): Unit = push(out, HttpResponse(entity = stageCounter.get().toString)) - override def onPull(): Unit = pull(in) - override def onDownstreamFinish(): Unit = cancelCounter.incrementAndGet() - - setHandlers(in, out, this) - } - } - - def performFaultyRequest() = { - val socket = new Socket(hostname, port) - val os = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream, "UTF8")) - - os.write("YOLO") - os.close() - - socket.close() - } - - def performValidRequest() = Http().outgoingConnection(hostname, port).runWith(Source.single(HttpRequest()), Sink.ignore) - - def assertCounters(stage: Int, cancel: Int) = eventually(timeout(1.second)) { - stageCounter.get shouldEqual stage - cancelCounter.get shouldEqual cancel - } - - val bind = Await.result(Http().bindAndHandle(Flow.fromGraph(stage), hostname, port)(materializer2), 1.seconds) - - performValidRequest() - assertCounters(0, 1) - - performFaultyRequest() - assertCounters(0, 2) - - performValidRequest() - assertCounters(0, 3) - - Await.result(bind.unbind(), 1.second) - }(materializer2) - } - - "properly complete a simple request/response cycle" in Utils.assertAllStagesStopped { - new TestSetup { - val (clientOut, clientIn) = openNewClientConnection() - val (serverIn, serverOut) = acceptConnection() - - val clientOutSub = clientOut.expectSubscription() - clientOutSub.expectRequest() - clientOutSub.sendNext(HttpRequest(uri = "/abc")) - - val serverInSub = serverIn.expectSubscription() - serverInSub.request(1) - serverIn.expectNext().uri shouldEqual Uri(s"http://$hostname:$port/abc") - - val serverOutSub = serverOut.expectSubscription() - serverOutSub.expectRequest() - serverOutSub.sendNext(HttpResponse(entity = "yeah")) - - val clientInSub = clientIn.expectSubscription() - clientInSub.request(1) - val response = clientIn.expectNext() - toStrict(response.entity) shouldEqual HttpEntity("yeah") - - clientOutSub.sendComplete() - serverIn.expectComplete() - serverOutSub.expectCancellation() - clientIn.expectComplete() - - binding.foreach(_.unbind()) - } - } - - "properly complete a chunked request/response cycle" in Utils.assertAllStagesStopped { - new TestSetup { - val (clientOut, clientIn) = openNewClientConnection() - val (serverIn, serverOut) = acceptConnection() - - val chunks = List(Chunk("abc"), Chunk("defg"), Chunk("hijkl"), LastChunk) - val chunkedContentType: ContentType = MediaTypes.`application/base64` withCharset HttpCharsets.`UTF-8` - val chunkedEntity = HttpEntity.Chunked(chunkedContentType, Source(chunks)) - - val clientOutSub = clientOut.expectSubscription() - clientOutSub.sendNext(HttpRequest(POST, "/chunked", List(Accept(MediaRanges.`*/*`)), chunkedEntity)) - - val serverInSub = serverIn.expectSubscription() - serverInSub.request(1) - private val HttpRequest(POST, uri, List(Accept(Seq(MediaRanges.`*/*`)), Host(_, _), `User-Agent`(_)), - Chunked(`chunkedContentType`, chunkStream), HttpProtocols.`HTTP/1.1`) = serverIn.expectNext() mapHeaders (_.filterNot(_.is("timeout-access"))) - uri shouldEqual Uri(s"http://$hostname:$port/chunked") - Await.result(chunkStream.limit(5).runWith(Sink.seq), 100.millis) shouldEqual chunks - - val serverOutSub = serverOut.expectSubscription() - serverOutSub.expectRequest() - serverOutSub.sendNext(HttpResponse(206, List(Age(42)), chunkedEntity)) - - val clientInSub = clientIn.expectSubscription() - clientInSub.request(1) - val HttpResponse(StatusCodes.PartialContent, List(Age(42), Server(_), Date(_)), - Chunked(`chunkedContentType`, chunkStream2), HttpProtocols.`HTTP/1.1`) = clientIn.expectNext() - Await.result(chunkStream2.limit(1000).runWith(Sink.seq), 100.millis) shouldEqual chunks - - clientOutSub.sendComplete() - serverInSub.request(1) - serverIn.expectComplete() - serverOutSub.expectCancellation() - clientInSub.request(1) - clientIn.expectComplete() - - connSourceSub.cancel() - } - } - - "be able to deal with eager closing of the request stream on the client side" in Utils.assertAllStagesStopped { - new TestSetup { - val (clientOut, clientIn) = openNewClientConnection() - val (serverIn, serverOut) = acceptConnection() - - val clientOutSub = clientOut.expectSubscription() - clientOutSub.sendNext(HttpRequest(uri = "/abc")) - clientOutSub.sendComplete() - // complete early - - val serverInSub = serverIn.expectSubscription() - serverInSub.request(1) - serverIn.expectNext().uri shouldEqual Uri(s"http://$hostname:$port/abc") - - val serverOutSub = serverOut.expectSubscription() - serverOutSub.expectRequest() - serverOutSub.sendNext(HttpResponse(entity = "yeah")) - - val clientInSub = clientIn.expectSubscription() - clientInSub.request(1) - val response = clientIn.expectNext() - toStrict(response.entity) shouldEqual HttpEntity("yeah") - - serverIn.expectComplete() - serverOutSub.expectCancellation() - clientIn.expectComplete() - - connSourceSub.cancel() - } - } - } - - override def afterAll() = { - system.terminate() - system2.shutdown() - } - - class TestSetup { - val (_, hostname, port) = TestUtils.temporaryServerHostnameAndPort() - def configOverrides = "" - - // automatically bind a server - val (connSource, binding: Future[ServerBinding]) = { - val settings = configOverrides.toOption.fold(ServerSettings(system))(ServerSettings(_)) - val connections = Http().bind(hostname, port, settings = settings) - val probe = TestSubscriber.manualProbe[Http.IncomingConnection] - val binding = connections.toMat(Sink.fromSubscriber(probe))(Keep.left).run() - (probe, binding) - } - val connSourceSub = connSource.expectSubscription() - - def openNewClientConnection(settings: ClientConnectionSettings = ClientConnectionSettings(system)) = { - val requestPublisherProbe = TestPublisher.manualProbe[HttpRequest]() - val responseSubscriberProbe = TestSubscriber.manualProbe[HttpResponse]() - - val connectionFuture = Source.fromPublisher(requestPublisherProbe) - .viaMat(Http().outgoingConnection(hostname, port, settings = settings))(Keep.right) - .to(Sink.fromSubscriber(responseSubscriberProbe)).run() - - val connection = Await.result(connectionFuture, 3.seconds) - - connection.remoteAddress.getHostName shouldEqual hostname - connection.remoteAddress.getPort shouldEqual port - requestPublisherProbe → responseSubscriberProbe - } - - def acceptConnection(): (TestSubscriber.ManualProbe[HttpRequest], TestPublisher.ManualProbe[HttpResponse]) = { - connSourceSub.request(1) - val incomingConnection = connSource.expectNext() - val sink = Sink.asPublisher[HttpRequest](false) - val source = Source.asSubscriber[HttpResponse] - - val handler = Flow.fromSinkAndSourceMat(sink, source)(Keep.both) - - val (pub, sub) = incomingConnection.handleWith(handler) - val requestSubscriberProbe = TestSubscriber.manualProbe[HttpRequest]() - val responsePublisherProbe = TestPublisher.manualProbe[HttpResponse]() - - pub.subscribe(requestSubscriberProbe) - responsePublisherProbe.subscribe(sub) - requestSubscriberProbe → responsePublisherProbe - } - - def openClientSocket() = new Socket(hostname, port) - - def write(socket: Socket, data: String) = { - val writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream)) - writer.write(data) - writer.flush() - writer - } - - def readAll(socket: Socket)(reader: BufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream))): (String, BufferedReader) = { - val sb = new java.lang.StringBuilder - val cbuf = new Array[Char](256) - @tailrec def drain(): (String, BufferedReader) = reader.read(cbuf) match { - case -1 ⇒ sb.toString → reader - case n ⇒ sb.append(cbuf, 0, n); drain() - } - drain() - } - } - - def toStrict(entity: HttpEntity): HttpEntity.Strict = Await.result(entity.toStrict(500.millis), 1.second) -} diff --git a/akka-http-core/src/test/scala/akka/http/scaladsl/ClientSpec.scala b/akka-http-core/src/test/scala/akka/http/scaladsl/ClientSpec.scala deleted file mode 100644 index abff177431..0000000000 --- a/akka-http-core/src/test/scala/akka/http/scaladsl/ClientSpec.scala +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (C) 2016 Lightbend Inc. - */ - -package akka.http.scaladsl - -import akka.actor.ActorSystem -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.HttpMethods._ -import akka.stream.ActorMaterializer -import com.typesafe.config.{ Config, ConfigFactory } -import org.scalatest.{ Matchers, WordSpec } -import scala.concurrent.duration._ -import scala.concurrent.{ Await } -import org.scalatest.BeforeAndAfterAll -import akka.testkit.TestKit - -class ClientSpec extends WordSpec with Matchers with BeforeAndAfterAll { - val testConf: Config = ConfigFactory.parseString(""" - akka.loggers = ["akka.testkit.TestEventListener"] - akka.loglevel = ERROR - akka.stdout-loglevel = ERROR - windows-connection-abort-workaround-enabled = auto - akka.log-dead-letters = OFF - akka.http.server.request-timeout = infinite""") - implicit val system = ActorSystem(getClass.getSimpleName, testConf) - implicit val materializer = ActorMaterializer() - - override def afterAll(): Unit = { - TestKit.shutdownActorSystem(system) - } - - "HTTP Client" should { - - "reuse connection pool" in { - val (_, hostname, port) = TestUtils.temporaryServerHostnameAndPort() - val bindingFuture = Http().bindAndHandleSync(_ ⇒ HttpResponse(), hostname, port) - val binding = Await.result(bindingFuture, 3.seconds) - - val respFuture = Http().singleRequest(HttpRequest(POST, s"http://$hostname:$port/")) - val resp = Await.result(respFuture, 3.seconds) - resp.status shouldBe StatusCodes.OK - - Await.result(Http().poolSize, 1.second) shouldEqual 1 - - val respFuture2 = Http().singleRequest(HttpRequest(POST, s"http://$hostname:$port/")) - val resp2 = Await.result(respFuture, 3.seconds) - resp2.status shouldBe StatusCodes.OK - - Await.result(Http().poolSize, 1.second) shouldEqual 1 - - Await.ready(binding.unbind(), 1.second) - } - } -} diff --git a/akka-http-core/src/test/scala/akka/http/scaladsl/SslConfigWarningsSpec.scala b/akka-http-core/src/test/scala/akka/http/scaladsl/SslConfigWarningsSpec.scala deleted file mode 100644 index d5fbc25395..0000000000 --- a/akka-http-core/src/test/scala/akka/http/scaladsl/SslConfigWarningsSpec.scala +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (C) 2016 Lightbend Inc. - */ - -package akka.http.scaladsl - -import akka.actor.ActorSystem -import akka.event.Logging -import akka.http.scaladsl.model.HttpMethods._ -import akka.http.scaladsl.model._ -import akka.stream.ActorMaterializer -import akka.testkit.{ EventFilter, TestKit, TestProbe } -import com.typesafe.config.{ Config, ConfigFactory } -import org.scalatest.{ Matchers, WordSpec } - -import scala.concurrent.Await -import scala.concurrent.duration._ - -class SslConfigWarningsSpec extends WordSpec with Matchers { - val testConf: Config = ConfigFactory.parseString(""" - akka.loglevel = INFO - akka.stdout-loglevel = INFO - akka.log-dead-letters = OFF - akka.http.server.request-timeout = infinite - - akka.ssl-config { - loose { - disableSNI = true - disableHostnameVerification = true - } - } - """) - - "ssl-config loose options should cause warnings to be logged" should { - - "warn if SNI is disabled globally" in { - implicit val system = ActorSystem(getClass.getSimpleName, testConf) - implicit val materializer = ActorMaterializer() - - val p = TestProbe() - system.eventStream.subscribe(p.ref, classOf[Logging.LogEvent]) - - EventFilter.warning(start = "Detected that Server Name Indication (SNI) is disabled globally ", occurrences = 1) intercept { - Http()(system) - } - - // the very big warning shall be logged only once per actor system (extension) - EventFilter.warning(start = "Detected that Server Name Indication (SNI) is disabled globally ", occurrences = 0) intercept { - Http()(system) - } - - TestKit.shutdownActorSystem(system) - } - - "warn if hostname verification is disabled globally" in { - implicit val system = ActorSystem(getClass.getSimpleName, testConf) - implicit val materializer = ActorMaterializer() - - val p = TestProbe() - system.eventStream.subscribe(p.ref, classOf[Logging.LogEvent]) - - val msgStart = "Detected that Hostname Verification is disabled globally " - - EventFilter.warning(start = msgStart, occurrences = 1) intercept { Http()(system) } - - // the very big warning shall be logged only once per actor system (extension) - EventFilter.warning(start = msgStart, occurrences = 0) intercept { Http()(system) } - - TestKit.shutdownActorSystem(system) - } - } -} diff --git a/akka-http-core/src/test/scala/akka/http/scaladsl/TestClient.scala b/akka-http-core/src/test/scala/akka/http/scaladsl/TestClient.scala deleted file mode 100644 index 7f593ef718..0000000000 --- a/akka-http-core/src/test/scala/akka/http/scaladsl/TestClient.scala +++ /dev/null @@ -1,117 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl - -import java.io.File -import java.nio.file.spi.FileSystemProvider -import java.nio.file.{ FileSystem, Path } - -import com.typesafe.config.{ Config, ConfigFactory } - -import scala.util.{ Failure, Success } -import akka.actor.{ ActorSystem, UnhandledMessage } -import akka.stream.{ ActorMaterializer, IOResult } -import akka.stream.scaladsl.{ FileIO, Sink, Source } -import akka.http.scaladsl.model._ -import akka.http.impl.util._ -import akka.util.ByteString - -import scala.concurrent.{ Await, Future } -import scala.concurrent.duration._ - -object TestClient extends App { - val testConf: Config = ConfigFactory.parseString(""" - akka.loglevel = DEBUG - akka.log-dead-letters = off - akka.io.tcp.trace-logging = off""") - implicit val system = ActorSystem("ServerTest", testConf) - implicit val fm = ActorMaterializer() - import system.dispatcher - - installEventStreamLoggerFor[UnhandledMessage] - - val host = "github.com" - - fetchServerVersion1() - - // Console.readLine() - // system.terminate() - - def fetchServerVersion1(): Unit = { - println(s"Fetching HTTPS server version of host `$host` via a direct low-level connection ...") - - val connection = Http().outgoingConnectionHttps(host) - val result = Source.single(HttpRequest()).via(connection).runWith(Sink.head) - result.map(_.header[headers.Server]) onComplete { - case Success(res) ⇒ - println(s"$host is running ${res mkString ", "}") - println() - fetchServerVersion2() - - case Failure(error) ⇒ - println(s"Error: $error") - println() - fetchServerVersion2() - } - } - - def fetchServerVersion2(): Unit = { - println(s"Fetching HTTP server version of host `$host` via the high-level API ...") - val result = Http().singleRequest(HttpRequest(uri = s"https://$host/")) - result.map(_.header[headers.Server]) onComplete { - case Success(res) ⇒ - println(s"$host is running ${res mkString ", "}") - Http().shutdownAllConnectionPools().onComplete { _ ⇒ system.log.info("STOPPED"); shutdown() } - - case Failure(error) ⇒ - println(s"Error: $error") - shutdown() - } - } - - // for gathering dumps of entity and headers from akka http client - // and curl in parallel to compare - def fetchAndStoreABunchOfUrlsWithHttpAndCurl(urls: Seq[String]): Unit = { - assert(urls.nonEmpty) - assert(new File("/tmp/client-dumps/").exists(), "you need to create /tmp/client-dumps/ before running") - - val testConf: Config = ConfigFactory.parseString(""" - akka.loglevel = DEBUG - akka.log-dead-letters = off - akka.io.tcp.trace-logging = off""") - implicit val system = ActorSystem("ServerTest", testConf) - implicit val fm = ActorMaterializer() - import system.dispatcher - - try { - val done = Future.traverse(urls.zipWithIndex) { - case (url, index) ⇒ - Http().singleRequest(HttpRequest(uri = url)).map { response ⇒ - - val path = new File(s"/tmp/client-dumps/akka-body-$index.dump").toPath - val headersPath = new File(s"/tmp/client-dumps/akka-headers-$index.dump").toPath - - import scala.sys.process._ - (s"""curl -D /tmp/client-dumps/curl-headers-$index.dump $url""" #> new File(s"/tmp/client-dumps/curl-body-$index.dump")).! - - val headers = Source(response.headers).map(header ⇒ ByteString(header.name + ": " + header.value + "\n")) - .runWith(FileIO.toPath(headersPath)) - - val body = response.entity.dataBytes - .runWith(FileIO.toPath(path)) - .map(res ⇒ (url, path, res)): Future[(String, Path, IOResult)] - - headers.flatMap(_ ⇒ body) - } - } - - println("Fetched urls: " + Await.result(done, 10.minutes)) - } finally { - Http().shutdownAllConnectionPools().flatMap(_ ⇒ system.terminate()) - } - } - - def shutdown(): Unit = system.terminate() -} \ No newline at end of file diff --git a/akka-http-core/src/test/scala/akka/http/scaladsl/TestServer.scala b/akka-http-core/src/test/scala/akka/http/scaladsl/TestServer.scala deleted file mode 100644 index 071b46cd5b..0000000000 --- a/akka-http-core/src/test/scala/akka/http/scaladsl/TestServer.scala +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl - -import javax.net.ssl.SSLContext - -import akka.NotUsed - -import scala.concurrent.duration._ -import scala.concurrent.Await -import akka.actor.ActorSystem -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.ws._ -import akka.stream._ -import akka.stream.scaladsl.{ Source, Flow } -import com.typesafe.config.{ ConfigFactory, Config } -import HttpMethods._ - -object TestServer extends App { - val testConf: Config = ConfigFactory.parseString(""" - akka.loglevel = INFO - akka.log-dead-letters = off - akka.stream.materializer.debug.fuzzing-mode = off - akka.actor.serialize-creators = off - akka.actor.serialize-messages = off - akka.actor.default-dispatcher.throughput = 1000 - """) - implicit val system = ActorSystem("ServerTest", testConf) - - val settings = ActorMaterializerSettings(system) - .withFuzzing(false) - // .withSyncProcessingLimit(Int.MaxValue) - .withInputBuffer(128, 128) - implicit val fm = ActorMaterializer(settings) - try { - val binding = Http().bindAndHandleSync({ - case req @ HttpRequest(GET, Uri.Path("/"), _, _, _) if req.header[UpgradeToWebSocket].isDefined ⇒ - req.header[UpgradeToWebSocket] match { - case Some(upgrade) ⇒ upgrade.handleMessages(echoWebSocketService) // needed for running the autobahn test suite - case None ⇒ HttpResponse(400, entity = "Not a valid websocket request!") - } - case HttpRequest(GET, Uri.Path("/"), _, _, _) ⇒ index - case HttpRequest(GET, Uri.Path("/ping"), _, _, _) ⇒ HttpResponse(entity = "PONG!") - case HttpRequest(GET, Uri.Path("/crash"), _, _, _) ⇒ sys.error("BOOM!") - case req @ HttpRequest(GET, Uri.Path("/ws-greeter"), _, _, _) ⇒ - req.header[UpgradeToWebSocket] match { - case Some(upgrade) ⇒ upgrade.handleMessages(greeterWebSocketService) - case None ⇒ HttpResponse(400, entity = "Not a valid websocket request!") - } - case _: HttpRequest ⇒ HttpResponse(404, entity = "Unknown resource!") - }, interface = "localhost", port = 9001) - - Await.result(binding, 1.second) // throws if binding fails - println("Server online at http://localhost:9001") - println("Press RETURN to stop...") - Console.readLine() - } finally { - system.terminate() - } - - ////////////// helpers ////////////// - - lazy val index = HttpResponse( - entity = HttpEntity( - ContentTypes.`text/html(UTF-8)`, - """| - | - |

Say hello to akka-http-core!

- |

Defined resources:

- | - | - |""".stripMargin)) - - def echoWebSocketService: Flow[Message, Message, NotUsed] = - Flow[Message] // just let message flow directly to the output - - def greeterWebSocketService: Flow[Message, Message, NotUsed] = - Flow[Message] - .collect { - case TextMessage.Strict(name) ⇒ TextMessage(s"Hello '$name'") - case tm: TextMessage ⇒ TextMessage(Source.single("Hello ") ++ tm.textStream) - // ignore binary messages - } -} diff --git a/akka-http-core/src/test/scala/akka/http/scaladsl/TestUtils.scala b/akka-http-core/src/test/scala/akka/http/scaladsl/TestUtils.scala deleted file mode 100644 index 9792439070..0000000000 --- a/akka-http-core/src/test/scala/akka/http/scaladsl/TestUtils.scala +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl - -import java.net.InetSocketAddress -import java.nio.channels.ServerSocketChannel - -object TestUtils { - def temporaryServerAddress(interface: String = "127.0.0.1"): InetSocketAddress = { - val serverSocket = ServerSocketChannel.open() - try { - serverSocket.socket.bind(new InetSocketAddress(interface, 0)) - val port = serverSocket.socket.getLocalPort - new InetSocketAddress(interface, port) - } finally serverSocket.close() - } - - def temporaryServerHostnameAndPort(interface: String = "127.0.0.1"): (InetSocketAddress, String, Int) = { - val socketAddress = temporaryServerAddress(interface) - (socketAddress, socketAddress.getHostName, socketAddress.getPort) - } -} diff --git a/akka-http-core/src/test/scala/akka/http/scaladsl/TightRequestTimeoutSpec.scala b/akka-http-core/src/test/scala/akka/http/scaladsl/TightRequestTimeoutSpec.scala deleted file mode 100644 index 30fd489929..0000000000 --- a/akka-http-core/src/test/scala/akka/http/scaladsl/TightRequestTimeoutSpec.scala +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl - -import java.io.{ BufferedReader, BufferedWriter, InputStreamReader, OutputStreamWriter } -import java.net.{ BindException, Socket } -import java.util.concurrent.TimeoutException -import akka.actor.ActorSystem -import akka.event.Logging -import akka.event.Logging.LogEvent -import akka.http.impl.util._ -import akka.http.scaladsl.Http.ServerBinding -import akka.http.scaladsl.model.HttpEntity._ -import akka.http.scaladsl.model.HttpMethods._ -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers._ -import akka.http.scaladsl.settings.{ ClientConnectionSettings, ConnectionPoolSettings, ServerSettings } -import akka.stream.scaladsl._ -import akka.stream.testkit._ -import akka.stream.{ OverflowStrategy, ActorMaterializer, BindFailedException, StreamTcpException } -import akka.testkit.{ TestProbe, EventFilter } -import akka.util.ByteString -import com.typesafe.config.{ Config, ConfigFactory } -import org.scalatest.concurrent.ScalaFutures -import org.scalatest.{ BeforeAndAfterAll, Matchers, WordSpec } -import scala.annotation.tailrec -import scala.concurrent.duration._ -import scala.concurrent.{ Await, Future, Promise } -import scala.util.{ Success, Try } -import akka.testkit.TestKit - -class TightRequestTimeoutSpec extends WordSpec with Matchers with BeforeAndAfterAll with ScalaFutures { - val testConf: Config = ConfigFactory.parseString(""" - akka.loggers = ["akka.testkit.TestEventListener"] - akka.loglevel = ERROR - akka.stdout-loglevel = ERROR - windows-connection-abort-workaround-enabled = auto - akka.log-dead-letters = OFF - akka.http.server.request-timeout = 10ms""") - - implicit val system = ActorSystem(getClass.getSimpleName, testConf) - import system.dispatcher - implicit val materializer = ActorMaterializer() - implicit val patience = PatienceConfig(3.seconds) - - override def afterAll(): Unit = { - TestKit.shutdownActorSystem(system) - } - - "Tight request timeout" should { - - "not cause double push error caused by the late response attemting to push" in { - val (_, hostname, port) = TestUtils.temporaryServerHostnameAndPort() - val slowHandler = Flow[HttpRequest].map(_ ⇒ HttpResponse()).delay(500.millis, OverflowStrategy.backpressure) - val binding = Http().bindAndHandle(slowHandler, hostname, port) - - val p = TestProbe() - system.eventStream.subscribe(p.ref, classOf[Logging.Error]) - - val response = Http().singleRequest(HttpRequest(uri = s"http://$hostname:$port/")).futureValue - response.status should ===(StatusCodes.ServiceUnavailable) // the timeout response - - p.expectNoMsg(1.second) // here the double push might happen - - binding.flatMap(_.unbind()).futureValue - } - - } -} diff --git a/akka-http-core/src/test/scala/akka/http/scaladsl/model/DateTimeSpec.scala b/akka-http-core/src/test/scala/akka/http/scaladsl/model/DateTimeSpec.scala deleted file mode 100644 index 5942f10797..0000000000 --- a/akka-http-core/src/test/scala/akka/http/scaladsl/model/DateTimeSpec.scala +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model - -import java.util.TimeZone -import scala.util.Random -import org.scalatest.{ Matchers, WordSpec } -import org.scalatest.matchers.{ Matcher, MatchResult } - -class DateTimeSpec extends WordSpec with Matchers { - - val GMT = TimeZone.getTimeZone("GMT") - val specificClicks = DateTime(2011, 7, 12, 14, 8, 12).clicks - val startClicks = DateTime(1800, 1, 1, 0, 0, 0).clicks - val maxClickDelta = DateTime(2199, 12, 31, 23, 59, 59).clicks - startClicks - val random = new Random() - val httpDateTimes = Stream.continually { - DateTime(startClicks + math.abs(random.nextLong()) % maxClickDelta) - } - - "DateTime.toRfc1123DateTimeString" should { - "properly print a known date" in { - DateTime(specificClicks).toRfc1123DateTimeString shouldEqual "Tue, 12 Jul 2011 14:08:12 GMT" - DateTime(2011, 7, 12, 14, 8, 12).toRfc1123DateTimeString shouldEqual "Tue, 12 Jul 2011 14:08:12 GMT" - } - "behave exactly as a corresponding formatting via SimpleDateFormat" in { - val Rfc1123Format = { - val fmt = new java.text.SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", java.util.Locale.US) - fmt.setTimeZone(GMT) - fmt - } - def rfc1123Format(dt: DateTime) = Rfc1123Format.format(new java.util.Date(dt.clicks)) - val matchSimpleDateFormat: Matcher[DateTime] = Matcher { dt: DateTime ⇒ - MatchResult( - dt.toRfc1123DateTimeString == rfc1123Format(dt), - dt.toRfc1123DateTimeString + " != " + rfc1123Format(dt), - dt.toRfc1123DateTimeString + " == " + rfc1123Format(dt)) - } - all(httpDateTimes.take(10000)) should matchSimpleDateFormat - } - } - - "DateTime.toIsoDateTimeString" should { - "properly print a known date" in { - DateTime(specificClicks).toIsoDateTimeString shouldEqual "2011-07-12T14:08:12" - } - } - - "DateTime.fromIsoDateTimeString" should { - "properly parse a legal string" in { - DateTime.fromIsoDateTimeString("2011-07-12T14:08:12") shouldBe Some(DateTime(specificClicks)) - } - "properly parse a legal extended string" in { - DateTime.fromIsoDateTimeString("2011-07-12T14:08:12.123Z") shouldBe Some(DateTime(specificClicks)) - } - "fail on an illegal string" in { - DateTime.fromIsoDateTimeString("2011-07-12T14:08:12x") shouldBe None - DateTime.fromIsoDateTimeString("2011-07-12T14:08_12") shouldBe None - DateTime.fromIsoDateTimeString("201A-07-12T14:08:12") shouldBe None - DateTime.fromIsoDateTimeString("2011-13-12T14:08:12") shouldBe None - } - "fail on an illegal extended string" in { - DateTime.fromIsoDateTimeString("2011-07-12T14:08:12.") shouldBe None - DateTime.fromIsoDateTimeString("2011-07-12T14:08:12.a") shouldBe None - DateTime.fromIsoDateTimeString("2011-07-12T14:08:12.Z") shouldBe None - DateTime.fromIsoDateTimeString("2011-07-12T14:08:12.1") shouldBe None - DateTime.fromIsoDateTimeString("2011-07-12T14:08:12.12") shouldBe None - DateTime.fromIsoDateTimeString("2011-07-12T14:08:12.123") shouldBe None - DateTime.fromIsoDateTimeString("2011-07-12T14:08:12.1234") shouldBe None - DateTime.fromIsoDateTimeString("2011-07-12T14:08:12.1Z") shouldBe None - DateTime.fromIsoDateTimeString("2011-07-12T14:08:12.12Z") shouldBe None - DateTime.fromIsoDateTimeString("2011-07-12T14:08:12.1234Z") shouldBe None - } - } - - "The two DateTime implementations" should { - "allow for transparent round-trip conversions" in { - def roundTrip(dt: DateTime) = DateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second) - val roundTripOk: Matcher[DateTime] = Matcher { dt: DateTime ⇒ - MatchResult( - { val rt = roundTrip(dt); dt == rt && dt.weekday == rt.weekday }, - dt.toRfc1123DateTimeString + " != " + roundTrip(dt).toRfc1123DateTimeString, - dt.toRfc1123DateTimeString + " == " + roundTrip(dt).toRfc1123DateTimeString) - } - all(httpDateTimes.take(10000)) should roundTripOk - } - "properly represent DateTime.MinValue" in { - DateTime.MinValue.toString shouldEqual "1800-01-01T00:00:00" - DateTime(DateTime.MinValue.clicks).toString shouldEqual "1800-01-01T00:00:00" - } - "properly represent DateTime.MaxValue" in { - DateTime.MaxValue.toString shouldEqual "2199-12-31T23:59:59" - DateTime(DateTime.MaxValue.clicks).toString shouldEqual "2199-12-31T23:59:59" - } - } -} diff --git a/akka-http-core/src/test/scala/akka/http/scaladsl/model/EntityDiscardingSpec.scala b/akka-http-core/src/test/scala/akka/http/scaladsl/model/EntityDiscardingSpec.scala deleted file mode 100644 index 404da1e424..0000000000 --- a/akka-http-core/src/test/scala/akka/http/scaladsl/model/EntityDiscardingSpec.scala +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model - -import akka.Done -import akka.http.scaladsl.model.HttpEntity.Chunked -import akka.http.scaladsl.{ Http, TestUtils } -import akka.stream.ActorMaterializer -import akka.stream.scaladsl._ -import akka.testkit.AkkaSpec -import scala.concurrent.duration._ -import akka.util.ByteString - -import scala.concurrent.{ Await, Promise } - -class EntityDiscardingSpec extends AkkaSpec { - - implicit val mat = ActorMaterializer() - - val testData = Vector.tabulate(200)(i ⇒ ByteString(s"row-$i")) - - "HttpRequest" should { - - "discard entity stream after .discardEntityBytes() call" in { - - val p = Promise[Done]() - val s = Source - .fromIterator[ByteString](() ⇒ testData.iterator) - .alsoTo(Sink.onComplete(t ⇒ p.complete(t))) - - val req = HttpRequest(entity = HttpEntity(ContentTypes.`text/csv(UTF-8)`, s)) - val de = req.discardEntityBytes() - - p.future.futureValue should ===(Done) - de.future.futureValue should ===(Done) - } - } - - "HttpResponse" should { - - "discard entity stream after .discardEntityBytes() call" in { - - val p = Promise[Done]() - val s = Source - .fromIterator[ByteString](() ⇒ testData.iterator) - .alsoTo(Sink.onComplete(t ⇒ p.complete(t))) - - val resp = HttpResponse(entity = HttpEntity(ContentTypes.`text/csv(UTF-8)`, s)) - val de = resp.discardEntityBytes() - - p.future.futureValue should ===(Done) - de.future.futureValue should ===(Done) - } - - // TODO consider improving this by storing a mutable "already materialized" flag somewhere - // TODO likely this is going to inter-op with the auto-draining as described in #18716 - "should not allow draining a second time" in { - val (_, host, port) = TestUtils.temporaryServerHostnameAndPort() - val bound = Http().bindAndHandleSync( - req ⇒ - HttpResponse(entity = HttpEntity( - ContentTypes.`text/csv(UTF-8)`, Source.fromIterator[ByteString](() ⇒ testData.iterator))), - host, port).futureValue - - try { - - val response = Http().singleRequest(HttpRequest(uri = s"http://$host:$port/")).futureValue - - val de = response.discardEntityBytes() - de.future.futureValue should ===(Done) - - val de2 = response.discardEntityBytes() - val secondRunException = intercept[IllegalStateException] { Await.result(de2.future, 3.seconds) } - secondRunException.getMessage should include("Source cannot be materialized more than once") - } finally bound.unbind().futureValue - } - } - -} diff --git a/akka-http-core/src/test/scala/akka/http/scaladsl/model/HttpEntitySpec.scala b/akka-http-core/src/test/scala/akka/http/scaladsl/model/HttpEntitySpec.scala deleted file mode 100755 index ddef91fa85..0000000000 --- a/akka-http-core/src/test/scala/akka/http/scaladsl/model/HttpEntitySpec.scala +++ /dev/null @@ -1,251 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model - -import java.util.concurrent.TimeoutException -import akka.NotUsed -import com.typesafe.config.{ ConfigFactory, Config } -import scala.concurrent.{ Promise, Await } -import scala.concurrent.duration._ -import org.scalatest.{ BeforeAndAfterAll, MustMatchers, FreeSpec } -import org.scalatest.matchers.{ MatchResult, Matcher } -import akka.util.ByteString -import akka.actor.ActorSystem -import akka.stream.scaladsl._ -import akka.stream.ActorMaterializer -import akka.http.scaladsl.model.HttpEntity._ -import akka.http.impl.util.StreamUtils - -import scala.util.Random - -class HttpEntitySpec extends FreeSpec with MustMatchers with BeforeAndAfterAll { - val tpe: ContentType = ContentTypes.`application/octet-stream` - val abc = ByteString("abc") - val de = ByteString("de") - val fgh = ByteString("fgh") - val ijk = ByteString("ijk") - - val testConf: Config = ConfigFactory.parseString(""" - akka.event-handlers = ["akka.testkit.TestEventListener"] - akka.loglevel = WARNING""") - implicit val system = ActorSystem(getClass.getSimpleName, testConf) - - implicit val materializer = ActorMaterializer() - override def afterAll() = system.terminate() - - val awaitAtMost = 3.seconds - - "HttpEntity" - { - "support dataBytes" - { - "Strict" in { - Strict(tpe, abc) must collectBytesTo(abc) - } - "Default" in { - Default(tpe, 11, source(abc, de, fgh, ijk)) must collectBytesTo(abc, de, fgh, ijk) - } - "CloseDelimited" in { - CloseDelimited(tpe, source(abc, de, fgh, ijk)) must collectBytesTo(abc, de, fgh, ijk) - } - "Chunked w/o LastChunk" in { - Chunked(tpe, source(Chunk(abc), Chunk(fgh), Chunk(ijk))) must collectBytesTo(abc, fgh, ijk) - } - "Chunked with LastChunk" in { - Chunked(tpe, source(Chunk(abc), Chunk(fgh), Chunk(ijk), LastChunk)) must collectBytesTo(abc, fgh, ijk) - } - } - "support contentLength" - { - "Strict" in { - Strict(tpe, abc).contentLengthOption mustEqual Some(3) - } - "Default" in { - Default(tpe, 11, source(abc, de, fgh, ijk)).contentLengthOption mustEqual Some(11) - } - "CloseDelimited" in { - CloseDelimited(tpe, source(abc, de, fgh, ijk)).contentLengthOption mustEqual None - } - "Chunked" in { - Chunked(tpe, source(Chunk(abc), Chunk(fgh), Chunk(ijk))).contentLengthOption mustEqual None - } - } - "support toStrict" - { - "Strict" in { - Strict(tpe, abc) must strictifyTo(Strict(tpe, abc)) - } - "Default" in { - Default(tpe, 11, source(abc, de, fgh, ijk)) must - strictifyTo(Strict(tpe, abc ++ de ++ fgh ++ ijk)) - } - "CloseDelimited" in { - CloseDelimited(tpe, source(abc, de, fgh, ijk)) must - strictifyTo(Strict(tpe, abc ++ de ++ fgh ++ ijk)) - } - "Chunked w/o LastChunk" in { - Chunked(tpe, source(Chunk(abc), Chunk(fgh), Chunk(ijk))) must - strictifyTo(Strict(tpe, abc ++ fgh ++ ijk)) - } - "Chunked with LastChunk" in { - Chunked(tpe, source(Chunk(abc), Chunk(fgh), Chunk(ijk), LastChunk)) must - strictifyTo(Strict(tpe, abc ++ fgh ++ ijk)) - } - "Infinite data stream" in { - val neverCompleted = Promise[ByteString]() - intercept[TimeoutException] { - Await.result(Default(tpe, 42, Source.fromFuture(neverCompleted.future)).toStrict(100.millis), awaitAtMost) - }.getMessage must be("HttpEntity.toStrict timed out after 100 milliseconds while still waiting for outstanding data") - } - } - "support transformDataBytes" - { - "Strict" in { - Strict(tpe, abc) must transformTo(Strict(tpe, doubleChars("abc") ++ trailer)) - } - "Default" in { - Default(tpe, 11, source(abc, de, fgh, ijk)) must - transformTo(Strict(tpe, doubleChars("abcdefghijk") ++ trailer)) - } - "CloseDelimited" in { - CloseDelimited(tpe, source(abc, de, fgh, ijk)) must - transformTo(Strict(tpe, doubleChars("abcdefghijk") ++ trailer)) - } - "Chunked w/o LastChunk" in { - Chunked(tpe, source(Chunk(abc), Chunk(fgh), Chunk(ijk))) must - transformTo(Strict(tpe, doubleChars("abcfghijk") ++ trailer)) - } - "Chunked with LastChunk" in { - Chunked(tpe, source(Chunk(abc), Chunk(fgh), Chunk(ijk), LastChunk)) must - transformTo(Strict(tpe, doubleChars("abcfghijk") ++ trailer)) - } - "Chunked with extra LastChunk" in { - Chunked(tpe, source(Chunk(abc), Chunk(fgh), Chunk(ijk), LastChunk, LastChunk)) must - transformTo(Strict(tpe, doubleChars("abcfghijk") ++ trailer)) - } - } - "support toString" - { - "Strict with binary MediaType" in { - val binaryType = ContentTypes.`application/octet-stream` - val entity = Strict(binaryType, abc) - entity must renderStrictDataAs(entity.data.toString()) - } - "Strict with non-binary MediaType and less than 4096 bytes" in { - val nonBinaryType = ContentTypes.`application/json` - val entity = Strict(nonBinaryType, abc) - entity must renderStrictDataAs(entity.data.decodeString(nonBinaryType.charset.value)) - } - "Strict with non-binary MediaType and over 4096 bytes" in { - val utf8Type = ContentTypes.`text/plain(UTF-8)` - val longString = Random.alphanumeric.take(10000).mkString - val entity = Strict(utf8Type, ByteString.apply(longString, utf8Type.charset.value)) - entity must renderStrictDataAs(s"${longString.take(4095)} ... (10000 bytes total)") - } - "Default" in { - val entity = Default(tpe, 11, source(abc, de, fgh, ijk)) - entity.toString must include(entity.productPrefix) - entity.toString must include("11") - entity.toString mustNot include("Source") - } - "CloseDelimited" in { - val entity = CloseDelimited(tpe, source(abc, de, fgh, ijk)) - entity.toString must include(entity.productPrefix) - entity.toString mustNot include("Source") - } - "Chunked" in { - val entity = Chunked(tpe, source(Chunk(abc))) - entity.toString must include(entity.productPrefix) - entity.toString mustNot include("Source") - } - "IndefiniteLength" in { - val entity = IndefiniteLength(tpe, source(abc, de, fgh, ijk)) - entity.toString must include(entity.productPrefix) - entity.toString mustNot include("Source") - } - } - "support withoutSizeLimit" - { - "Strict" in { - HttpEntity.Empty.withoutSizeLimit - withReturnType[UniversalEntity](Strict(tpe, abc).withoutSizeLimit) - withReturnType[RequestEntity](Strict(tpe, abc).asInstanceOf[RequestEntity].withoutSizeLimit) - withReturnType[ResponseEntity](Strict(tpe, abc).asInstanceOf[ResponseEntity].withoutSizeLimit) - withReturnType[HttpEntity](Strict(tpe, abc).asInstanceOf[HttpEntity].withoutSizeLimit) - } - "Default" in { - withReturnType[Default](Default(tpe, 11, source(abc, de, fgh, ijk)).withoutSizeLimit) - withReturnType[RequestEntity](Default(tpe, 11, source(abc, de, fgh, ijk)).asInstanceOf[RequestEntity].withoutSizeLimit) - withReturnType[ResponseEntity](Default(tpe, 11, source(abc, de, fgh, ijk)).asInstanceOf[ResponseEntity].withoutSizeLimit) - withReturnType[HttpEntity](Default(tpe, 11, source(abc, de, fgh, ijk)).asInstanceOf[HttpEntity].withoutSizeLimit) - } - "CloseDelimited" in { - withReturnType[CloseDelimited](CloseDelimited(tpe, source(abc, de, fgh, ijk)).withoutSizeLimit) - withReturnType[ResponseEntity](CloseDelimited(tpe, source(abc, de, fgh, ijk)).asInstanceOf[ResponseEntity].withoutSizeLimit) - withReturnType[HttpEntity](CloseDelimited(tpe, source(abc, de, fgh, ijk)).asInstanceOf[HttpEntity].withoutSizeLimit) - } - "Chunked" in { - withReturnType[Chunked](Chunked(tpe, source(Chunk(abc), Chunk(fgh), Chunk(ijk), LastChunk)).withoutSizeLimit) - withReturnType[RequestEntity](Chunked(tpe, source(Chunk(abc), Chunk(fgh), Chunk(ijk), LastChunk)).asInstanceOf[RequestEntity].withoutSizeLimit) - withReturnType[ResponseEntity](Chunked(tpe, source(Chunk(abc), Chunk(fgh), Chunk(ijk), LastChunk)).asInstanceOf[ResponseEntity].withoutSizeLimit) - withReturnType[HttpEntity](Chunked(tpe, source(Chunk(abc), Chunk(fgh), Chunk(ijk), LastChunk)).asInstanceOf[HttpEntity].withoutSizeLimit) - } - } - "support withSizeLimit" - { - "Strict" in { - HttpEntity.Empty.withSizeLimit(123L) - withReturnType[UniversalEntity](Strict(tpe, abc).withSizeLimit(123L)) - withReturnType[RequestEntity](Strict(tpe, abc).asInstanceOf[RequestEntity].withSizeLimit(123L)) - withReturnType[ResponseEntity](Strict(tpe, abc).asInstanceOf[ResponseEntity].withSizeLimit(123L)) - withReturnType[HttpEntity](Strict(tpe, abc).asInstanceOf[HttpEntity].withSizeLimit(123L)) - } - "Default" in { - withReturnType[Default](Default(tpe, 11, source(abc, de, fgh, ijk)).withoutSizeLimit) - withReturnType[RequestEntity](Default(tpe, 11, source(abc, de, fgh, ijk)).asInstanceOf[RequestEntity].withSizeLimit(123L)) - withReturnType[ResponseEntity](Default(tpe, 11, source(abc, de, fgh, ijk)).asInstanceOf[ResponseEntity].withSizeLimit(123L)) - withReturnType[HttpEntity](Default(tpe, 11, source(abc, de, fgh, ijk)).asInstanceOf[HttpEntity].withSizeLimit(123L)) - } - "CloseDelimited" in { - withReturnType[CloseDelimited](CloseDelimited(tpe, source(abc, de, fgh, ijk)).withSizeLimit(123L)) - withReturnType[ResponseEntity](CloseDelimited(tpe, source(abc, de, fgh, ijk)).asInstanceOf[ResponseEntity].withSizeLimit(123L)) - withReturnType[HttpEntity](CloseDelimited(tpe, source(abc, de, fgh, ijk)).asInstanceOf[HttpEntity].withSizeLimit(123L)) - } - "Chunked" in { - withReturnType[Chunked](Chunked(tpe, source(Chunk(abc), Chunk(fgh), Chunk(ijk), LastChunk)).withSizeLimit(123L)) - withReturnType[RequestEntity](Chunked(tpe, source(Chunk(abc), Chunk(fgh), Chunk(ijk), LastChunk)).asInstanceOf[RequestEntity].withSizeLimit(123L)) - withReturnType[ResponseEntity](Chunked(tpe, source(Chunk(abc), Chunk(fgh), Chunk(ijk), LastChunk)).asInstanceOf[ResponseEntity].withSizeLimit(123L)) - withReturnType[HttpEntity](Chunked(tpe, source(Chunk(abc), Chunk(fgh), Chunk(ijk), LastChunk)).asInstanceOf[HttpEntity].withSizeLimit(123L)) - } - } - } - - def source[T](elems: T*) = Source(elems.toList) - - def collectBytesTo(bytes: ByteString*): Matcher[HttpEntity] = - equal(bytes.toVector).matcher[Seq[ByteString]].compose { entity ⇒ - val future = entity.dataBytes.limit(1000).runWith(Sink.seq) - Await.result(future, awaitAtMost) - } - - def withReturnType[T](expr: T) = expr - - def strictifyTo(strict: Strict): Matcher[HttpEntity] = - equal(strict).matcher[Strict].compose(x ⇒ Await.result(x.toStrict(awaitAtMost), awaitAtMost)) - - def transformTo(strict: Strict): Matcher[HttpEntity] = - equal(strict).matcher[Strict].compose { x ⇒ - val transformed = x.transformDataBytes(duplicateBytesTransformer) - Await.result(transformed.toStrict(awaitAtMost), awaitAtMost) - } - - def renderStrictDataAs(dataRendering: String): Matcher[Strict] = - Matcher { strict: Strict ⇒ - val expectedRendering = s"${strict.productPrefix}(${strict.contentType},$dataRendering)" - MatchResult( - strict.toString == expectedRendering, - strict.toString + " != " + expectedRendering, - strict.toString + " == " + expectedRendering) - } - - def duplicateBytesTransformer(): Flow[ByteString, ByteString, NotUsed] = - Flow[ByteString].via(StreamUtils.byteStringTransformer(doubleChars, () ⇒ trailer)) - - def trailer: ByteString = ByteString("--dup") - def doubleChars(bs: ByteString): ByteString = ByteString(bs.flatMap(b ⇒ Seq(b, b)): _*) - def doubleChars(str: String): ByteString = doubleChars(ByteString(str)) -} diff --git a/akka-http-core/src/test/scala/akka/http/scaladsl/model/HttpMessageSpec.scala b/akka-http-core/src/test/scala/akka/http/scaladsl/model/HttpMessageSpec.scala deleted file mode 100644 index 4b67f4ecd6..0000000000 --- a/akka-http-core/src/test/scala/akka/http/scaladsl/model/HttpMessageSpec.scala +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model - -import akka.util.ByteString -import headers.Host -import headers.`Content-Type` -import org.scalatest.{ Matchers, WordSpec } - -class HttpMessageSpec extends WordSpec with Matchers { - - def test(uri: String, hostHeader: Host, effectiveUri: String) = - HttpRequest.effectiveUri(Uri(uri), List(hostHeader), securedConnection = false, null) shouldEqual Uri(effectiveUri) - - def fail(uri: String, hostHeader: Host) = - an[IllegalUriException] should be thrownBy - HttpRequest.effectiveUri(Uri(uri), List(hostHeader), securedConnection = false, null) - - "HttpRequest" should { - "provide an effective URI for relative URIs or matching Host-headers" in { - test("/segment", Host("example.com"), "http://example.com/segment") - test("http://example.com/", Host("example.com"), "http://example.com/") - test("http://example.com:8080/", Host("example.com", 8080), "http://example.com:8080/") - } - - "throw IllegalUriException for non-matching Host-headers" in { - fail("http://example.net/", Host("example.com")) - fail("http://example.com:8080/", Host("example.com")) - fail("http://example.com/", Host("example.com", 8080)) - } - } - - "HttpMessage" should { - "not throw a ClassCastException on header[`Content-Type`]" in { - val entity = HttpEntity.Strict(ContentTypes.`text/plain(UTF-8)`, ByteString.fromString("hello akka")) - HttpResponse(entity = entity).header[`Content-Type`] shouldBe Some(`Content-Type`(ContentTypes.`text/plain(UTF-8)`)) - } - } - -} diff --git a/akka-http-core/src/test/scala/akka/http/scaladsl/model/MultipartSpec.scala b/akka-http-core/src/test/scala/akka/http/scaladsl/model/MultipartSpec.scala deleted file mode 100644 index 998a3ffe2b..0000000000 --- a/akka-http-core/src/test/scala/akka/http/scaladsl/model/MultipartSpec.scala +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model - -import com.typesafe.config.{ Config, ConfigFactory } - -import scala.concurrent.Await -import scala.concurrent.duration._ -import org.scalatest.{ BeforeAndAfterAll, Inside, Matchers, WordSpec } -import akka.stream.ActorMaterializer -import akka.stream.scaladsl.{ Sink, Source } -import akka.util.ByteString -import akka.actor.ActorSystem -import headers._ - -class MultipartSpec extends WordSpec with Matchers with Inside with BeforeAndAfterAll { - - val testConf: Config = ConfigFactory.parseString(""" - akka.event-handlers = ["akka.testkit.TestEventListener"] - akka.loglevel = WARNING""") - implicit val system = ActorSystem(getClass.getSimpleName, testConf) - implicit val materializer = ActorMaterializer() - override def afterAll() = system.terminate() - - "Multipart.General" should { - "support `toStrict` on the streamed model" in { - val streamed = Multipart.General( - MediaTypes.`multipart/mixed`, - Source(Multipart.General.BodyPart(defaultEntity("data"), List(ETag("xzy"))) :: Nil)) - val strict = Await.result(streamed.toStrict(1.second), 1.second) - - strict shouldEqual Multipart.General( - MediaTypes.`multipart/mixed`, - Multipart.General.BodyPart.Strict(HttpEntity("data"), List(ETag("xzy")))) - } - - "support `toEntity`" in { - val streamed = Multipart.General( - MediaTypes.`multipart/mixed`, - Source(Multipart.General.BodyPart(defaultEntity("data"), List(ETag("xzy"))) :: Nil)) - val result = streamed.toEntity(boundary = "boundary") - result.contentType shouldBe MediaTypes.`multipart/mixed`.withBoundary("boundary").withCharset(HttpCharsets.`UTF-8`) - val encoding = Await.result(result.dataBytes.runWith(Sink.seq), 1.second) - encoding.map(_.utf8String).mkString shouldBe "--boundary\r\nContent-Type: text/plain; charset=UTF-8\r\nETag: \"xzy\"\r\n\r\ndata\r\n--boundary--" - } - } - - "Multipart.FormData" should { - "support `toStrict` on the streamed model" in { - val streamed = Multipart.FormData(Source( - Multipart.FormData.BodyPart("foo", defaultEntity("FOO")) :: - Multipart.FormData.BodyPart("bar", defaultEntity("BAR")) :: Nil)) - val strict = Await.result(streamed.toStrict(1.second), 1.second) - - strict shouldEqual Multipart.FormData(Map("foo" → HttpEntity("FOO"), "bar" → HttpEntity("BAR"))) - } - } - - "Multipart.ByteRanges" should { - "support `toStrict` on the streamed model" in { - val streamed = Multipart.ByteRanges(Source( - Multipart.ByteRanges.BodyPart(ContentRange(0, 6), defaultEntity("snippet"), _additionalHeaders = List(ETag("abc"))) :: - Multipart.ByteRanges.BodyPart(ContentRange(8, 9), defaultEntity("PR"), _additionalHeaders = List(ETag("xzy"))) :: Nil)) - val strict = Await.result(streamed.toStrict(1.second), 1.second) - - strict shouldEqual Multipart.ByteRanges( - Multipart.ByteRanges.BodyPart.Strict(ContentRange(0, 6), HttpEntity("snippet"), additionalHeaders = List(ETag("abc"))), - Multipart.ByteRanges.BodyPart.Strict(ContentRange(8, 9), HttpEntity("PR"), additionalHeaders = List(ETag("xzy")))) - } - } - - def defaultEntity(content: String) = - HttpEntity.Default(ContentTypes.`text/plain(UTF-8)`, content.length, Source(ByteString(content) :: Nil)) -} diff --git a/akka-http-core/src/test/scala/akka/http/scaladsl/model/SerializabilitySpec.scala b/akka-http-core/src/test/scala/akka/http/scaladsl/model/SerializabilitySpec.scala deleted file mode 100644 index 41a74cb5db..0000000000 --- a/akka-http-core/src/test/scala/akka/http/scaladsl/model/SerializabilitySpec.scala +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model - -import java.io._ -import headers._ -import org.scalatest.{ Matchers, WordSpec } -import org.scalatest.matchers.{ MatchResult, Matcher } -import scala.util.Try -import akka.util.ByteString - -class SerializabilitySpec extends WordSpec with Matchers { - - "HttpRequests" should { - "be serializable" when { - pending - "empty" in { - HttpRequest() should beSerializable - } - "with complex URI" in { - HttpRequest(uri = Uri("/test?blub=28&x=5+3")) should beSerializable - } - "with content type" in { - HttpRequest().withEntity(HttpEntity(ContentTypes.`application/json`, ByteString.empty)) should beSerializable - } - "with accepted media types" in { - HttpRequest().withHeaders(Accept(MediaTypes.`application/json`)) should beSerializable - } - "with accept-charset" in { - HttpRequest().withHeaders(`Accept-Charset`(HttpCharsets.`UTF-16`)) should beSerializable - HttpRequest().withHeaders(`Accept-Charset`(HttpCharset.custom("utf8"))) should beSerializable - } - "with accepted encodings" in { - HttpRequest().withHeaders(`Accept-Encoding`(HttpEncodings.chunked)) should beSerializable - HttpRequest().withHeaders(`Accept-Encoding`(HttpEncoding.custom("test"))) should beSerializable - } - } - } - - "HttpResponse" should { - "be serializable" when { - "empty" in { - pending - HttpResponse() should beSerializable - } - } - } - - "Header values" should { - "be serializable" when { - "Cache" in { CacheDirectives.`no-store` should beSerializable } - "DateTime" in { DateTime.now should beSerializable } - "Charsets" in { - tryToSerialize(HttpCharsets.`UTF-16`).nioCharset shouldEqual HttpCharsets.`UTF-16`.nioCharset - } - "LanguageRange" in { - Language("a", "b") should beSerializable - LanguageRange.`*` should beSerializable - } - "MediaRange" in { MediaRanges.`application/*` should beSerializable } - } - } - - def beSerializable: Matcher[AnyRef] = Matcher[AnyRef] { value ⇒ - val result = Try(tryToSerialize(value)) - MatchResult( - result.isSuccess, - "Failed with " + result, - "Was unexpectly successful and returned " + result) - } - - def tryToSerialize[T](obj: T): T = { - val baos = new ByteArrayOutputStream - val oos = new ObjectOutputStream(baos) - oos.writeObject(obj) - oos.close() - // make sure to use correct class loader - val loader = classOf[HttpRequest].getClassLoader - val ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray)) { - override def resolveClass(desc: ObjectStreamClass): Class[_] = - Class.forName(desc.getName, false, loader) - } - - val rereadObj = ois.readObject() - rereadObj == obj - rereadObj.toString == obj.toString - rereadObj.asInstanceOf[T] - } -} diff --git a/akka-http-core/src/test/scala/akka/http/scaladsl/model/TurkishISpec.scala b/akka-http-core/src/test/scala/akka/http/scaladsl/model/TurkishISpec.scala deleted file mode 100644 index da38778349..0000000000 --- a/akka-http-core/src/test/scala/akka/http/scaladsl/model/TurkishISpec.scala +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model - -import java.util.Locale -import org.scalatest.{ Matchers, WordSpec } -import akka.http.impl.util._ - -class TurkishISpec extends WordSpec with Matchers { - "Model" should { - "not suffer from turkish-i problem" in { - val charsetCons = Class.forName("akka.http.scaladsl.model.HttpCharsets$").getDeclaredConstructor() - charsetCons.setAccessible(true) - - val previousLocale = Locale.getDefault - - try { - // recreate HttpCharsets in turkish locale - Locale.setDefault(new Locale("tr", "TR")) - - val testString = "ISO-8859-1" - // demonstrate difference between toRootLowerCase and toLowerCase(turkishLocale) - testString.toLowerCase should not equal (testString.toRootLowerCase) - - val newCharsets = charsetCons.newInstance().asInstanceOf[HttpCharsets.type] - newCharsets.getForKey("iso-8859-1") shouldEqual Some(newCharsets.`ISO-8859-1`) - } finally { - Locale.setDefault(previousLocale) - } - } - } -} diff --git a/akka-http-core/src/test/scala/akka/http/scaladsl/model/UriSpec.scala b/akka-http-core/src/test/scala/akka/http/scaladsl/model/UriSpec.scala deleted file mode 100644 index a2c9113490..0000000000 --- a/akka-http-core/src/test/scala/akka/http/scaladsl/model/UriSpec.scala +++ /dev/null @@ -1,652 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model - -import java.nio.charset.Charset -import java.net.InetAddress -import akka.http.impl.util.StringRendering -import org.scalatest.matchers.{ MatchResult, Matcher } -import org.scalatest.{ Matchers, WordSpec } -import akka.parboiled2.UTF8 -import Uri._ - -class UriSpec extends WordSpec with Matchers { - - "Uri.Host instances" should { - - "correctly parse empty hosts" in { - Host("") shouldEqual Host.Empty - } - - "parse correctly from IPv4 literals" in { - Host("192.0.2.16") shouldEqual IPv4Host("192.0.2.16") - Host("255.0.0.0") shouldEqual IPv4Host("255.0.0.0") - Host("0.0.0.0") shouldEqual IPv4Host("0.0.0.0") - Host("1.0.0.0") shouldEqual IPv4Host("1.0.0.0") - Host("2.0.0.0") shouldEqual IPv4Host("2.0.0.0") - Host("3.0.0.0") shouldEqual IPv4Host("3.0.0.0") - Host("30.0.0.0") shouldEqual IPv4Host("30.0.0.0") - } - - "support inetAddresses round-trip for Inet4Addresses" in { - def roundTrip(ip: String): Unit = { - val inetAddr = InetAddress.getByName(ip) - val addr = Host(inetAddr) - addr shouldEqual IPv4Host(ip) - addr.inetAddresses shouldEqual Seq(inetAddr) - } - - roundTrip("192.0.2.16") - roundTrip("192.0.2.16") - roundTrip("255.0.0.0") - roundTrip("0.0.0.0") - roundTrip("1.0.0.0") - roundTrip("2.0.0.0") - roundTrip("3.0.0.0") - roundTrip("30.0.0.0") - } - - "parse correctly from IPv6 literals (RFC2732)" in { - // various - Host("[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]") shouldEqual IPv6Host("FEDCBA9876543210FEDCBA9876543210", "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210") - Host("[1080:0:0:0:8:800:200C:417A]") shouldEqual IPv6Host("108000000000000000080800200C417A", "1080:0:0:0:8:800:200C:417A") - Host("[3ffe:2a00:100:7031::1]") shouldEqual IPv6Host("3ffe2a00010070310000000000000001", "3ffe:2a00:100:7031::1") - Host("[1080::8:800:200C:417A]") shouldEqual IPv6Host("108000000000000000080800200C417A", "1080::8:800:200C:417A") - Host("[::192.9.5.5]") shouldEqual IPv6Host("000000000000000000000000C0090505", "::192.9.5.5") - Host("[::FFFF:129.144.52.38]") shouldEqual IPv6Host("00000000000000000000FFFF81903426", "::FFFF:129.144.52.38") - Host("[2010:836B:4179::836B:4179]") shouldEqual IPv6Host("2010836B4179000000000000836B4179", "2010:836B:4179::836B:4179") - - // Quad length - Host("[abcd::]") shouldEqual IPv6Host("ABCD0000000000000000000000000000", "abcd::") - Host("[abcd::1]") shouldEqual IPv6Host("ABCD0000000000000000000000000001", "abcd::1") - Host("[abcd::12]") shouldEqual IPv6Host("ABCD0000000000000000000000000012", "abcd::12") - Host("[abcd::123]") shouldEqual IPv6Host("ABCD0000000000000000000000000123", "abcd::123") - Host("[abcd::1234]") shouldEqual IPv6Host("ABCD0000000000000000000000001234", "abcd::1234") - - // Full length - Host("[2001:0db8:0100:f101:0210:a4ff:fee3:9566]") shouldEqual IPv6Host("20010db80100f1010210a4fffee39566", "2001:0db8:0100:f101:0210:a4ff:fee3:9566") // lower hex - Host("[2001:0DB8:0100:F101:0210:A4FF:FEE3:9566]") shouldEqual IPv6Host("20010db80100f1010210a4fffee39566", "2001:0DB8:0100:F101:0210:A4FF:FEE3:9566") // Upper hex - Host("[2001:db8:100:f101:210:a4ff:fee3:9566]") shouldEqual IPv6Host("20010db80100f1010210a4fffee39566", "2001:db8:100:f101:210:a4ff:fee3:9566") - Host("[2001:0db8:100:f101:0:0:0:1]") shouldEqual IPv6Host("20010db80100f1010000000000000001", "2001:0db8:100:f101:0:0:0:1") - Host("[1:2:3:4:5:6:255.255.255.255]") shouldEqual IPv6Host("000100020003000400050006FFFFFFFF", "1:2:3:4:5:6:255.255.255.255") - - // Legal IPv4 - Host("[::1.2.3.4]") shouldEqual IPv6Host("00000000000000000000000001020304", "::1.2.3.4") - Host("[3:4::5:1.2.3.4]") shouldEqual IPv6Host("00030004000000000000000501020304", "3:4::5:1.2.3.4") - Host("[::ffff:1.2.3.4]") shouldEqual IPv6Host("00000000000000000000ffff01020304", "::ffff:1.2.3.4") - Host("[::0.0.0.0]") shouldEqual IPv6Host("00000000000000000000000000000000", "::0.0.0.0") // Min IPv4 - Host("[::255.255.255.255]") shouldEqual IPv6Host("000000000000000000000000FFFFFFFF", "::255.255.255.255") // Max IPv4 - - // Zipper position - Host("[::1:2:3:4:5:6:7]") shouldEqual IPv6Host("00000001000200030004000500060007", "::1:2:3:4:5:6:7") - Host("[1::1:2:3:4:5:6]") shouldEqual IPv6Host("00010000000100020003000400050006", "1::1:2:3:4:5:6") - Host("[1:2::1:2:3:4:5]") shouldEqual IPv6Host("00010002000000010002000300040005", "1:2::1:2:3:4:5") - Host("[1:2:3::1:2:3:4]") shouldEqual IPv6Host("00010002000300000001000200030004", "1:2:3::1:2:3:4") - Host("[1:2:3:4::1:2:3]") shouldEqual IPv6Host("00010002000300040000000100020003", "1:2:3:4::1:2:3") - Host("[1:2:3:4:5::1:2]") shouldEqual IPv6Host("00010002000300040005000000010002", "1:2:3:4:5::1:2") - Host("[1:2:3:4:5:6::1]") shouldEqual IPv6Host("00010002000300040005000600000001", "1:2:3:4:5:6::1") - Host("[1:2:3:4:5:6:7::]") shouldEqual IPv6Host("00010002000300040005000600070000", "1:2:3:4:5:6:7::") - - // Zipper length - Host("[1:1:1::1:1:1:1]") shouldEqual IPv6Host("00010001000100000001000100010001", "1:1:1::1:1:1:1") - Host("[1:1:1::1:1:1]") shouldEqual IPv6Host("00010001000100000000000100010001", "1:1:1::1:1:1") - Host("[1:1:1::1:1]") shouldEqual IPv6Host("00010001000100000000000000010001", "1:1:1::1:1") - Host("[1:1::1:1]") shouldEqual IPv6Host("00010001000000000000000000010001", "1:1::1:1") - Host("[1:1::1]") shouldEqual IPv6Host("00010001000000000000000000000001", "1:1::1") - Host("[1::1]") shouldEqual IPv6Host("00010000000000000000000000000001", "1::1") - Host("[::1]") shouldEqual IPv6Host("00000000000000000000000000000001", "::1") // == localhost - Host("[::]") shouldEqual IPv6Host("00000000000000000000000000000000", "::") // == all addresses - - // A few more variations - Host("[21ff:abcd::1]") shouldEqual IPv6Host("21ffabcd000000000000000000000001", "21ff:abcd::1") - Host("[2001:db8:100:f101::1]") shouldEqual IPv6Host("20010db80100f1010000000000000001", "2001:db8:100:f101::1") - Host("[a:b:c::12:1]") shouldEqual IPv6Host("000a000b000c00000000000000120001", "a:b:c::12:1") - Host("[a:b::0:1:2:3]") shouldEqual IPv6Host("000a000b000000000000000100020003", "a:b::0:1:2:3") - } - "support inetAddresses round-trip for Inet6Addresses" in { - def fromAddress(address: String): IPv6Host = Host(s"[$address]").asInstanceOf[IPv6Host] - def roundTrip(ip: String): Unit = { - val inetAddr = InetAddress.getByName(ip) - val addr = Host(inetAddr) - addr equalsIgnoreCase fromAddress(ip) should be(true) - addr.inetAddresses shouldEqual Seq(inetAddr) - } - - roundTrip("1:1:1::1:1:1:1") - roundTrip("::1:2:3:4:5:6:7") - roundTrip("2001:0DB8:0100:F101:0210:A4FF:FEE3:9566") - roundTrip("2001:0db8:100:f101:0:0:0:1") - roundTrip("abcd::12") - roundTrip("::192.9.5.5") - } - - "parse correctly from NamedHost literals" in { - Host("www.spray.io") shouldEqual NamedHost("www.spray.io") - Host("localhost") shouldEqual NamedHost("localhost") - Host("%2FH%C3%A4ll%C3%B6%5C") shouldEqual NamedHost("""/hällö\""") - } - - "not accept illegal IPv4 literals" in { - Host("01.0.0.0") shouldBe a[NamedHost] - Host("001.0.0.0") shouldBe a[NamedHost] - Host("00.0.0.0") shouldBe a[NamedHost] - Host("000.0.0.0") shouldBe a[NamedHost] - Host("256.0.0.0") shouldBe a[NamedHost] - Host("300.0.0.0") shouldBe a[NamedHost] - Host("1111.0.0.0") shouldBe a[NamedHost] - Host("-1.0.0.0") shouldBe a[NamedHost] - Host("0.0.0") shouldBe a[NamedHost] - Host("0.0.0.") shouldBe a[NamedHost] - Host("0.0.0.0.") shouldBe a[NamedHost] - Host("0.0.0.0.0") shouldBe a[NamedHost] - Host("0.0..0") shouldBe a[NamedHost] - Host(".0.0.0") shouldBe a[NamedHost] - } - - "not accept illegal IPv6 literals" in { - // 5 char quad - the[IllegalUriException] thrownBy Host("[::12345]") shouldBe { - IllegalUriException( - "Illegal URI host: Invalid input '5', expected ':' or ']' (line 1, column 8)", - "[::12345]\n" + - " ^") - } - - // Two zippers - a[IllegalUriException] should be thrownBy Host("[abcd::abcd::abcd]") - - // Triple-colon zipper - a[IllegalUriException] should be thrownBy Host("[:::1234]") - a[IllegalUriException] should be thrownBy Host("[1234:::1234:1234]") - a[IllegalUriException] should be thrownBy Host("[1234:1234:::1234]") - a[IllegalUriException] should be thrownBy Host("[1234:::]") - - // No quads, just IPv4 - a[IllegalUriException] should be thrownBy Host("[1.2.3.4]") - a[IllegalUriException] should be thrownBy Host("[0001.0002.0003.0004]") - - // Five quads - a[IllegalUriException] should be thrownBy Host("[0000:0000:0000:0000:0000:1.2.3.4]") - - // Seven quads - a[IllegalUriException] should be thrownBy Host("[0:0:0:0:0:0:0]") - a[IllegalUriException] should be thrownBy Host("[0:0:0:0:0:0:0:]") - a[IllegalUriException] should be thrownBy Host("[0:0:0:0:0:0:0:1.2.3.4]") - - // Nine quads - a[IllegalUriException] should be thrownBy Host("[0:0:0:0:0:0:0:0:0]") - - // Invalid IPv4 part - a[IllegalUriException] should be thrownBy Host("[::ffff:001.02.03.004]") // Leading zeros - a[IllegalUriException] should be thrownBy Host("[::ffff:1.2.3.1111]") // Four char octet - a[IllegalUriException] should be thrownBy Host("[::ffff:1.2.3.256]") // > 255 - a[IllegalUriException] should be thrownBy Host("[::ffff:311.2.3.4]") // > 155 - a[IllegalUriException] should be thrownBy Host("[::ffff:1.2.3:4]") // Not a dot - a[IllegalUriException] should be thrownBy Host("[::ffff:1.2.3]") // Missing octet - a[IllegalUriException] should be thrownBy Host("[::ffff:1.2.3.]") // Missing octet - a[IllegalUriException] should be thrownBy Host("[::ffff:1.2.3a.4]") // Hex in octet - a[IllegalUriException] should be thrownBy Host("[::ffff:1.2.3.4:123]") // Crap input - - // Nonhex - a[IllegalUriException] should be thrownBy Host("[g:0:0:0:0:0:0]") - } - } - - "Uri.Path instances" should { - import Path.Empty - "be parsed and rendered correctly" in { - def roundTripTo(p: Path, cs: Charset = UTF8) = - Matcher[String] { s ⇒ - val rendering = UriRendering.renderPath(new StringRendering, p, cs).get - if (rendering != s) MatchResult(matches = false, s"The path rendered to '$rendering' rather than '$s'", "") - else if (Path(s, cs) != p) MatchResult(matches = false, s"The string parsed to '${Path(s, cs)}' rather than '$p'", "") - else MatchResult(matches = true, "", "") - } - - "" should roundTripTo(Empty) - "/" should roundTripTo(Path./) - "a" should roundTripTo("a" :: Empty) - "//" should roundTripTo(Path./ / "") - "a/" should roundTripTo("a" :: Path./) - "/a" should roundTripTo(Path / "a") - "/abc/de/f" should roundTripTo(Path / "abc" / "de" / "f") - "abc/de/f/" should roundTripTo("abc" :: '/' :: "de" :: '/' :: "f" :: Path./) - "abc///de" should roundTripTo("abc" :: '/' :: '/' :: '/' :: "de" :: Empty) - "/abc%2F" should roundTripTo(Path / "abc/") - "/:foo:/" should roundTripTo(Path / ":foo:" / "") - "/%2520" should roundTripTo(Path / "%20") - "/foo%20bar" should roundTripTo(Path / "foo bar") - "H%C3%A4ll%C3%B6" should roundTripTo("Hällö" :: Empty) - "/%2F%5C" should roundTripTo(Path / """/\""") - "/foo%F0%9F%92%A9bar" should roundTripTo(Path / "foo\ud83d\udca9bar") - "/%C3%89g%20get%20eti%C3%B0%20gler%20%C3%A1n%20%C3%BEess%20a%C3%B0%20mei%C3%B0a%20mig" should - roundTripTo(Path / "Ég get etið gler án þess að meiða mig") - "/%00%E4%00%F6%00%FC" should roundTripTo(Path / "äöü", Charset.forName("UTF-16BE")) - } - "support the `startsWith` predicate" in { - Empty startsWith Empty shouldBe true - Path./ startsWith Empty shouldBe true - Path("abc") startsWith Empty shouldBe true - Empty startsWith Path./ shouldBe false - Empty startsWith Path("abc") shouldBe false - Path./ startsWith Path./ shouldBe true - Path./ startsWith Path("abc") shouldBe false - Path("/abc") startsWith Path./ shouldBe true - Path("abc") startsWith Path./ shouldBe false - Path("abc") startsWith Path("ab") shouldBe true - Path("abc") startsWith Path("abc") shouldBe true - Path("/abc") startsWith Path("/a") shouldBe true - Path("/abc") startsWith Path("/abc") shouldBe true - Path("/ab") startsWith Path("/abc") shouldBe false - Path("/abc") startsWith Path("/abd") shouldBe false - Path("/abc/def") startsWith Path("/ab") shouldBe true - Path("/abc/def") startsWith Path("/abc/") shouldBe true - Path("/abc/def") startsWith Path("/abc/d") shouldBe true - Path("/abc/def") startsWith Path("/abc/def") shouldBe true - Path("/abc/def") startsWith Path("/abc/def/") shouldBe false - } - "support the `endsWithSlash` predicate" in { - Empty.endsWithSlash shouldBe false - Path./.endsWithSlash shouldBe true - Path("abc").endsWithSlash shouldBe false - Path("abc/").endsWithSlash shouldBe true - Path("/abc").endsWithSlash shouldBe false - Path("/abc/def").endsWithSlash shouldBe false - Path("/abc/def/").endsWithSlash shouldBe true - } - "support the `dropChars` modifier" in { - Path./.dropChars(0) shouldEqual Path./ - Path./.dropChars(1) shouldEqual Empty - Path("/abc/def/").dropChars(0) shouldEqual Path("/abc/def/") - Path("/abc/def/").dropChars(1) shouldEqual Path("abc/def/") - Path("/abc/def/").dropChars(2) shouldEqual Path("bc/def/") - Path("/abc/def/").dropChars(3) shouldEqual Path("c/def/") - Path("/abc/def/").dropChars(4) shouldEqual Path("/def/") - Path("/abc/def/").dropChars(5) shouldEqual Path("def/") - Path("/abc/def/").dropChars(6) shouldEqual Path("ef/") - Path("/abc/def/").dropChars(7) shouldEqual Path("f/") - Path("/abc/def/").dropChars(8) shouldEqual Path("/") - Path("/abc/def/").dropChars(9) shouldEqual Empty - } - } - - "Uri.Query instances" should { - def parser(mode: Uri.ParsingMode): String ⇒ Query = Query(_, mode = mode) - "be parsed correctly in strict mode" in { - val strict = parser(Uri.ParsingMode.Strict) - strict("") shouldEqual ("", "") +: Query.Empty - strict("a") shouldEqual ("a", "") +: Query.Empty - strict("a=") shouldEqual ("a", "") +: Query.Empty - strict("a=+") shouldEqual ("a", " ") +: Query.Empty - strict("a=%2B") shouldEqual ("a", "+") +: Query.Empty - strict("=a") shouldEqual ("", "a") +: Query.Empty - strict("a&") shouldEqual ("a", "") +: ("", "") +: Query.Empty - strict("a=%62") shouldEqual ("a", "b") +: Query.Empty - a[IllegalUriException] should be thrownBy strict("a^=b") - } - "be parsed correctly in relaxed mode" in { - val relaxed = parser(Uri.ParsingMode.Relaxed) - relaxed("") shouldEqual ("", "") +: Query.Empty - relaxed("a") shouldEqual ("a", "") +: Query.Empty - relaxed("a=") shouldEqual ("a", "") +: Query.Empty - relaxed("a=+") shouldEqual ("a", " ") +: Query.Empty - relaxed("a=%2B") shouldEqual ("a", "+") +: Query.Empty - relaxed("=a") shouldEqual ("", "a") +: Query.Empty - relaxed("a&") shouldEqual ("a", "") +: ("", "") +: Query.Empty - relaxed("a=%62") shouldEqual ("a", "b") +: Query.Empty - relaxed("a^=b") shouldEqual ("a^", "b") +: Query.Empty - } - "properly support the retrieval interface" in { - val query = Query("a=1&b=2&c=3&b=4&b") - query.get("a") shouldEqual Some("1") - query.get("d") shouldEqual None - query.getOrElse("a", "x") shouldEqual "1" - query.getOrElse("d", "x") shouldEqual "x" - query.getAll("b") shouldEqual List("", "4", "2") - query.getAll("d") shouldEqual Nil - query.toMap shouldEqual Map("a" → "1", "b" → "", "c" → "3") - query.toMultiMap shouldEqual Map("a" → List("1"), "b" → List("", "4", "2"), "c" → List("3")) - query.toList shouldEqual List("a" → "1", "b" → "2", "c" → "3", "b" → "4", "b" → "") - query.toSeq shouldEqual Seq("a" → "1", "b" → "2", "c" → "3", "b" → "4", "b" → "") - } - "support conversion from list of name/value pairs" in { - import Query._ - val pairs = List("key1" → "value1", "key2" → "value2", "key3" → "value3") - Query(pairs: _*).toList.diff(pairs) shouldEqual Nil - Query() shouldEqual Empty - Query("k" → "v") shouldEqual ("k" → "v") +: Empty - } - "encode special separators in query parameter names" in { - Query("a=b" → "c").toString() shouldEqual "a%3Db=c" - Query("a&b" → "c").toString() shouldEqual "a%26b=c" - Query("a+b" → "c").toString() shouldEqual "a%2Bb=c" - Query("a;b" → "c").toString() shouldEqual "a%3Bb=c" - } - "encode special separators in query parameter values" in { - Query("a" → "b=c").toString() shouldEqual "a=b%3Dc" - Query("a" → "b&c").toString() shouldEqual "a=b%26c" - Query("a" → "b+c").toString() shouldEqual "a=b%2Bc" - Query("a" → "b;c").toString() shouldEqual "a=b%3Bc" - } - } - - "URIs" should { - - // http://tools.ietf.org/html/rfc3986#section-1.1.2 - "be correctly parsed from and rendered to simple test examples" in { - Uri("ftp://ftp.is.co.za/rfc/rfc1808.txt") shouldEqual - Uri.from(scheme = "ftp", host = "ftp.is.co.za", path = "/rfc/rfc1808.txt") - - Uri("http://www.ietf.org/rfc/rfc2396.txt") shouldEqual - Uri.from(scheme = "http", host = "www.ietf.org", path = "/rfc/rfc2396.txt") - - Uri("ldap://[2001:db8::7]/c=GB?objectClass?one") shouldEqual - Uri.from(scheme = "ldap", host = "[2001:db8::7]", path = "/c=GB", queryString = Some("objectClass?one")) - - Uri("mailto:John.Doe@example.com") shouldEqual - Uri.from(scheme = "mailto", path = "John.Doe@example.com") - - Uri("news:comp.infosystems.www.servers.unix") shouldEqual - Uri.from(scheme = "news", path = "comp.infosystems.www.servers.unix") - - Uri("tel:+1-816-555-1212") shouldEqual - Uri.from(scheme = "tel", path = "+1-816-555-1212") - - Uri("telnet://192.0.2.16:80/") shouldEqual - Uri.from(scheme = "telnet", host = "192.0.2.16", port = 80, path = "/") - - Uri("urn:oasis:names:specification:docbook:dtd:xml:4.1.2") shouldEqual - Uri.from(scheme = "urn", path = "oasis:names:specification:docbook:dtd:xml:4.1.2") - - // more examples - Uri("http://") shouldEqual Uri(scheme = "http", authority = Authority(Host.Empty)) - Uri("http:?") shouldEqual Uri.from(scheme = "http", queryString = Some("")) - Uri("http:") shouldEqual Uri.from(scheme = "http", queryString = None) - Uri("?a+b=c%2Bd").query() shouldEqual ("a b", "c+d") +: Query.Empty - - // illegal paths - Uri("foo/another@url/[]and{}") shouldEqual Uri.from(path = "foo/another@url/%5B%5Dand%7B%7D") - a[IllegalUriException] should be thrownBy Uri("foo/another@url/[]and{}", mode = Uri.ParsingMode.Strict) - - // handle query parameters with more than percent-encoded character - Uri("?%7Ba%7D=$%7B%7D", UTF8, Uri.ParsingMode.Strict).query() shouldEqual Query.Cons("{a}", s"$${}", Query.Empty) - - // don't double decode - Uri("%2520").path.head shouldEqual "%20" - Uri("/%2F%5C").path shouldEqual Path / """/\""" - - // render - Uri("https://server.com/path/to/here?st=12345").toString shouldEqual "https://server.com/path/to/here?st=12345" - Uri("/foo/?a#b").toString shouldEqual "/foo/?a#b" - - // empty host - Uri("http://:8000/foo") shouldEqual Uri("http", Authority(Host.Empty, 8000), Path / "foo") - Uri("http://:80/foo") shouldEqual Uri("http", Authority(Host.Empty, 80), Path / "foo") - } - - "properly complete a normalization cycle" in { - - // http://tools.ietf.org/html/rfc3986#section-6.2.2 - normalize("eXAMPLE://a/./b/../b/%63/%7bfoo%7d") shouldEqual "example://a/b/c/%7Bfoo%7D" - - // more examples - normalize("") shouldEqual "" - normalize("/") shouldEqual "/" - normalize("../../") shouldEqual "../../" - normalize("aBc") shouldEqual "aBc" - - normalize("Http://Localhost") shouldEqual "http://localhost" - normalize("hTtP://localHost") shouldEqual "http://localhost" - normalize("https://:443") shouldEqual "https://" - normalize("https://:444") shouldEqual "https://:444" - normalize("http://:80/foo") shouldEqual "http:///foo" - normalize("http://:8080/foo") shouldEqual "http://:8080/foo" - normalize("ftp://example.com:21") shouldEqual "ftp://example.com" - normalize("example.com:21") shouldEqual "example.com:21" // example.com is parsed as the SCHEME (which is correct) - normalize("//example.com:21") shouldEqual "//example.com:21" - normalize("ftp://example.com:22") shouldEqual "ftp://example.com:22" - - normalize("//user:pass@[::1]:80/segment/index.html?query#frag") shouldEqual "//user:pass@[::1]:80/segment/index.html?query#frag" - normalize("http://[::1]:80/segment/index.html?query#frag") shouldEqual "http://[::1]/segment/index.html?query#frag" - normalize("http://user:pass@[::1]/segment/index.html?query#frag") shouldEqual "http://user:pass@[::1]/segment/index.html?query#frag" - normalize("http://user:pass@[::1]:80?query#frag") shouldEqual "http://user:pass@[::1]?query#frag" - normalize("http://user:pass@[::1]/segment/index.html#frag") shouldEqual "http://user:pass@[::1]/segment/index.html#frag" - normalize("http://user:pass@[::1]:81/segment/index.html?query") shouldEqual "http://user:pass@[::1]:81/segment/index.html?query" - normalize("ftp://host:21/gnu/") shouldEqual "ftp://host/gnu/" - normalize("one/two/three") shouldEqual "one/two/three" - normalize("/one/two/three") shouldEqual "/one/two/three" - normalize("//user:pass@localhost/one/two/three") shouldEqual "//user:pass@localhost/one/two/three" - normalize("http://www.example.com/") shouldEqual "http://www.example.com/" - normalize("http://sourceforge.net/projects/uriparser/") shouldEqual "http://sourceforge.net/projects/uriparser/" - normalize("http://sourceforge.net/project/platformdownload.php?group_id=182840") shouldEqual "http://sourceforge.net/project/platformdownload.php?group_id=182840" - normalize("mailto:test@example.com") shouldEqual "mailto:test@example.com" - normalize("file:/bin/bash") shouldEqual "file:///bin/bash" - normalize("http://www.example.com/name%20with%20spaces/") shouldEqual "http://www.example.com/name%20with%20spaces/" - normalize("http://examp%4Ce.com/") shouldEqual "http://example.com/" - normalize("http://example.com/a/b/%2E%2E/") shouldEqual "http://example.com/a/" - normalize("http://user:pass@SOMEHOST.COM:123") shouldEqual "http://user:pass@somehost.com:123" - normalize("HTTP://a:b@HOST:123/./1/2/../%41?abc#def") shouldEqual "http://a:b@host:123/1/A?abc#def" - - // acceptance and normalization of unescaped ascii characters such as {} and []: - normalize("eXAMPLE://a/./b/../b/%63/{foo}/[bar]") shouldEqual "example://a/b/c/%7Bfoo%7D/%5Bbar%5D" - a[IllegalUriException] should be thrownBy normalize("eXAMPLE://a/./b/../b/%63/{foo}/[bar]", mode = Uri.ParsingMode.Strict) - - // queries and fragments - normalize("?") shouldEqual "?" - normalize("?key") shouldEqual "?key" - normalize("?key=") shouldEqual "?key=" - normalize("?key=&a=b") shouldEqual "?key=&a=b" - normalize("?key={}&a=[]") shouldEqual "?key={}&a=[]" - normalize("?=value") shouldEqual "?=value" - normalize("?key=value") shouldEqual "?key=value" - normalize("?a+b") shouldEqual "?a+b" - normalize("?=a+b") shouldEqual "?=a+b" - normalize("?a+b=c+d") shouldEqual "?a+b=c+d" - normalize("??") shouldEqual "??" - normalize("?a=1&b=2") shouldEqual "?a=1&b=2" - normalize("?a+b=c%2Bd") shouldEqual "?a+b=c%2Bd" - normalize("?a&a") shouldEqual "?a&a" - normalize("?&#") shouldEqual "?&#" - normalize("?#") shouldEqual "?#" - normalize("#") shouldEqual "#" - normalize("#{}[]") shouldEqual "#%7B%7D%5B%5D" - a[IllegalUriException] should be thrownBy normalize("#{}[]", mode = Uri.ParsingMode.Strict) - } - - "support tunneling a URI through a query param" in { - val uri = Uri("http://aHost/aPath?aParam=aValue#aFragment") - val q = Query("uri" → uri.toString) - val uri2 = Uri(path = Path./, fragment = Some("aFragment")).withQuery(q).toString - uri2 shouldEqual "/?uri=http://ahost/aPath?aParam%3DaValue%23aFragment#aFragment" - Uri(uri2).query() shouldEqual q - Uri(q.getOrElse("uri", "")) shouldEqual uri - } - - "produce proper error messages for illegal URIs" in { - // illegal scheme - the[IllegalUriException] thrownBy Uri("foö:/a") shouldBe { - IllegalUriException( - "Illegal URI reference: Invalid input 'ö', expected scheme-char, 'EOI', '#', ':', '?', slashSegments or pchar (line 1, column 3)", - "foö:/a\n" + - " ^") - } - - // illegal userinfo - the[IllegalUriException] thrownBy Uri("http://user:ö@host") shouldBe { - IllegalUriException( - "Illegal URI reference: Invalid input 'ö', expected userinfo-char, pct-encoded, '@' or port (line 1, column 13)", - "http://user:ö@host\n" + - " ^") - } - - // illegal percent-encoding - the[IllegalUriException] thrownBy Uri("http://use%2G@host") shouldBe { - IllegalUriException( - "Illegal URI reference: Invalid input 'G', expected HEXDIG (line 1, column 13)", - "http://use%2G@host\n" + - " ^") - } - - // illegal path - the[IllegalUriException] thrownBy Uri("http://www.example.com/name with spaces/") shouldBe { - IllegalUriException( - "Illegal URI reference: Invalid input ' ', expected '/', 'EOI', '#', '?' or pchar (line 1, column 28)", - "http://www.example.com/name with spaces/\n" + - " ^") - } - - // illegal path with control character - the[IllegalUriException] thrownBy Uri("http:///with\newline") shouldBe { - IllegalUriException( - "Illegal URI reference: Invalid input '\\n', expected '/', 'EOI', '#', '?' or pchar (line 1, column 13)", - "http:///with\n" + - " ^") - } - - // illegal query - the[IllegalUriException] thrownBy Uri("?a=b=c").query() shouldBe { - IllegalUriException( - "Illegal query: Invalid input '=', expected '+', query-char, 'EOI', '&' or pct-encoded (line 1, column 4)", - "a=b=c\n" + - " ^") - } - } - - // http://tools.ietf.org/html/rfc3986#section-5.4 - "pass the RFC 3986 reference resolution examples" when { - val base = parseAbsolute("http://a/b/c/d;p?q") - def resolve(uri: String) = parseAndResolve(uri, base).toString - - "normal examples" in { - resolve("g:h") shouldEqual "g:h" - resolve("g") shouldEqual "http://a/b/c/g" - resolve("./g") shouldEqual "http://a/b/c/g" - resolve("g/") shouldEqual "http://a/b/c/g/" - resolve("/g") shouldEqual "http://a/g" - resolve("//g") shouldEqual "http://g" - resolve("?y") shouldEqual "http://a/b/c/d;p?y" - resolve("g?y") shouldEqual "http://a/b/c/g?y" - resolve("#s") shouldEqual "http://a/b/c/d;p?q#s" - resolve("g#s") shouldEqual "http://a/b/c/g#s" - resolve("g?y#s") shouldEqual "http://a/b/c/g?y#s" - resolve(";x") shouldEqual "http://a/b/c/;x" - resolve("g;x") shouldEqual "http://a/b/c/g;x" - resolve("g;x?y#s") shouldEqual "http://a/b/c/g;x?y#s" - resolve("") shouldEqual "http://a/b/c/d;p?q" - resolve(".") shouldEqual "http://a/b/c/" - resolve("./") shouldEqual "http://a/b/c/" - resolve("..") shouldEqual "http://a/b/" - resolve("../") shouldEqual "http://a/b/" - resolve("../g") shouldEqual "http://a/b/g" - resolve("../..") shouldEqual "http://a/" - resolve("../../") shouldEqual "http://a/" - resolve("../../g") shouldEqual "http://a/g" - } - - "abnormal examples" in { - resolve("../../../g") shouldEqual "http://a/g" - resolve("../../../../g") shouldEqual "http://a/g" - - resolve("/./g") shouldEqual "http://a/g" - resolve("/../g") shouldEqual "http://a/g" - resolve("g.") shouldEqual "http://a/b/c/g." - resolve(".g") shouldEqual "http://a/b/c/.g" - resolve("g..") shouldEqual "http://a/b/c/g.." - resolve("..g") shouldEqual "http://a/b/c/..g" - - resolve("./../g") shouldEqual "http://a/b/g" - resolve("./g/.") shouldEqual "http://a/b/c/g/" - resolve("g/./h") shouldEqual "http://a/b/c/g/h" - resolve("g/../h") shouldEqual "http://a/b/c/h" - resolve("g;x=1/./y") shouldEqual "http://a/b/c/g;x=1/y" - resolve("g;x=1/../y") shouldEqual "http://a/b/c/y" - - resolve("g?y/./x") shouldEqual "http://a/b/c/g?y/./x" - resolve("g?y/../x") shouldEqual "http://a/b/c/g?y/../x" - resolve("g#s/./x") shouldEqual "http://a/b/c/g#s/./x" - resolve("g#s/../x") shouldEqual "http://a/b/c/g#s/../x" - - resolve("http:g") shouldEqual "http:g" - } - } - - "be properly copyable" in { - val uri = Uri("http://host:80/path?query#fragment") - uri.copy() shouldEqual uri - } - - "provide sugar for fluent transformations" in { - val uri = Uri("http://host/path?query#fragment") - val explicitDefault = Uri("http://host:80/path?query#fragment") - val nonDefaultUri = Uri("http://host:6060/path?query#fragment") - - uri.withScheme("https") shouldEqual Uri("https://host/path?query#fragment") - explicitDefault.withScheme("https") shouldEqual Uri("https://host:80/path?query#fragment") - nonDefaultUri.withScheme("https") shouldEqual Uri("https://host:6060/path?query#fragment") - - uri.withAuthority(Authority(Host("other"), 3030)) shouldEqual Uri("http://other:3030/path?query#fragment") - uri.withAuthority(Host("other"), 3030) shouldEqual Uri("http://other:3030/path?query#fragment") - uri.withAuthority("other", 3030) shouldEqual Uri("http://other:3030/path?query#fragment") - - uri.withHost(Host("other")) shouldEqual Uri("http://other/path?query#fragment") - explicitDefault.withHost(Host("other")) shouldEqual Uri("http://other:80/path?query#fragment") - uri.withHost("other") shouldEqual Uri("http://other/path?query#fragment") - explicitDefault.withHost("other") shouldEqual Uri("http://other:80/path?query#fragment") - uri.withPort(90) shouldEqual Uri("http://host:90/path?query#fragment") - explicitDefault.withPort(90) shouldEqual Uri("http://host:90/path?query#fragment") - - uri.withPath(Path("/newpath")) shouldEqual Uri("http://host/newpath?query#fragment") - explicitDefault.withPath(Path("/newpath")) shouldEqual Uri("http://host:80/newpath?query#fragment") - - uri.withUserInfo("someInfo") shouldEqual Uri("http://someInfo@host/path?query#fragment") - explicitDefault.withUserInfo("someInfo") shouldEqual Uri("http://someInfo@host:80/path?query#fragment") - - uri.withQuery(Query("param1" → "value1")) shouldEqual Uri("http://host/path?param1=value1#fragment") - uri.withQuery(Query(Map("param1" → "value1"))) shouldEqual Uri("http://host/path?param1=value1#fragment") - uri.withRawQueryString("param1=value1") shouldEqual Uri("http://host/path?param1=value1#fragment") - - uri.withFragment("otherFragment") shouldEqual Uri("http://host/path?query#otherFragment") - } - - "return the correct effective port" in { - Uri("http://host/").effectivePort shouldEqual 80 - Uri("ftp://host/").effectivePort shouldEqual 21 - Uri("http://host:9090/").effectivePort shouldEqual 9090 - Uri("https://host/").effectivePort shouldEqual 443 - - Uri("https://host/").withPort(4450).effectivePort shouldEqual 4450 - Uri("https://host:3030/").withPort(4450).effectivePort shouldEqual 4450 - } - - "keep the specified authority port" in { - Uri("example.com").withPort(0).authority.port shouldEqual 0 - Uri("example.com").withPort(80).authority.port shouldEqual 80 - Uri("http://example.com").withPort(80).authority.port shouldEqual 80 - Uri("http://example.com").withPort(0).authority.port shouldEqual 0 - Uri("https://example.com").withPort(0).authority.port shouldEqual 0 - Uri("https://example.com").withPort(443).authority.port shouldEqual 443 - } - - "properly render as HTTP request target origin forms" in { - Uri("http://example.com/foo/bar?query=1#frag").toHttpRequestTargetOriginForm.toString === "/foo/bar?query=1" - Uri("http://example.com//foo/bar?query=1#frag").toHttpRequestTargetOriginForm.toString === "//foo/bar?query=1" - } - - "survive parsing a URI with thousands of path segments" in { - val slashes = "/a/" * 2000 - val uri = Uri(s"http://foo.bar/$slashes") - uri.toString // was reported to throw StackOverflowException in Spray's URI - } - - "survive parsing a URI with thousands of query string values" in { - val uriString = (1 to 2000).map("a=" + _).mkString("http://foo.bar/?", "&", "") - val uri = Uri(uriString) - val query = uri.query() - query.size shouldEqual 2000 - query.head._2 shouldEqual "1" - query.last._2 shouldEqual "2000" - } - } -} diff --git a/akka-http-core/src/test/scala/akka/http/scaladsl/model/headers/HeaderSpec.scala b/akka-http-core/src/test/scala/akka/http/scaladsl/model/headers/HeaderSpec.scala deleted file mode 100644 index c8dd27e196..0000000000 --- a/akka-http-core/src/test/scala/akka/http/scaladsl/model/headers/HeaderSpec.scala +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.model.headers - -import akka.http.impl.util._ -import org.scalatest._ -import java.net.InetAddress -import akka.http.scaladsl.model._ - -class HeaderSpec extends FreeSpec with Matchers { - "ModeledCompanion should" - { - "provide parseFromValueString method" - { - "successful parse run" in { - headers.`Cache-Control`.parseFromValueString("private, no-cache, no-cache=Set-Cookie, proxy-revalidate, s-maxage=1000") shouldEqual - Right(headers.`Cache-Control`( - CacheDirectives.`private`(), - CacheDirectives.`no-cache`, - CacheDirectives.`no-cache`("Set-Cookie"), - CacheDirectives.`proxy-revalidate`, - CacheDirectives.`s-maxage`(1000))) - } - "failing parse run" in { - val Left(List(ErrorInfo(summary, detail))) = headers.`Last-Modified`.parseFromValueString("abc") - summary shouldEqual "Illegal HTTP header 'Last-Modified': Invalid input 'a', expected IMF-fixdate, asctime-date or '0' (line 1, column 1)" - detail shouldEqual - """abc - |^""".stripMarginWithNewline("\n") - - } - } - } - - "MediaType should" - { - "provide parse method" - { - "successful parse run" in { - MediaType.parse("application/gnutar") shouldEqual Right(MediaTypes.`application/gnutar`) - } - "failing parse run" in { - val Left(List(ErrorInfo(summary, detail))) = MediaType.parse("application//gnutar") - summary shouldEqual "Illegal HTTP header 'Content-Type': Invalid input '/', expected subtype (line 1, column 13)" - detail shouldEqual - """application//gnutar - | ^""".stripMarginWithNewline("\n") - } - } - } - - "ContentType should" - { - "provide parse method" - { - "successful parse run" in { - ContentType.parse("text/plain; charset=UTF8") shouldEqual Right(MediaTypes.`text/plain`.withCharset(HttpCharsets.`UTF-8`)) - } - "failing parse run" in { - val Left(List(ErrorInfo(summary, detail))) = ContentType.parse("text/plain, charset=UTF8") - summary shouldEqual "Illegal HTTP header 'Content-Type': Invalid input ',', expected tchar, OWS, ws or 'EOI' (line 1, column 11)" - detail shouldEqual - """text/plain, charset=UTF8 - | ^""".stripMarginWithNewline("\n") - } - } - } - - "Strict-Transport-Security should" - { - "provide parseFromValueString method" - { - "successful parse run" in { - headers.`Strict-Transport-Security`.parseFromValueString("max-age=30; includeSubDomains") shouldEqual Right(headers.`Strict-Transport-Security`(30, true)) - headers.`Strict-Transport-Security`.parseFromValueString("max-age=30; includeSubDomains; preload") shouldEqual Right(headers.`Strict-Transport-Security`(30, true)) - } - "successful parse run with additional values" in { - headers.`Strict-Transport-Security`.parseFromValueString("max-age=30; includeSubDomains; preload; dummy") shouldEqual - Right(headers.`Strict-Transport-Security`(30, true)) - headers.`Strict-Transport-Security`.parseFromValueString("max-age=30; includeSubDomains; dummy; preload") shouldEqual - Right(headers.`Strict-Transport-Security`(30, true)) - } - "failing parse run" in { - val Left(List(ErrorInfo(summary, detail))) = `Strict-Transport-Security`.parseFromValueString("max-age=30; includeSubDomains; preload;") - summary shouldEqual "Illegal HTTP header 'Strict-Transport-Security': Invalid input 'EOI', expected OWS or token0 (line 1, column 40)" - } - } - } - - "All request headers should" - { - - "render in request" in { - val requestHeaders = Vector[HttpHeader]( - Accept(MediaRanges.`*/*`), - `Accept-Charset`(HttpCharsetRange(HttpCharsets.`UTF-8`)), - `Accept-Encoding`(HttpEncodingRange(HttpEncodings.gzip)), - `Accept-Language`(LanguageRange(Language("sv_SE"))), - `Access-Control-Request-Headers`("Host"), - `Access-Control-Request-Method`(HttpMethods.GET), - Authorization(BasicHttpCredentials("johan", "correcthorsebatterystaple")), - `Cache-Control`(CacheDirectives.`max-age`(3000)), - Connection("upgrade"), - `Content-Length`(2000), - `Content-Disposition`(ContentDispositionTypes.inline), - `Content-Encoding`(HttpEncodings.gzip), - `Content-Type`(ContentTypes.`text/xml(UTF-8)`), - Cookie("cookie", "with-chocolate"), - Date(DateTime(2016, 2, 4, 9, 9, 0)), - Expect.`100-continue`, - Host("example.com"), - `If-Match`(EntityTag("hash")), - `If-Modified-Since`(DateTime(2016, 2, 4, 9, 9, 0)), - `If-None-Match`(EntityTagRange(EntityTag("hashhash"))), - `If-Range`(DateTime(2016, 2, 4, 9, 9, 0)), - `If-Unmodified-Since`(DateTime(2016, 2, 4, 9, 9, 0)), - Link(Uri("http://example.com"), LinkParams.`title*`("example")), - Origin(HttpOrigin("http", Host("example.com"))), - `Proxy-Authorization`(BasicHttpCredentials("johan", "correcthorsebatterystaple")), - Range(RangeUnits.Bytes, Vector(ByteRange(1, 1024))), - Referer(Uri("http://example.com/")), - `Sec-WebSocket-Protocol`(Vector("chat", "superchat")), - `Sec-WebSocket-Key`("dGhlIHNhbXBsZSBub25jZQ"), - `Sec-WebSocket-Version`(Vector(13)), - `Transfer-Encoding`(TransferEncodings.chunked), - Upgrade(Vector(UpgradeProtocol("HTTP", Some("2.0")))), - `User-Agent`("Akka HTTP Client 2.4"), - `X-Forwarded-For`(RemoteAddress(InetAddress.getByName("192.168.0.1"))), - `X-Real-Ip`(RemoteAddress(InetAddress.getByName("192.168.1.1")))) - - requestHeaders.foreach { header ⇒ - header shouldBe 'renderInRequests - } - } - } - - "All response headers should" - { - - "render in response" in { - val responseHeaders = Vector[HttpHeader]( - `Accept-Ranges`(RangeUnits.Bytes), - `Access-Control-Allow-Credentials`(true), - `Access-Control-Allow-Headers`("X-Custom"), - `Access-Control-Allow-Methods`(HttpMethods.GET), - `Access-Control-Allow-Origin`(HttpOrigin("http://example.com")), - `Access-Control-Expose-Headers`("X-Custom"), - `Access-Control-Max-Age`(2000), - Age(2000), - Allow(HttpMethods.GET), - `Cache-Control`(CacheDirectives.`no-cache`), - Connection("close"), - `Content-Length`(2000), - `Content-Disposition`(ContentDispositionTypes.inline), - `Content-Encoding`(HttpEncodings.gzip), - `Content-Range`(ContentRange.Default(1, 20, None)), - `Content-Type`(ContentTypes.`text/xml(UTF-8)`), - Date(DateTime(2016, 2, 4, 9, 9, 0)), - ETag("suchhashwow"), - Expires(DateTime(2016, 2, 4, 9, 9, 0)), - `Last-Modified`(DateTime(2016, 2, 4, 9, 9, 0)), - Link(Uri("http://example.com"), LinkParams.`title*`("example")), - Location(Uri("http://example.com")), - `Proxy-Authenticate`(HttpChallenge("Basic", Some("example.com"))), - `Sec-WebSocket-Accept`("dGhlIHNhbXBsZSBub25jZQ"), - `Sec-WebSocket-Extensions`(Vector(WebSocketExtension("foo"))), - `Sec-WebSocket-Version`(Vector(13)), - Server("Akka-HTTP/2.4"), - `Set-Cookie`(HttpCookie("sessionId", "b0eb8b8b3ad246")), - `Transfer-Encoding`(TransferEncodings.chunked), - Upgrade(Vector(UpgradeProtocol("HTTP", Some("2.0")))), - `WWW-Authenticate`(HttpChallenge("Basic", Some("example.com")))) - - responseHeaders.foreach { header ⇒ - header shouldBe 'renderInResponses - } - } - } -} diff --git a/akka-http-core/src/test/scala/akka/http/scaladsl/settings/SettingsEqualitySpec.scala b/akka-http-core/src/test/scala/akka/http/scaladsl/settings/SettingsEqualitySpec.scala deleted file mode 100644 index bc16bad978..0000000000 --- a/akka-http-core/src/test/scala/akka/http/scaladsl/settings/SettingsEqualitySpec.scala +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright (C) 2016 Lightbend Inc. - */ - -package akka.http.scaladsl.settings - -import com.typesafe.config.ConfigFactory - -import org.scalatest.Matchers -import org.scalatest.WordSpec - -class SettingsEqualitySpec extends WordSpec with Matchers { - - val config = ConfigFactory.parseString(""" - akka.http.routing { - verbose-error-messages = off - file-get-conditional = on - render-vanity-footer = yes - range-coalescing-threshold = 80 - range-count-limit = 16 - decode-max-bytes-per-chunk = 1m - file-io-dispatcher = ${akka.stream.blocking-io-dispatcher} - } - """).withFallback(ConfigFactory.load).resolve - - "equality" should { - "hold for ConnectionPoolSettings" in { - val s1 = ConnectionPoolSettings(config) - val s2 = ConnectionPoolSettings(config) - - s1 shouldBe s2 - s1.toString should startWith("ConnectionPoolSettings(") - } - - "hold for ParserSettings" in { - val s1 = ParserSettings(config) - val s2 = ParserSettings(config) - - s1 shouldBe s2 - s1.toString should startWith("ParserSettings(") - } - - "hold for ClientConnectionSettings" in { - val s1 = ClientConnectionSettings(config) - val s2 = ClientConnectionSettings(config) - - s1 shouldBe s2 - s1.toString should startWith("ClientConnectionSettings(") - } - - "hold for RoutingSettings" in { - val s1 = RoutingSettings(config) - val s2 = RoutingSettings(config) - - s1 shouldBe s2 - s1.toString should startWith("RoutingSettings(") - } - - "hold for ServerSettings" in { - val s1 = ServerSettings(config) - val s2 = ServerSettings(config) - - s1 shouldBe s2 - s1.toString should startWith("ServerSettings(") - } - } - -} diff --git a/akka-http-core/src/test/scala/akka/http/scaladsl/util/FastFutureSpec.scala b/akka-http-core/src/test/scala/akka/http/scaladsl/util/FastFutureSpec.scala deleted file mode 100644 index cc0fef9c8b..0000000000 --- a/akka-http-core/src/test/scala/akka/http/scaladsl/util/FastFutureSpec.scala +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.util - -import scala.util.control.NoStackTrace -import scala.concurrent.{ Await, Promise, Future } -import scala.concurrent.duration._ -import scala.concurrent.ExecutionContext.Implicits.global -import org.scalatest.{ FreeSpec, Matchers } -import scala.util.{ Try, Failure, Success } -import akka.http.scaladsl.util.FastFuture._ - -class FastFutureSpec extends FreeSpec with Matchers { - object TheException extends RuntimeException("Expected exception") with NoStackTrace - - "FastFuture should implement" - { - "transformWith(Try => Future)" - { - "Success -> Success" in { - test(Success(23), _.transformWith(t ⇒ FastFuture(t.map(_ + 19)))) { - _ shouldEqual Success(42) - } - } - "Success -> Failure" in { - test(Success(23), _.transformWith(_ ⇒ FastFuture.failed(TheException))) { - _ shouldEqual Failure(TheException) - } - } - "Failure -> Success" in { - test(Failure(TheException), _.transformWith(t ⇒ FastFuture.successful(23))) { - _ shouldEqual Success(23) - } - } - "Failure -> Failure" in { - test(Failure(TheException), _.transformWith(_ ⇒ FastFuture.failed(TheException))) { - _ shouldEqual Failure(TheException) - } - } - "Success -> user-function failed" in { - test(Success(23), _.transformWith(failF)) { - _ shouldEqual Failure(TheException) - } - } - "Failure -> user-function failed" in { - test(Failure(TheException), _.transformWith(failF)) { - _ shouldEqual Failure(TheException) - } - } - } - "transformWith(A => Future[B], Throwable => Future[B])" - { - "Success -> Success" in { - test(Success(23), _.transformWith(t ⇒ FastFuture.successful(t + 19), neverCalled)) { - _ shouldEqual Success(42) - } - } - "Success -> Failure" in { - test(Success(23), _.transformWith(_ ⇒ FastFuture.failed(TheException), neverCalled)) { - _ shouldEqual Failure(TheException) - } - } - "Failure -> Success" in { - test(Failure(TheException), _.transformWith(neverCalled, t ⇒ FastFuture.successful(23))) { - _ shouldEqual Success(23) - } - } - "Failure -> Failure" in { - test(Failure(TheException), _.transformWith(neverCalled, _ ⇒ FastFuture.failed(TheException))) { - _ shouldEqual Failure(TheException) - } - } - "Success -> user-function failed" in { - test(Success(23), _.transformWith(failF, neverCalled)) { - _ shouldEqual Failure(TheException) - } - } - "Failure -> user-function failed" in { - test(Failure(TheException), _.transformWith(neverCalled, failF)) { - _ shouldEqual Failure(TheException) - } - } - } - "map" - { - "map success" in { - test(Success(23), _.map(_ + 19)) { - _ shouldEqual Success(42) - } - } - "report exceptions from user function" in { - test(Success(23), _.map(failF)) { - _ shouldEqual Failure(TheException) - } - } - "propagate errors" in { - test(Failure(TheException), _.map(neverCalled)) { - _ shouldEqual Failure(TheException) - } - } - } - "flatMap" - { - "both success" in { - test(Success(23), _.flatMap(i ⇒ FastFuture.successful(i + 19))) { - _ shouldEqual Success(42) - } - } - "outer failure" in { - test(Failure(TheException), _.flatMap(neverCalled)) { - _ shouldEqual Failure(TheException) - } - } - "inner failure" in { - test(Success(23), _.flatMap(i ⇒ FastFuture.failed(TheException))) { - _ shouldEqual Failure(TheException) - } - } - "user-func failure" in { - test(Success(23), _.flatMap(failF)) { - _ shouldEqual Failure(TheException) - } - } - } - "recoverWith" - { - "Success" in { - test(Success(23), _.recoverWith(neverCalled)) { - _ shouldEqual Success(23) - } - } - "Failure -> Success" in { - test(Failure(UnexpectedException), _.recoverWith { case _ ⇒ FastFuture.successful(23) }) { - _ shouldEqual Success(23) - } - } - "Failure -> Failure" in { - test(Failure(UnexpectedException), _.recoverWith { case _ ⇒ FastFuture.failed(TheException) }) { - _ shouldEqual Failure(TheException) - } - } - "user-function failed" in { - test(Failure(UnexpectedException), _.recoverWith(failF)) { - _ shouldEqual Failure(TheException) - } - } - } - "recover" - { - "Success" in { - test(Success(23), _.recover(neverCalled)) { - _ shouldEqual Success(23) - } - } - "Failure -> Success" in { - test(Failure(UnexpectedException), _.recover { case _ ⇒ 23 }) { - _ shouldEqual Success(23) - } - } - "user-function failed" in { - test(Failure(UnexpectedException), _.recoverWith(failF)) { - _ shouldEqual Failure(TheException) - } - } - } - } - - def test(result: Try[Int], op: FastFuture[Int] ⇒ Future[Int])(check: Try[Int] ⇒ Unit): Unit = { - def testStrictly(): Unit = { - val f = FastFuture(result) - check(op(f.fast).value.get) - } - def testLazily(): Unit = { - val p = Promise[Int] - val opped = op(p.future.fast) - p.complete(result) - Await.ready(opped, 100.millis) - check(opped.value.get) - } - testStrictly() - testLazily() - } - - def failF: PartialFunction[Any, Nothing] = PartialFunction(_ ⇒ throw TheException) - class UnexpectedException extends RuntimeException("Unexpected exception - should never happen") - object UnexpectedException extends UnexpectedException with NoStackTrace - def neverCalled: PartialFunction[Any, Nothing] = PartialFunction(_ ⇒ throw new UnexpectedException) -} diff --git a/akka-http-core/src/test/scala/io/akka/integrationtest/http/HttpModelIntegrationSpec.scala b/akka-http-core/src/test/scala/io/akka/integrationtest/http/HttpModelIntegrationSpec.scala deleted file mode 100644 index d1af34f168..0000000000 --- a/akka-http-core/src/test/scala/io/akka/integrationtest/http/HttpModelIntegrationSpec.scala +++ /dev/null @@ -1,214 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package io.akka.integrationtest.http - -import com.typesafe.config.{ ConfigFactory, Config } -import scala.concurrent.Await -import scala.concurrent.duration._ -import org.scalatest.{ BeforeAndAfterAll, Matchers, WordSpec } -import akka.util.ByteString -import akka.actor.ActorSystem -import akka.http.scaladsl.model._ -import akka.stream.ActorMaterializer -import akka.stream.scaladsl._ -import headers._ - -/** - * Integration test for external HTTP libraries that are built on top of - * Akka HTTP core. An example of an external library is Play Framework. - * The way these libraries use Akka HTTP core may be different to how - * normal users would use Akka HTTP core. For example the libraries may - * need direct access to some model objects that users who use Akka HTTP - * directly would not require. - * - * This test is designed to capture the needs of these external libaries. - * Each test gives a use case of an external library and then checks that - * it can be fulfilled. A typical example of a use case is converting - * between one library's HTTP model and the Akka HTTP core HTTP model. - * - * This test is located a different package (io.akka vs akka) in order to - * check for any visibility issues when Akka HTTP core is used by third - * party libraries. - */ -class HttpModelIntegrationSpec extends WordSpec with Matchers with BeforeAndAfterAll { - - val testConf: Config = ConfigFactory.parseString(""" - akka.event-handlers = ["akka.testkit.TestEventListener"] - akka.loglevel = WARNING""") - implicit val system = ActorSystem(getClass.getSimpleName, testConf) - - override def afterAll() = system.terminate() - - implicit val materializer = ActorMaterializer() - - "External HTTP libraries" should { - - "be able to get String headers and an Array[Byte] body out of an HttpRequest" in { - - // First create an incoming HttpRequest for the external HTTP library - // to deal with. We're going to convert this request into the library's - // own HTTP model. - - val request = HttpRequest( - method = HttpMethods.POST, - uri = Uri("/greeting"), - headers = List( - Host("localhost"), - RawHeader("Origin", "null")), - entity = HttpEntity.Default( - contentType = ContentTypes.`application/json`, - contentLength = 5, - Source(List(ByteString("hello"))))) - - // Our library uses a simple model of headers: a Seq[(String, String)]. - // The body is represented as an Array[Byte]. To get the headers in - // the form we want we first need to reconstruct the original headers - // by combining the request.headers value with the headers that are - // implicitly contained by the request.entity. We convert all the - // HttpHeaders by getting their name and value. We convert Content-Type - // and Content-Length by using the toString of their values. - - val partialTextHeaders: Seq[(String, String)] = request.headers.map(h ⇒ (h.name, h.value)) - val entityTextHeaders: Seq[(String, String)] = request.entity match { - case HttpEntity.Default(contentType, contentLength, _) ⇒ - Seq(("Content-Type", contentType.toString), ("Content-Length", contentLength.toString)) - case _ ⇒ - ??? - } - val textHeaders: Seq[(String, String)] = entityTextHeaders ++ partialTextHeaders - textHeaders shouldEqual Seq( - "Content-Type" → "application/json", - "Content-Length" → "5", - "Host" → "localhost", - "Origin" → "null") - - // Finally convert the body into an Array[Byte]. - - val entityBytes: Array[Byte] = Await.result(request.entity.toStrict(1.second), 2.seconds).data.toArray - entityBytes.to[Seq] shouldEqual ByteString("hello").to[Seq] - } - - "be able to build an HttpResponse from String headers and Array[Byte] body" in { - - // External HTTP libraries (such as Play) will model HTTP differently - // to Akka HTTP. One model uses a Seq[(String, String)] for headers and - // an Array[Byte] for a body. The following data structures show an - // example simple model of an HTTP response. - - val textHeaders: Seq[(String, String)] = Seq( - "Content-Type" → "text/plain", - "Content-Length" → "3", - "X-Greeting" → "Hello") - val byteArrayBody: Array[Byte] = "foo".getBytes - - // Now we need to convert this model to Akka HTTP's model. To do that - // we use Akka HTTP's HeaderParser to parse the headers, giving us a - // List[HttpHeader]. - - val parsingResults = textHeaders map { case (name, value) ⇒ HttpHeader.parse(name, value) } - val convertedHeaders = parsingResults collect { case HttpHeader.ParsingResult.Ok(h, _) ⇒ h } - val parseErrors = parsingResults.flatMap(_.errors) - parseErrors shouldBe empty - - // Most of these headers are modeled by Akka HTTP as a Seq[HttpHeader], - // but the Content-Type and Content-Length are special: their - // values relate to the HttpEntity and so they're modeled as part of - // the HttpEntity. These headers need to be stripped out of the main - // Seq[Header] and dealt with separately. - - val normalHeaders = convertedHeaders.filter { - case _: `Content-Type` ⇒ false - case _: `Content-Length` ⇒ false - case _ ⇒ true - } - normalHeaders.head shouldEqual RawHeader("X-Greeting", "Hello") - normalHeaders.tail shouldEqual Nil - - val contentType = convertedHeaders.collectFirst { - case ct: `Content-Type` ⇒ ct.contentType - } - contentType shouldEqual Some(ContentTypes.`text/plain(UTF-8)`) - - val contentLength = convertedHeaders.collectFirst { - case cl: `Content-Length` ⇒ cl.length - } - contentLength shouldEqual Some(3) - - // We're going to model the HttpEntity as an HttpEntity.Default, so - // convert the body into a Publisher[ByteString]. - - val byteStringBody = ByteString(byteArrayBody) - val publisherBody = Source(List(byteStringBody)) - - // Finally we can create our HttpResponse. - - HttpResponse( - entity = HttpEntity.Default(contentType.get, contentLength.get, publisherBody)) - } - - "be able to wrap HttpHeaders with custom typed headers" in { - - // TODO potentially use the integration for Play / Lagom APIs? - // This HTTP model is typed. It uses Akka HTTP types internally, but - // no Akka HTTP types are visible to users. This typed model is a - // model that Play Framework may eventually move to. - - object ExampleLibrary { - - trait TypedHeader { - def name: String - def value: String - } - - private[ExampleLibrary] case class GenericHeader(internal: HttpHeader) extends TypedHeader { - def name: String = internal.name - def value: String = internal.value - } - private[ExampleLibrary] case class ContentTypeHeader(contentType: ContentType) extends TypedHeader { - def name: String = "Content-Type" - def value: String = contentType.toString - } - private[ExampleLibrary] case class ContentLengthHeader(length: Long) extends TypedHeader { - def name: String = "Content-Length" - def value: String = length.toString - } - - // Headers can be created from strings. - def header(name: String, value: String): TypedHeader = { - val parsedHeader = HttpHeader.parse(name, value) match { - case HttpHeader.ParsingResult.Ok(h, Nil) ⇒ h - case x ⇒ sys.error(s"Failed to parse: ${x.errors}") - } - parsedHeader match { - case `Content-Type`(contentType) ⇒ ContentTypeHeader(contentType) - case `Content-Length`(length) ⇒ ContentLengthHeader(length) - case _ ⇒ GenericHeader(parsedHeader) - } - } - - // Or they can be created more directly, without parsing. - def contentType(contentType: ContentType): TypedHeader = - new ContentTypeHeader(contentType) - def contentLength(length: Long): TypedHeader = - new ContentLengthHeader(length) - - } - - // Users of ExampleLibrary should be able to create headers by - // parsing strings - ExampleLibrary.header("X-Y-Z", "abc") - ExampleLibrary.header("Host", "null") - ExampleLibrary.header("Content-Length", "3") - - // Users should also be able to create headers directly. Internally - // we want avoid parsing when possible (for efficiency), so it's good - // to be able to directly create a ContentType and ContentLength - // headers. - ExampleLibrary.contentLength(3) - ExampleLibrary.contentType(ContentTypes.`text/plain(UTF-8)`) - } - - } -} diff --git a/akka-http-core/src/test/scripts/autobahn-jenkins/step-1-client-tests.sh b/akka-http-core/src/test/scripts/autobahn-jenkins/step-1-client-tests.sh deleted file mode 100644 index 7a54c230e6..0000000000 --- a/akka-http-core/src/test/scripts/autobahn-jenkins/step-1-client-tests.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash -### CLIENT TESTS ### - -CLIENT_CID=autobahn-client.cid - -echo "~~~ Docker state before running CLIENT tests ~~~" -listeners=docker ps | grep '8080->8080' | wc -l -if [ "$listeners" == "0" ]; then - echo "Docker state OK..." -else - for id in $(docker ps -q); do - docker kill $id; - done -fi - -## RUN CLIENT-SIDE TEST SERVER ## -# TODO technically should be possible to -v mount the reports instead (unsure on directory though) -# this would spare us step-3 -rm -f $CLIENT_CID -docker run -d --cidfile=$CLIENT_CID \ - -p 8080:8080 -p 9001:9001 \ - jrudolph/autobahn-testsuite diff --git a/akka-http-core/src/test/scripts/autobahn-jenkins/step-2-run-sbt-client.sh b/akka-http-core/src/test/scripts/autobahn-jenkins/step-2-run-sbt-client.sh deleted file mode 100644 index f8b0107c47..0000000000 --- a/akka-http-core/src/test/scripts/autobahn-jenkins/step-2-run-sbt-client.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -## MOCK, use jenkins to run: -# 'akka-http-core-experimental/test:run-main akka.http.impl.engine.ws.WSClientAutobahnTest' \ No newline at end of file diff --git a/akka-http-core/src/test/scripts/autobahn-jenkins/step-3-download-client-test-results.sh b/akka-http-core/src/test/scripts/autobahn-jenkins/step-3-download-client-test-results.sh deleted file mode 100644 index 85c5a77a2b..0000000000 --- a/akka-http-core/src/test/scripts/autobahn-jenkins/step-3-download-client-test-results.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash -# "download" [client] results from docker container - -rm -rf localhost\:8080/ -echo "Downloading client results report..." -wget --recursive --quiet --page-requisites --html-extension \ - --convert-links --domains localhost --no-parent \ - --directory-prefix=client/ \ - http://localhost:8080/cwd/reports/clients/index.html - -rm -f client/index.json* -wget --quiet --directory-prefix=client/ \ - http://localhost:8080/cwd/reports/clients/index.json diff --git a/akka-http-core/src/test/scripts/autobahn-jenkins/step-4-stop-client-tests-server.sh b/akka-http-core/src/test/scripts/autobahn-jenkins/step-4-stop-client-tests-server.sh deleted file mode 100644 index 2a8a31d916..0000000000 --- a/akka-http-core/src/test/scripts/autobahn-jenkins/step-4-stop-client-tests-server.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash -# STOP TEST SERVER (CLIENT TESTS) -docker stop $(cat autobahn-client.cid) -rm -f autobahn-client.cid \ No newline at end of file diff --git a/akka-http-core/src/test/scripts/autobahn-jenkins/step-5-server-tests-all.sh b/akka-http-core/src/test/scripts/autobahn-jenkins/step-5-server-tests-all.sh deleted file mode 100644 index 3d35452bb5..0000000000 --- a/akka-http-core/src/test/scripts/autobahn-jenkins/step-5-server-tests-all.sh +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env bash -### SERVER TESTS ### - -## RUN AKKA WS-ECHO SERVER ## -# sadly hardcoded here, we must do much more than just "run sbt", thus running manually -SBT_JAR=/usr/share/sbt-launcher-packaging/bin/sbt-launch.jar # this jar is "sbt-latest-deb" (from the apt package) -SBT_OPTS="-Dsbt.ivy.home=/localhome/jenkinsakka/.ivy2 -Dsbt.override.repos=false" - -# warning, bash magic – the parens are important, they cause the background process to be executed in a sub shell -# and only thanks to that, will it not be forced into [Stopped] state, as bash thinks sbt is awaiting on some input -# and then decides to stop it (instead of keep it running). Keeping it in a sub shell, keeps it running. -TEST_CLASS=akka.http.impl.engine.ws.WSServerAutobahnTest -(java $SBT_OPTS -Dakka.ws-mode=sleep -Dakka.ws-host=127.0.0.1 -jar $SBT_JAR "akka-http-core-experimental/test:run-main $TEST_CLASS" | tee output &) - -# because of the sub-shell $! is not reported, we need to find the PID some other way: -SUB_PID=$(jps -mlV | grep $TEST_CLASS | awk '{print $1}') # the PID of JVM - - -## PREPARE TCK ## -echo "~~~ Docker state before running SERVER tests ~~~" -listeners=docker ps | grep '8080->8080' | wc -l -if [ "$listeners" == "0" ]; then - echo "Docker state OK..." -else - for id in $(docker ps -q); do - docker kill $id; - done -fi - - -## AWAIT ON SERVER INIT ## -# we need to wait for the server to start (compilation may happen etc, it may take time) -set +x -echo "Awaiting server startup before running tests" -while [ "$(grep 'akka.http.impl.engine.ws.WSServerAutobahnTest' output | wc -l)" == "0" ]; -do - sleep 5 - echo -n '.'; -done -set -x - -# we need to configure the test-client to hit 127.0.0.1 (this works, even from within the container, see below) -echo '{ - "outdir": "/tmp/server-report", - "servers": [ - { - "agent": "AutobahnPython", - "url": "ws://127.0.0.1:9001" - } - ], - "cases": ["*"], - "exclude-cases": [], - "exclude-agent-cases": {} -}' > /tmp/fuzzingclient.json - -## RUN TESTS ## -# net=host allows treating localhost in the container as-if the "hosts" -docker run --net=host \ - --rm=true \ - -v /tmp/fuzzingclient.json:/tmp/fuzzingclient.json \ - -v `pwd`/server-reports:/tmp/server-report \ - jrudolph/autobahn-testsuite-client - -# reports are automatically put to `pwd`/reports, we expose them in jenkins - -# okey, time to shut down the server -kill -9 $SUB_PID \ No newline at end of file diff --git a/akka-http-core/src/test/scripts/autobahn-jenkins/step-6-collect-results.sh b/akka-http-core/src/test/scripts/autobahn-jenkins/step-6-collect-results.sh deleted file mode 100644 index f236a1158a..0000000000 --- a/akka-http-core/src/test/scripts/autobahn-jenkins/step-6-collect-results.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env bash -set +x -RED='\033[0;31m' -GREEN='\033[0;32m' -NC='\033[0m' # No Color - -# FAIL IF CLIENT TESTS FAILED -# one is there because of the example column -client_errors=$(cat client/index.json | grep --ignore-case -n -B1 -A3 '"behavior": "fail' || true) # or true, in order to not fail the build if grep finds no lines -if [ "$(echo $client_errors | wc -c)" -gt "1" ]; -then - echo -e "${RED}[FAILED] WebSocket Client tests failed! See report (you can find it on the left-hand side panel on Jenkins). ${NC}" - echo -e "${RED}[FAILED] Client tests report available: https://jenkins.akka.io:8498/job/akka-http-websockets/$BUILD_NUMBER/Autobahn_TCK_%E2%80%93_WebSocket_Client/ ${NC}" - echo -e "${RED}[FAILED] Summary:" - echo -e "$(cat client/index.json | grep --ignore-case -n -B1 -A3 '"behavior": "fail')" - echo -e "${NC}" - exit -1 -else - echo -e "${GREEN}[PASSED] WebSocket Client tests passed...${NC}" -fi - - -# FAIL IF SERVER TESTS FAILED -server_errors=$(cat reports/index.json | grep --ignore-case -n -B1 -A3 '"behavior": "fail' || true) # or true, in order to not fail the build if grep finds no lines -if [ "$(echo $server_errors | wc -c)" -gt "1" ]; -then - echo -e "${RED}[FAILED] WebSocket Server tests failed! See report (you can find it on the left-hand side panel on Jenkins). ${NC}" - echo -e "${RED}[FAILED] Server tests report available: https://jenkins.akka.io:8498/job/akka-http-websockets/$BUILD_NUMBER/Autobahn_TCK_%E2%80%93_WebSocket_Server/ ${NC}" - echo -e "${RED}[FAILED] Summary:" - echo -e "$(cat reports/index.json | grep --ignore-case -n -B1 -A3 '"behavior": "fail')" - echo -e "${NC}" - exit -1 -else - echo -e "${GREEN}[PASSED] WebSocket Server tests passed...${NC}" -fi - -set -x diff --git a/akka-http-marshallers-java/akka-http-jackson/build.sbt b/akka-http-marshallers-java/akka-http-jackson/build.sbt deleted file mode 100644 index d4b5e3b031..0000000000 --- a/akka-http-marshallers-java/akka-http-jackson/build.sbt +++ /dev/null @@ -1,10 +0,0 @@ -import akka._ - -AkkaBuild.defaultSettings -AkkaBuild.experimentalSettings -Formatting.formatSettings -OSGi.httpJackson -Dependencies.httpJackson - -enablePlugins(ScaladocNoVerificationOfDiagrams) -disablePlugins(MimaPlugin) // still experimental diff --git a/akka-http-marshallers-java/akka-http-jackson/src/main/java/akka/http/javadsl/marshallers/jackson/Jackson.java b/akka-http-marshallers-java/akka-http-jackson/src/main/java/akka/http/javadsl/marshallers/jackson/Jackson.java deleted file mode 100644 index 92cd6b9bb6..0000000000 --- a/akka-http-marshallers-java/akka-http-jackson/src/main/java/akka/http/javadsl/marshallers/jackson/Jackson.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2009-2016 Typesafe Inc. - */ -package akka.http.javadsl.marshallers.jackson; - -import java.io.IOException; - -import akka.http.javadsl.model.HttpEntity; -import akka.http.javadsl.model.MediaTypes; -import akka.http.javadsl.model.RequestEntity; -import akka.http.javadsl.marshalling.Marshaller; -import akka.http.javadsl.unmarshalling.Unmarshaller; - -import akka.util.ByteString; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.MapperFeature; -import com.fasterxml.jackson.databind.ObjectMapper; - -public class Jackson { - private static final ObjectMapper defaultObjectMapper = - new ObjectMapper().enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY); - - public static Marshaller marshaller() { - return marshaller(defaultObjectMapper); - } - - public static Marshaller marshaller(ObjectMapper mapper) { - return Marshaller.wrapEntity( - u -> toJSON(mapper, u), - Marshaller.stringToEntity(), - MediaTypes.APPLICATION_JSON - ); - } - - public static Unmarshaller byteStringUnmarshaller(Class expectedType) { - return byteStringUnmarshaller(defaultObjectMapper, expectedType); - } - - public static Unmarshaller unmarshaller(Class expectedType) { - return unmarshaller(defaultObjectMapper, expectedType); - } - - public static Unmarshaller unmarshaller(ObjectMapper mapper, Class expectedType) { - return Unmarshaller.forMediaType(MediaTypes.APPLICATION_JSON, Unmarshaller.entityToString()) - .thenApply(s -> fromJSON(mapper, s, expectedType)); - } - - public static Unmarshaller byteStringUnmarshaller(ObjectMapper mapper, Class expectedType) { - return Unmarshaller.sync(s -> fromJSON(mapper, s.utf8String(), expectedType)); - } - - private static String toJSON(ObjectMapper mapper, Object object) { - try { - return mapper.writeValueAsString(object); - } catch (JsonProcessingException e) { - throw new IllegalArgumentException("Cannot marshal to JSON: " + object, e); - } - } - - private static T fromJSON(ObjectMapper mapper, String json, Class expectedType) { - try { - return mapper.readerFor(expectedType).readValue(json); - } catch (IOException e) { - throw new IllegalArgumentException("Cannot unmarshal JSON as " + expectedType.getSimpleName(), e); - } - } -} diff --git a/akka-http-marshallers-scala/akka-http-spray-json/build.sbt b/akka-http-marshallers-scala/akka-http-spray-json/build.sbt deleted file mode 100644 index 87798cf403..0000000000 --- a/akka-http-marshallers-scala/akka-http-spray-json/build.sbt +++ /dev/null @@ -1,9 +0,0 @@ -import akka._ - -AkkaBuild.defaultSettings -AkkaBuild.experimentalSettings -Formatting.formatSettings -OSGi.httpSprayJson -Dependencies.httpSprayJson - -disablePlugins(MimaPlugin) // still experimental diff --git a/akka-http-marshallers-scala/akka-http-spray-json/src/main/scala/akka/http/scaladsl/marshallers/sprayjson/SprayJsonByteStringParserInput.scala b/akka-http-marshallers-scala/akka-http-spray-json/src/main/scala/akka/http/scaladsl/marshallers/sprayjson/SprayJsonByteStringParserInput.scala deleted file mode 100644 index 6e921267af..0000000000 --- a/akka-http-marshallers-scala/akka-http-spray-json/src/main/scala/akka/http/scaladsl/marshallers/sprayjson/SprayJsonByteStringParserInput.scala +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2016 Lightbend Inc. - */ - -package akka.http.scaladsl.marshallers.sprayjson - -import java.nio.{ ByteBuffer, CharBuffer } -import java.nio.charset.{ Charset, StandardCharsets } - -import akka.util.ByteString -import spray.json.ParserInput.DefaultParserInput -import scala.annotation.tailrec - -/** - * ParserInput reading directly off a ByteString. (Based on the ByteArrayBasedParserInput) - * that avoids a separate decoding step. - */ -final class SprayJsonByteStringParserInput(bytes: ByteString) extends DefaultParserInput { - - import SprayJsonByteStringParserInput._ - - private[this] val byteBuffer = ByteBuffer.allocate(4) - private[this] val charBuffer = CharBuffer.allocate(1) - - private[this] val decoder = Charset.forName("UTF-8").newDecoder() - - override def nextChar() = { - _cursor += 1 - if (_cursor < bytes.length) (bytes(_cursor) & 0xFF).toChar else EOI - } - - override def nextUtf8Char() = { - @tailrec def decode(byte: Byte, remainingBytes: Int): Char = { - byteBuffer.put(byte) - if (remainingBytes > 0) { - _cursor += 1 - if (_cursor < bytes.length) decode(bytes(_cursor), remainingBytes - 1) else ErrorChar - } else { - byteBuffer.flip() - val coderResult = decoder.decode(byteBuffer, charBuffer, false) - charBuffer.flip() - val result = if (coderResult.isUnderflow & charBuffer.hasRemaining) charBuffer.get() else ErrorChar - byteBuffer.clear() - charBuffer.clear() - result - } - } - - _cursor += 1 - if (_cursor < bytes.length) { - val byte = bytes(_cursor) - if (byte >= 0) byte.toChar // 7-Bit ASCII - else if ((byte & 0xE0) == 0xC0) decode(byte, 1) // 2-byte UTF-8 sequence - else if ((byte & 0xF0) == 0xE0) decode(byte, 2) // 3-byte UTF-8 sequence - else if ((byte & 0xF8) == 0xF0) decode(byte, 3) // 4-byte UTF-8 sequence, will probably produce an (unsupported) surrogate pair - else ErrorChar - } else EOI - } - - override def length: Int = bytes.size - override def sliceString(start: Int, end: Int): String = - bytes.slice(start, end - start).decodeString(StandardCharsets.UTF_8) - override def sliceCharArray(start: Int, end: Int): Array[Char] = - StandardCharsets.UTF_8.decode(bytes.slice(start, end).asByteBuffer).array() -} - -object SprayJsonByteStringParserInput { - private final val EOI = '\uFFFF' - // compile-time constant - private final val ErrorChar = '\uFFFD' // compile-time constant, universal UTF-8 replacement character '�' -} diff --git a/akka-http-marshallers-scala/akka-http-spray-json/src/main/scala/akka/http/scaladsl/marshallers/sprayjson/SprayJsonSupport.scala b/akka-http-marshallers-scala/akka-http-spray-json/src/main/scala/akka/http/scaladsl/marshallers/sprayjson/SprayJsonSupport.scala deleted file mode 100644 index 7f91733aa4..0000000000 --- a/akka-http-marshallers-scala/akka-http-spray-json/src/main/scala/akka/http/scaladsl/marshallers/sprayjson/SprayJsonSupport.scala +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.marshallers.sprayjson - -import akka.NotUsed -import akka.http.scaladsl.common.EntityStreamingSupport -import akka.http.scaladsl.marshalling._ -import akka.http.scaladsl.model.MediaTypes.`application/json` -import akka.http.scaladsl.model.{ HttpCharsets, MediaTypes } -import akka.http.scaladsl.unmarshalling.{ FromByteStringUnmarshaller, FromEntityUnmarshaller, Unmarshaller } -import akka.http.scaladsl.util.FastFuture -import akka.stream.scaladsl.{ Flow, Keep, Source } -import akka.util.ByteString -import spray.json._ - -import scala.language.implicitConversions - -/** - * A trait providing automatic to and from JSON marshalling/unmarshalling using an in-scope *spray-json* protocol. - */ -trait SprayJsonSupport { - implicit def sprayJsonUnmarshallerConverter[T](reader: RootJsonReader[T]): FromEntityUnmarshaller[T] = - sprayJsonUnmarshaller(reader) - implicit def sprayJsonUnmarshaller[T](implicit reader: RootJsonReader[T]): FromEntityUnmarshaller[T] = - sprayJsValueUnmarshaller.map(jsonReader[T].read) - implicit def sprayJsonByteStringUnmarshaller[T](implicit reader: RootJsonReader[T]): FromByteStringUnmarshaller[T] = - Unmarshaller.withMaterializer[ByteString, JsValue](_ ⇒ implicit mat ⇒ { bs ⇒ - // .compact so addressing into any address is very fast (also for large chunks) - // TODO we could optimise ByteStrings to better handle linear access like this (or provide ByteStrings.linearAccessOptimised) - // TODO IF it's worth it. - val parserInput = new SprayJsonByteStringParserInput(bs.compact) - FastFuture.successful(JsonParser(parserInput)) - }).map(jsonReader[T].read) - implicit def sprayJsValueUnmarshaller: FromEntityUnmarshaller[JsValue] = - Unmarshaller.byteStringUnmarshaller.forContentTypes(`application/json`).mapWithCharset { (data, charset) ⇒ - val input = - if (charset == HttpCharsets.`UTF-8`) ParserInput(data.toArray) - else ParserInput(data.decodeString(charset.nioCharset)) - JsonParser(input) - } - // support for as[Source[T, NotUsed]] - implicit def sprayJsonSourceReader[T](implicit reader: RootJsonReader[T], support: EntityStreamingSupport): FromEntityUnmarshaller[Source[T, NotUsed]] = - Unmarshaller.withMaterializer { implicit ec ⇒ implicit mat ⇒ e ⇒ - if (support.supported.matches(e.contentType)) { - val frames = e.dataBytes.via(support.framingDecoder) - val unmarshal = sprayJsonByteStringUnmarshaller(reader)(_) - val unmarshallingFlow = - if (support.unordered) Flow[ByteString].mapAsyncUnordered(support.parallelism)(unmarshal) - else Flow[ByteString].mapAsync(support.parallelism)(unmarshal) - val elements = frames.viaMat(unmarshallingFlow)(Keep.right) - FastFuture.successful(elements) - } else FastFuture.failed(Unmarshaller.UnsupportedContentTypeException(support.supported)) - } - - implicit def sprayJsonMarshallerConverter[T](writer: RootJsonWriter[T])(implicit printer: JsonPrinter = CompactPrinter): ToEntityMarshaller[T] = - sprayJsonMarshaller[T](writer, printer) - implicit def sprayJsonMarshaller[T](implicit writer: RootJsonWriter[T], printer: JsonPrinter = CompactPrinter): ToEntityMarshaller[T] = - sprayJsValueMarshaller compose writer.write - implicit def sprayJsValueMarshaller(implicit printer: JsonPrinter = CompactPrinter): ToEntityMarshaller[JsValue] = - Marshaller.StringMarshaller.wrap(MediaTypes.`application/json`)(printer) -} -object SprayJsonSupport extends SprayJsonSupport diff --git a/akka-http-marshallers-scala/akka-http-xml/build.sbt b/akka-http-marshallers-scala/akka-http-xml/build.sbt deleted file mode 100644 index 7c8f5d6739..0000000000 --- a/akka-http-marshallers-scala/akka-http-xml/build.sbt +++ /dev/null @@ -1,9 +0,0 @@ -import akka._ - -AkkaBuild.defaultSettings -AkkaBuild.experimentalSettings -Formatting.formatSettings -OSGi.httpXml -Dependencies.httpXml - -disablePlugins(MimaPlugin) // still experimental diff --git a/akka-http-marshallers-scala/akka-http-xml/src/main/scala/akka/http/scaladsl/marshallers/xml/ScalaXmlSupport.scala b/akka-http-marshallers-scala/akka-http-xml/src/main/scala/akka/http/scaladsl/marshallers/xml/ScalaXmlSupport.scala deleted file mode 100644 index 134fabb941..0000000000 --- a/akka-http-marshallers-scala/akka-http-xml/src/main/scala/akka/http/scaladsl/marshallers/xml/ScalaXmlSupport.scala +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.marshallers.xml - -import java.io.{ ByteArrayInputStream, InputStreamReader } -import javax.xml.parsers.{ SAXParserFactory, SAXParser } -import scala.collection.immutable -import scala.xml.{ XML, NodeSeq } -import akka.http.scaladsl.unmarshalling._ -import akka.http.scaladsl.marshalling._ -import akka.http.scaladsl.model._ -import MediaTypes._ - -trait ScalaXmlSupport { - implicit def defaultNodeSeqMarshaller: ToEntityMarshaller[NodeSeq] = - Marshaller.oneOf(ScalaXmlSupport.nodeSeqMediaTypes.map(nodeSeqMarshaller): _*) - - def nodeSeqMarshaller(mediaType: MediaType.NonBinary): ToEntityMarshaller[NodeSeq] = - Marshaller.StringMarshaller.wrap(mediaType)(_.toString()) - - implicit def defaultNodeSeqUnmarshaller: FromEntityUnmarshaller[NodeSeq] = - nodeSeqUnmarshaller(ScalaXmlSupport.nodeSeqContentTypeRanges: _*) - - def nodeSeqUnmarshaller(ranges: ContentTypeRange*): FromEntityUnmarshaller[NodeSeq] = - Unmarshaller.byteArrayUnmarshaller.forContentTypes(ranges: _*).mapWithCharset { (bytes, charset) ⇒ - if (bytes.length > 0) { - val reader = new InputStreamReader(new ByteArrayInputStream(bytes), charset.nioCharset) - XML.withSAXParser(createSAXParser()).load(reader): NodeSeq // blocking call! Ideally we'd have a `loadToFuture` - } else NodeSeq.Empty - } - - /** - * Provides a SAXParser for the NodeSeqUnmarshaller to use. Override to provide a custom SAXParser implementation. - * Will be called once for for every request to be unmarshalled. The default implementation calls `ScalaXmlSupport.createSaferSAXParser`. - */ - protected def createSAXParser(): SAXParser = ScalaXmlSupport.createSaferSAXParser() -} -object ScalaXmlSupport extends ScalaXmlSupport { - val nodeSeqMediaTypes: immutable.Seq[MediaType.NonBinary] = List(`text/xml`, `application/xml`, `text/html`, `application/xhtml+xml`) - val nodeSeqContentTypeRanges: immutable.Seq[ContentTypeRange] = nodeSeqMediaTypes.map(ContentTypeRange(_)) - - /** Creates a safer SAXParser. */ - def createSaferSAXParser(): SAXParser = { - val factory = SAXParserFactory.newInstance() - import com.sun.org.apache.xerces.internal.impl.Constants - import javax.xml.XMLConstants - - factory.setFeature(Constants.SAX_FEATURE_PREFIX + Constants.EXTERNAL_GENERAL_ENTITIES_FEATURE, false) - factory.setFeature(Constants.SAX_FEATURE_PREFIX + Constants.EXTERNAL_PARAMETER_ENTITIES_FEATURE, false) - factory.setFeature(Constants.XERCES_FEATURE_PREFIX + Constants.DISALLOW_DOCTYPE_DECL_FEATURE, true) - factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true) - val parser = factory.newSAXParser() - try { - parser.setProperty("http://apache.org/xml/properties/locale", java.util.Locale.ROOT) - } catch { - case e: org.xml.sax.SAXNotRecognizedException ⇒ // property is not needed - } - parser - } -} \ No newline at end of file diff --git a/akka-http-testkit/build.sbt b/akka-http-testkit/build.sbt deleted file mode 100644 index 03c594c48a..0000000000 --- a/akka-http-testkit/build.sbt +++ /dev/null @@ -1,10 +0,0 @@ -import akka._ - -AkkaBuild.defaultSettings -Formatting.formatSettings -OSGi.httpTestkit -Dependencies.httpTestkit - -scalacOptions in Compile += "-language:postfixOps" - -disablePlugins(MimaPlugin) // testkit, no bin compat guaranteed diff --git a/akka-http-testkit/src/main/scala/akka/http/javadsl/testkit/DefaultHostInfo.scala b/akka-http-testkit/src/main/scala/akka/http/javadsl/testkit/DefaultHostInfo.scala deleted file mode 100644 index 1bead9ee9e..0000000000 --- a/akka-http-testkit/src/main/scala/akka/http/javadsl/testkit/DefaultHostInfo.scala +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.javadsl.testkit - -import akka.http.javadsl.model.headers.Host - -final case class DefaultHostInfo(private val host: Host, private val securedConnection: Boolean) { - - def getHost(): Host = host - - def isSecuredConnection(): Boolean = securedConnection - -} diff --git a/akka-http-testkit/src/main/scala/akka/http/javadsl/testkit/JUnitRouteTest.scala b/akka-http-testkit/src/main/scala/akka/http/javadsl/testkit/JUnitRouteTest.scala deleted file mode 100644 index 68e43ffcfc..0000000000 --- a/akka-http-testkit/src/main/scala/akka/http/javadsl/testkit/JUnitRouteTest.scala +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.testkit - -import akka.actor.ActorSystem -import akka.event.Logging -import akka.http.javadsl.model.HttpRequest -import akka.http.javadsl.server._ -import akka.http.scaladsl.model.HttpResponse -import akka.stream.{ Materializer, ActorMaterializer } -import com.typesafe.config.{ ConfigFactory, Config } -import org.junit.rules.ExternalResource -import org.junit.{ Assert, Rule } -import org.scalatest.junit.{ JUnitSuiteLike, JUnitSuite } -import scala.concurrent.duration._ -import scala.concurrent.Await - -/** - * A RouteTest that uses JUnit assertions. ActorSystem and Materializer are provided as an [[org.junit.rules.ExternalResource]] - * and their lifetime is automatically managed. - */ -abstract class JUnitRouteTestBase extends RouteTest with JUnitSuiteLike { - protected def systemResource: ActorSystemResource - implicit def system: ActorSystem = systemResource.system - implicit def materializer: Materializer = systemResource.materializer - - protected def createTestRouteResult(request: HttpRequest, result: RouteResult): TestRouteResult = - new TestRouteResult(result, awaitDuration)(system.dispatcher, materializer) { - protected def assertEquals(expected: AnyRef, actual: AnyRef, message: String): Unit = - reportDetails { Assert.assertEquals(message, expected, actual) } - - protected def assertEquals(expected: Int, actual: Int, message: String): Unit = - Assert.assertEquals(message, expected, actual) - - protected def assertTrue(predicate: Boolean, message: String): Unit = - Assert.assertTrue(message, predicate) - - protected def fail(message: String): Unit = { - Assert.fail(message) - throw new IllegalStateException("Assertion should have failed") - } - - def reportDetails[T](block: ⇒ T): T = { - try block catch { - case t: Throwable ⇒ throw new AssertionError(t.getMessage + "\n" + - " Request was: " + request + "\n" + - " Route result was: " + result + "\n", t) - } - } - } -} -abstract class JUnitRouteTest extends JUnitRouteTestBase { - protected def additionalConfig: Config = ConfigFactory.empty() - - private[this] val _systemResource = new ActorSystemResource(Logging.simpleName(getClass), additionalConfig) - @Rule - protected def systemResource: ActorSystemResource = _systemResource -} - -class ActorSystemResource(name: String, additionalConfig: Config) extends ExternalResource { - protected def config = additionalConfig.withFallback(ConfigFactory.load()) - protected def createSystem(): ActorSystem = ActorSystem(name, config) - protected def createMaterializer(system: ActorSystem): ActorMaterializer = ActorMaterializer()(system) - - implicit def system: ActorSystem = _system - implicit def materializer: ActorMaterializer = _materializer - - private[this] var _system: ActorSystem = null - private[this] var _materializer: ActorMaterializer = null - - override def before(): Unit = { - require((_system eq null) && (_materializer eq null)) - _system = createSystem() - _materializer = createMaterializer(_system) - } - override def after(): Unit = { - Await.result(_system.terminate(), 5.seconds) - _system = null - _materializer = null - } -} diff --git a/akka-http-testkit/src/main/scala/akka/http/javadsl/testkit/RouteTest.scala b/akka-http-testkit/src/main/scala/akka/http/javadsl/testkit/RouteTest.scala deleted file mode 100644 index bb41a2ad8f..0000000000 --- a/akka-http-testkit/src/main/scala/akka/http/javadsl/testkit/RouteTest.scala +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.testkit - -import scala.annotation.varargs -import scala.concurrent.ExecutionContextExecutor -import scala.concurrent.duration.DurationInt -import scala.concurrent.duration.FiniteDuration -import akka.actor.ActorSystem -import akka.event.NoLogging -import akka.http.impl.util.AddFutureAwaitResult -import akka.http.impl.util.JavaMapping.Implicits.AddAsScala -import akka.http.javadsl.model.HttpRequest -import akka.http.javadsl.model.headers.Host -import akka.http.javadsl.server.AllDirectives -import akka.http.javadsl.server.Directives -import akka.http.javadsl.server.Route -import akka.http.javadsl.server.RouteResult -import akka.http.scaladsl.server -import akka.http.scaladsl.server.{ ExceptionHandler, RequestContextImpl, Route ⇒ ScalaRoute } -import akka.http.scaladsl.settings.RoutingSettings -import akka.stream.Materializer - -/** - * A base class to create route tests for testing libraries. An implementation needs to provide - * code to provide and shutdown an [[akka.actor.ActorSystem]], [[akka.stream.Materializer]], and [[scala.concurrent.ExecutionContextExecutor]]. - * - * See `JUnitRouteTest` for an example of a concrete implementation. - */ -abstract class RouteTest extends AllDirectives with WSTestRequestBuilding { - implicit def system: ActorSystem - implicit def materializer: Materializer - implicit def executionContext: ExecutionContextExecutor = system.dispatcher - - protected def awaitDuration: FiniteDuration = 3.seconds - - protected def defaultHostInfo: DefaultHostInfo = DefaultHostInfo(Host.create("example.com"), false) - - def runRoute(route: Route, request: HttpRequest): TestRouteResult = - runRoute(route, request, defaultHostInfo) - - def runRoute(route: Route, request: HttpRequest, defaultHostInfo: DefaultHostInfo): TestRouteResult = - runScalaRoute(route.seal(system, materializer).delegate, request, defaultHostInfo) - - def runRouteUnSealed(route: Route, request: HttpRequest): TestRouteResult = - runRouteUnSealed(route, request, defaultHostInfo) - - def runRouteUnSealed(route: Route, request: HttpRequest, defaultHostInfo: DefaultHostInfo): TestRouteResult = - runScalaRoute(route.delegate, request, defaultHostInfo) - - private def runScalaRoute(scalaRoute: ScalaRoute, request: HttpRequest, defaultHostInfo: DefaultHostInfo): TestRouteResult = { - val effectiveRequest = request.asScala - .withEffectiveUri( - securedConnection = defaultHostInfo.isSecuredConnection(), - defaultHostHeader = defaultHostInfo.getHost().asScala) - - // this will give us the default exception handler - val sealedExceptionHandler = ExceptionHandler.seal(null) - - val semiSealedRoute = // sealed for exceptions but not for rejections - akka.http.scaladsl.server.Directives.handleExceptions(sealedExceptionHandler)(scalaRoute) - - val result = semiSealedRoute(new server.RequestContextImpl(effectiveRequest, system.log, RoutingSettings(system))) - createTestRouteResult(request, result.awaitResult(awaitDuration)) - } - - /** - * Wraps a list of route alternatives with testing support. - */ - @varargs - def testRoute(first: Route, others: Route*): TestRoute = - new TestRoute { - val underlying: Route = Directives.route(first +: others: _*) - - def run(request: HttpRequest): TestRouteResult = runRoute(underlying, request) - } - - protected def createTestRouteResult(request: HttpRequest, result: RouteResult): TestRouteResult -} diff --git a/akka-http-testkit/src/main/scala/akka/http/javadsl/testkit/TestRoute.scala b/akka-http-testkit/src/main/scala/akka/http/javadsl/testkit/TestRoute.scala deleted file mode 100644 index 175d3b5921..0000000000 --- a/akka-http-testkit/src/main/scala/akka/http/javadsl/testkit/TestRoute.scala +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.testkit - -import akka.http.javadsl.model.HttpRequest -import akka.http.javadsl.server.Route - -/** - * A wrapped route that has a `run` method to run a request through the underlying route to create - * a [[TestResponse]]. - * - * A TestRoute is created by deriving a test class from the concrete RouteTest implementation for your - * testing framework (like [[JUnitRouteTest]] for JUnit) and then using its `testRoute` method to wrap - * a route with testing support. - */ -trait TestRoute { - def underlying: Route - def run(request: HttpRequest): TestRouteResult -} \ No newline at end of file diff --git a/akka-http-testkit/src/main/scala/akka/http/javadsl/testkit/TestRouteResult.scala b/akka-http-testkit/src/main/scala/akka/http/javadsl/testkit/TestRouteResult.scala deleted file mode 100644 index 54859d3de7..0000000000 --- a/akka-http-testkit/src/main/scala/akka/http/javadsl/testkit/TestRouteResult.scala +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.testkit - -import akka.http.javadsl.server.RouteResult -import akka.http.javadsl.unmarshalling.Unmarshaller - -import scala.reflect.ClassTag -import scala.concurrent.ExecutionContext -import scala.concurrent.duration.FiniteDuration -import akka.util.ByteString -import akka.stream.Materializer -import akka.http.scaladsl -import akka.http.scaladsl.unmarshalling.Unmarshal -import akka.http.scaladsl.model.HttpResponse -import akka.http.impl.util._ -import akka.http.impl.util.JavaMapping.Implicits._ -import akka.http.javadsl.server.{ Rejection, RoutingJavaMapping } -import RoutingJavaMapping._ -import akka.http.javadsl.model._ - -import scala.collection.JavaConverters._ -import scala.annotation.varargs - -/** - * A wrapper for route results. - * - * To support the testkit API, a third-party testing library needs to implement this class and provide - * implementations for the abstract assertion methods. - */ -abstract class TestRouteResult(_result: RouteResult, awaitAtMost: FiniteDuration)(implicit ec: ExecutionContext, materializer: Materializer) { - - private def _response = _result match { - case scaladsl.server.RouteResult.Complete(r) ⇒ r - case scaladsl.server.RouteResult.Rejected(rejections) ⇒ doFail("Expected route to complete, but was instead rejected with " + rejections) - } - - private def _rejections = _result match { - case scaladsl.server.RouteResult.Complete(r) ⇒ doFail("Request was not rejected, response was " + r) - case scaladsl.server.RouteResult.Rejected(ex) ⇒ ex - } - - /** - * Returns the strictified entity of the response. It will be strictified on first access. - */ - lazy val entity: HttpEntity.Strict = _response.entity.toStrict(awaitAtMost).awaitResult(awaitAtMost) - - /** - * Returns a copy of the underlying response with the strictified entity. - */ - lazy val response: HttpResponse = _response.withEntity(entity) - - /** - * Returns the response's content-type - */ - def contentType: ContentType = _response.entity.contentType - - /** - * Returns a string representation of the response's content-type - */ - def contentTypeString: String = contentType.toString - - /** - * Returns the media-type of the the response's content-type - */ - def mediaType: MediaType = contentType.mediaType - - /** - * Returns a string representation of the media-type of the response's content-type - */ - def mediaTypeString: String = mediaType.toString - - /** - * Returns the bytes of the response entity - */ - def entityBytes: ByteString = entity.getData - - /** - * Returns the entity of the response unmarshalled with the given ``Unmarshaller``. - */ - def entity[T](unmarshaller: Unmarshaller[HttpEntity, T]): T = - Unmarshal(response.entity) - .to(unmarshaller.asScala, ec, materializer) - .awaitResult(awaitAtMost) - - /** - * Returns the entity of the response interpreted as an UTF-8 encoded string. - */ - def entityString: String = entity.getData.utf8String - - /** - * Returns the [[akka.http.javadsl.model.StatusCode]] of the response. - */ - def status: StatusCode = response.status.asJava - - /** - * Returns the numeric status code of the response. - */ - def statusCode: Int = response.status.intValue - - /** - * Returns the first header of the response which is of the given class. - */ - def header[T >: Null <: HttpHeader](clazz: Class[T]): T = - response.header(ClassTag(clazz)) - .getOrElse(doFail(s"Expected header of type ${clazz.getSimpleName} but wasn't found.")) - - /** - * Expects the route to have been rejected, returning the list of rejections, or empty list if the route - * was rejected with an empty rejection list. - * Fails the test if the route completes with a response rather than having been rejected. - */ - def rejections: java.util.List[Rejection] = _rejections.map(_.asJava).asJava - - /** - * Expects the route to have been rejected with a single rejection. - * Fails the test if the route completes with a response, or is rejected with 0 or >1 rejections. - */ - def rejection: Rejection = { - val r = rejections - if (r.size == 1) r.get(0) else doFail("Expected a single rejection but got %s (%s)".format(r.size, r)) - } - - /** - * Assert on the numeric status code. - */ - def assertStatusCode(expected: Int): TestRouteResult = - assertStatusCode(StatusCodes.get(expected)) - - /** - * Assert on the status code. - */ - def assertStatusCode(expected: StatusCode): TestRouteResult = - assertEqualsKind(expected, status, "status code") - - /** - * Assert on the media type of the response. - */ - def assertMediaType(expected: String): TestRouteResult = - assertEqualsKind(expected, mediaTypeString, "media type") - - /** - * Assert on the media type of the response. - */ - def assertMediaType(expected: MediaType): TestRouteResult = - assertEqualsKind(expected, mediaType, "media type") - - /** - * Assert on the content type of the response. - */ - def assertContentType(expected: String): TestRouteResult = - assertEqualsKind(expected, contentTypeString, "content type") - - /** - * Assert on the content type of the response. - */ - def assertContentType(expected: ContentType): TestRouteResult = - assertEqualsKind(expected, contentType, "content type") - - /** - * Assert on the response entity to be a UTF8 representation of the given string. - */ - def assertEntity(expected: String): TestRouteResult = - assertEqualsKind(expected, entityString, "entity") - - /** - * Assert on the response entity to equal the given bytes. - */ - def assertEntityBytes(expected: ByteString): TestRouteResult = - assertEqualsKind(expected, entityBytes, "entity") - - /** - * Assert on the response entity to equal the given object after applying an [[akka.http.javadsl.unmarshalling.Unmarshaller]]. - */ - def assertEntityAs[T <: AnyRef](unmarshaller: Unmarshaller[HttpEntity, T], expected: T): TestRouteResult = - assertEqualsKind(expected, entity(unmarshaller), "entity") - - /** - * Assert that a given header instance exists in the response. - */ - def assertHeaderExists(expected: HttpHeader): TestRouteResult = { - assertTrue(response.headers.exists(_ == expected), s"Header $expected was missing.") - this - } - - /** - * Assert that a header of the given type exists. - */ - def assertHeaderKindExists(name: String): TestRouteResult = { - val lowercased = name.toRootLowerCase - assertTrue(response.headers.exists(_.is(lowercased)), s"Expected `$name` header was missing.") - this - } - - /** - * Assert that a header of the given type does not exist. - */ - def assertHeaderKindNotExists(name: String): TestRouteResult = { - val lowercased = name.toRootLowerCase - assertTrue(response.headers.forall(!_.is(lowercased)), s"`$name` header was not expected to appear.") - this - } - - /** - * Assert that a header of the given name and value exists. - */ - def assertHeaderExists(name: String, value: String): TestRouteResult = { - val lowercased = name.toRootLowerCase - val headers = response.headers.filter(_.is(lowercased)) - if (headers.isEmpty) fail(s"Expected `$name` header was missing.") - else assertTrue( - headers.exists(_.value == value), - s"`$name` header was found but had the wrong value. Found headers: ${headers.mkString(", ")}") - - this - } - - @varargs def assertRejections(expectedRejections: Rejection*): TestRouteResult = { - if (rejections.asScala == expectedRejections.toSeq) { - this - } else { - doFail(s"Expected rejections [${expectedRejections.mkString(",")}], but rejected with [${rejections.asScala.mkString(",")}] instead.") - } - } - - protected def assertEqualsKind(expected: AnyRef, actual: AnyRef, kind: String): TestRouteResult = { - assertEquals(expected, actual, s"Unexpected $kind!") - this - } - protected def assertEqualsKind(expected: Int, actual: Int, kind: String): TestRouteResult = { - assertEquals(expected, actual, s"Unexpected $kind!") - this - } - - // allows to `fail` as an expression - private def doFail(message: String): Nothing = { - fail(message) - throw new IllegalStateException("Shouldn't be reached") - } - - protected def fail(message: String): Unit - protected def assertEquals(expected: AnyRef, actual: AnyRef, message: String): Unit - protected def assertEquals(expected: Int, actual: Int, message: String): Unit - protected def assertTrue(predicate: Boolean, message: String): Unit -} diff --git a/akka-http-testkit/src/main/scala/akka/http/javadsl/testkit/WSProbe.scala b/akka-http-testkit/src/main/scala/akka/http/javadsl/testkit/WSProbe.scala deleted file mode 100644 index d00e03c8d2..0000000000 --- a/akka-http-testkit/src/main/scala/akka/http/javadsl/testkit/WSProbe.scala +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2016-2016 Lightbend Inc. - */ - -package akka.http.javadsl.testkit - -import akka.NotUsed -import akka.actor.ActorSystem -import akka.http.javadsl.model.ws.Message -import akka.stream.Materializer -import akka.stream.javadsl.Flow -import akka.stream.scaladsl -import akka.util.ByteString - -import akka.http.scaladsl.{ testkit ⇒ st } - -import akka.http.impl.util.JavaMapping.Implicits._ - -import scala.concurrent.duration._ - -/** - * A WSProbe is a probe that implements a `Flow[Message, Message, Unit]` for testing - * websocket code. - * - * Requesting elements is handled automatically. - */ -class WSProbe(delegate: st.WSProbe) { - - def flow: Flow[Message, Message, Any] = { - val underlying = scaladsl.Flow[Message].map(_.asScala).via(delegate.flow).map(_.asJava) - new Flow[Message, Message, NotUsed](underlying) - } - - /** - * Send the given messages out of the flow. - */ - def sendMessage(message: Message): Unit = delegate.sendMessage(message.asScala) - - /** - * Send a text message containing the given string out of the flow. - */ - def sendMessage(text: String): Unit = delegate.sendMessage(text) - - /** - * Send a binary message containing the given bytes out of the flow. - */ - def sendMessage(bytes: ByteString): Unit = delegate.sendMessage(bytes) - - /** - * Complete the output side of the flow. - */ - def sendCompletion(): Unit = delegate.sendCompletion() - - /** - * Expect a message on the input side of the flow. - */ - def expectMessage(): Message = delegate.expectMessage() - - /** - * Expect a text message on the input side of the flow and compares its payload with the given one. - * If the received message is streamed its contents are collected and then asserted against the given - * String. - */ - def expectMessage(text: String): Unit = delegate.expectMessage(text) - - /** - * Expect a binary message on the input side of the flow and compares its payload with the given one. - * If the received message is streamed its contents are collected and then asserted against the given - * ByteString. - */ - def expectMessage(bytes: ByteString): Unit = delegate.expectMessage(bytes) - - /** - * Expect no message on the input side of the flow. - */ - def expectNoMessage(): Unit = delegate.expectNoMessage() - - /** - * Expect no message on the input side of the flow for the given maximum duration. - */ - def expectNoMessage(max: FiniteDuration): Unit = delegate.expectNoMessage(max) - - /** - * Expect completion on the input side of the flow. - */ - def expectCompletion(): Unit = delegate.expectCompletion() - -} - -object WSProbe { - - // A convenient method to create WSProbe with default maxChunks and maxChunkCollectionMills - def create(system: ActorSystem, materializer: Materializer): WSProbe = { - create(system, materializer, 1000, 5000) - } - - /** - * Creates a WSProbe to use in tests against websocket handlers. - * - * @param maxChunks The maximum number of chunks to collect for streamed messages. - * @param maxChunkCollectionMills The maximum time in milliseconds to collect chunks for streamed messages. - */ - def create(system: ActorSystem, materializer: Materializer, maxChunks: Int, maxChunkCollectionMills: Long): WSProbe = { - val delegate = st.WSProbe(maxChunks, maxChunkCollectionMills)(system, materializer) - new WSProbe(delegate) - } - -} diff --git a/akka-http-testkit/src/main/scala/akka/http/javadsl/testkit/WSTestRequestBuilding.scala b/akka-http-testkit/src/main/scala/akka/http/javadsl/testkit/WSTestRequestBuilding.scala deleted file mode 100644 index abd4340b19..0000000000 --- a/akka-http-testkit/src/main/scala/akka/http/javadsl/testkit/WSTestRequestBuilding.scala +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2016-2016 Lightbend Inc. - */ - -package akka.http.javadsl.testkit - -import akka.http.javadsl.model.ws.Message -import akka.http.javadsl.model.{ HttpRequest, Uri } -import akka.http.scaladsl.{ model ⇒ sm } -import akka.stream.javadsl.Flow - -import akka.http.scaladsl.{ testkit ⇒ st } - -import akka.http.impl.util.JavaMapping.Implicits._ -import scala.collection.JavaConverters._ -import akka.stream.{ Materializer, scaladsl } - -trait WSTestRequestBuilding { - - def WS(uri: Uri, clientSideHandler: Flow[Message, Message, Any], materializer: Materializer): HttpRequest = { - WS(uri, clientSideHandler, materializer, java.util.Collections.emptyList()) - } - - def WS( - uri: Uri, - clientSideHandler: Flow[Message, Message, Any], - materializer: Materializer, - subprotocols: java.util.List[String]): HttpRequest = { - - val handler = scaladsl.Flow[sm.ws.Message].map(_.asJava).via(clientSideHandler).map(_.asScala) - st.WSTestRequestBuilding.WS(uri.asScala, handler, subprotocols.asScala)(materializer) - } - -} diff --git a/akka-http-testkit/src/main/scala/akka/http/scaladsl/testkit/MarshallingTestUtils.scala b/akka-http-testkit/src/main/scala/akka/http/scaladsl/testkit/MarshallingTestUtils.scala deleted file mode 100644 index 2baaeaeccc..0000000000 --- a/akka-http-testkit/src/main/scala/akka/http/scaladsl/testkit/MarshallingTestUtils.scala +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.testkit - -import scala.concurrent.duration._ -import scala.concurrent.{ Await, ExecutionContext } -import akka.http.scaladsl.unmarshalling.{ FromEntityUnmarshaller, Unmarshal } -import akka.http.scaladsl.marshalling._ -import akka.http.scaladsl.model.{ HttpEntity, HttpRequest, HttpResponse } -import akka.stream.Materializer - -import scala.util.Try - -trait MarshallingTestUtils { - def marshal[T: ToEntityMarshaller](value: T)(implicit ec: ExecutionContext, mat: Materializer): HttpEntity.Strict = - Await.result(Marshal(value).to[HttpEntity].flatMap(_.toStrict(1.second)), 1.second) - - def marshalToResponse[T: ToResponseMarshaller](value: T, request: HttpRequest = HttpRequest())(implicit ec: ExecutionContext, mat: Materializer): HttpResponse = { - Await.result(Marshal(value).toResponseFor(request), 1.second) - } - - def unmarshalValue[T: FromEntityUnmarshaller](entity: HttpEntity)(implicit ec: ExecutionContext, mat: Materializer): T = - unmarshal(entity).get - - def unmarshal[T: FromEntityUnmarshaller](entity: HttpEntity)(implicit ec: ExecutionContext, mat: Materializer): Try[T] = { - val fut = Unmarshal(entity).to[T] - Await.ready(fut, 1.second) - fut.value.get - } -} - diff --git a/akka-http-testkit/src/main/scala/akka/http/scaladsl/testkit/RouteTest.scala b/akka-http-testkit/src/main/scala/akka/http/scaladsl/testkit/RouteTest.scala deleted file mode 100644 index b7e1c15a36..0000000000 --- a/akka-http-testkit/src/main/scala/akka/http/scaladsl/testkit/RouteTest.scala +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.testkit - -import akka.http.scaladsl.marshalling.ToResponseMarshallable -import akka.http.scaladsl.server.directives.ExecutionDirectives._ -import akka.http.scaladsl.settings.RoutingSettings -import akka.stream.impl.ConstantFun -import com.typesafe.config.{ ConfigFactory, Config } -import scala.collection.immutable -import scala.concurrent.{ ExecutionContext, Await, Future } -import scala.concurrent.duration._ -import scala.util.DynamicVariable -import scala.reflect.ClassTag -import akka.actor.ActorSystem -import akka.stream.{ Materializer, ActorMaterializer } -import akka.http.scaladsl.client.RequestBuilding -import akka.http.scaladsl.util.FastFuture -import akka.http.scaladsl.server._ -import akka.http.scaladsl.unmarshalling._ -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers.{ Upgrade, `Sec-WebSocket-Protocol`, Host } -import FastFuture._ - -trait RouteTest extends RequestBuilding with WSTestRequestBuilding with RouteTestResultComponent with MarshallingTestUtils { - this: TestFrameworkInterface ⇒ - - /** Override to supply a custom ActorSystem */ - protected def createActorSystem(): ActorSystem = - ActorSystem(actorSystemNameFrom(getClass), testConfig) - - def actorSystemNameFrom(clazz: Class[_]) = - clazz.getName - .replace('.', '-') - .replace('_', '-') - .filter(_ != '$') - - def testConfigSource: String = "" - def testConfig: Config = { - val source = testConfigSource - val config = if (source.isEmpty) ConfigFactory.empty() else ConfigFactory.parseString(source) - config.withFallback(ConfigFactory.load()) - } - implicit val system = createActorSystem() - implicit def executor = system.dispatcher - implicit val materializer = ActorMaterializer() - - def cleanUp(): Unit = system.terminate() - - private val dynRR = new DynamicVariable[RouteTestResult](null) - private def result = - if (dynRR.value ne null) dynRR.value - else sys.error("This value is only available inside of a `check` construct!") - - def check[T](body: ⇒ T): RouteTestResult ⇒ T = result ⇒ dynRR.withValue(result.awaitResult)(body) - - private def responseSafe = if (dynRR.value ne null) dynRR.value.response else "" - - def handled: Boolean = result.handled - def response: HttpResponse = result.response - def responseEntity: HttpEntity = result.entity - def chunks: immutable.Seq[HttpEntity.ChunkStreamPart] = result.chunks - def entityAs[T: FromEntityUnmarshaller: ClassTag](implicit timeout: Duration = 1.second): T = { - def msg(e: Throwable) = s"Could not unmarshal entity to type '${implicitly[ClassTag[T]]}' for `entityAs` assertion: $e\n\nResponse was: $responseSafe" - Await.result(Unmarshal(responseEntity).to[T].fast.recover[T] { case error ⇒ failTest(msg(error)) }, timeout) - } - def responseAs[T: FromResponseUnmarshaller: ClassTag](implicit timeout: Duration = 1.second): T = { - def msg(e: Throwable) = s"Could not unmarshal response to type '${implicitly[ClassTag[T]]}' for `responseAs` assertion: $e\n\nResponse was: $responseSafe" - Await.result(Unmarshal(response).to[T].fast.recover[T] { case error ⇒ failTest(msg(error)) }, timeout) - } - def contentType: ContentType = responseEntity.contentType - def mediaType: MediaType = contentType.mediaType - def charsetOption: Option[HttpCharset] = contentType.charsetOption - def charset: HttpCharset = charsetOption getOrElse sys.error("Binary entity does not have charset") - def headers: immutable.Seq[HttpHeader] = response.headers - def header[T >: Null <: HttpHeader: ClassTag]: Option[T] = response.header[T](implicitly[ClassTag[T]]) - def header(name: String): Option[HttpHeader] = response.headers.find(_.is(name.toLowerCase)) - def status: StatusCode = response.status - - def closingExtension: String = chunks.lastOption match { - case Some(HttpEntity.LastChunk(extension, _)) ⇒ extension - case _ ⇒ "" - } - def trailer: immutable.Seq[HttpHeader] = chunks.lastOption match { - case Some(HttpEntity.LastChunk(_, trailer)) ⇒ trailer - case _ ⇒ Nil - } - - def rejections: immutable.Seq[Rejection] = result.rejections - def rejection: Rejection = { - val r = rejections - if (r.size == 1) r.head else failTest("Expected a single rejection but got %s (%s)".format(r.size, r)) - } - - def isWebSocketUpgrade: Boolean = - status == StatusCodes.SwitchingProtocols && header[Upgrade].exists(_.hasWebSocket) - - /** - * Asserts that the received response is a WebSocket upgrade response and the extracts - * the chosen subprotocol and passes it to the handler. - */ - def expectWebSocketUpgradeWithProtocol(body: String ⇒ Unit): Unit = { - if (!isWebSocketUpgrade) failTest("Response was no WebSocket Upgrade response") - header[`Sec-WebSocket-Protocol`] match { - case Some(`Sec-WebSocket-Protocol`(Seq(protocol))) ⇒ body(protocol) - case _ ⇒ failTest("No WebSocket protocol found in response.") - } - } - - /** - * A dummy that can be used as `~> runRoute` to run the route but without blocking for the result. - * The result of the pipeline is the result that can later be checked with `check`. See the - * "separate running route from checking" example from ScalatestRouteTestSpec.scala. - */ - def runRoute: RouteTestResult ⇒ RouteTestResult = ConstantFun.scalaIdentityFunction - - // there is already an implicit class WithTransformation in scope (inherited from akka.http.scaladsl.testkit.TransformerPipelineSupport) - // however, this one takes precedence - implicit class WithTransformation2(request: HttpRequest) { - /** - * Apply request to given routes for further inspection in `check { }` block. - */ - def ~>[A, B](f: A ⇒ B)(implicit ta: TildeArrow[A, B]): ta.Out = ta(request, f) - } - - abstract class TildeArrow[A, B] { - type Out - def apply(request: HttpRequest, f: A ⇒ B): Out - } - - case class DefaultHostInfo(host: Host, securedConnection: Boolean) - object DefaultHostInfo { - implicit def defaultHost: DefaultHostInfo = DefaultHostInfo(Host("example.com"), securedConnection = false) - } - object TildeArrow { - implicit object InjectIntoRequestTransformer extends TildeArrow[HttpRequest, HttpRequest] { - type Out = HttpRequest - def apply(request: HttpRequest, f: HttpRequest ⇒ HttpRequest) = f(request) - } - implicit def injectIntoRoute(implicit - timeout: RouteTestTimeout, - defaultHostInfo: DefaultHostInfo, - routingSettings: RoutingSettings, - executionContext: ExecutionContext, - materializer: Materializer, - routingLog: RoutingLog, - rejectionHandler: RejectionHandler = RejectionHandler.default, - exceptionHandler: ExceptionHandler = null) = - new TildeArrow[RequestContext, Future[RouteResult]] { - type Out = RouteTestResult - def apply(request: HttpRequest, route: Route): Out = { - val routeTestResult = new RouteTestResult(timeout.duration) - val effectiveRequest = - request.withEffectiveUri( - securedConnection = defaultHostInfo.securedConnection, - defaultHostHeader = defaultHostInfo.host) - val ctx = new RequestContextImpl(effectiveRequest, routingLog.requestLog(effectiveRequest), routingSettings) - val sealedExceptionHandler = ExceptionHandler.seal(exceptionHandler) - val semiSealedRoute = // sealed for exceptions but not for rejections - Directives.handleExceptions(sealedExceptionHandler)(route) - val deferrableRouteResult = semiSealedRoute(ctx) - deferrableRouteResult.fast.foreach(routeTestResult.handleResult)(executionContext) - routeTestResult - } - } - } -} - -//FIXME: trait Specs2RouteTest extends RouteTest with Specs2Interface diff --git a/akka-http-testkit/src/main/scala/akka/http/scaladsl/testkit/RouteTestResultComponent.scala b/akka-http-testkit/src/main/scala/akka/http/scaladsl/testkit/RouteTestResultComponent.scala deleted file mode 100644 index 44cae149f9..0000000000 --- a/akka-http-testkit/src/main/scala/akka/http/scaladsl/testkit/RouteTestResultComponent.scala +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.testkit - -import java.util.concurrent.CountDownLatch - -import scala.collection.immutable -import scala.concurrent.duration._ -import scala.concurrent.{ ExecutionContext } -import akka.stream.Materializer -import akka.stream.scaladsl._ -import akka.http.scaladsl.model.HttpEntity.ChunkStreamPart -import akka.http.scaladsl.server._ -import akka.http.scaladsl.model._ -import akka.http.impl.util._ - -trait RouteTestResultComponent { - - def failTest(msg: String): Nothing - - /** - * A receptacle for the response or rejections created by a route. - */ - class RouteTestResult(timeout: FiniteDuration)(implicit fm: Materializer) { - private[this] var result: Option[Either[immutable.Seq[Rejection], HttpResponse]] = None - private[this] val latch = new CountDownLatch(1) - - def handled: Boolean = synchronized { result.isDefined && result.get.isRight } - - def rejections: immutable.Seq[Rejection] = synchronized { - result match { - case Some(Left(rejections)) ⇒ rejections - case Some(Right(response)) ⇒ failTest("Request was not rejected, response was " + response) - case None ⇒ failNeitherCompletedNorRejected() - } - } - - def response: HttpResponse = rawResponse.copy(entity = entity) - - /** Returns a "fresh" entity with a "fresh" unconsumed byte- or chunk stream (if not strict) */ - def entity: ResponseEntity = entityRecreator() - - def chunks: immutable.Seq[ChunkStreamPart] = - entity match { - case HttpEntity.Chunked(_, chunks) ⇒ awaitAllElements[ChunkStreamPart](chunks) - case _ ⇒ Nil - } - - def ~>[T](f: RouteTestResult ⇒ T): T = f(this) - - private def rawResponse: HttpResponse = synchronized { - result match { - case Some(Right(response)) ⇒ response - case Some(Left(Nil)) ⇒ failTest("Request was rejected") - case Some(Left(rejection :: Nil)) ⇒ failTest("Request was rejected with rejection " + rejection) - case Some(Left(rejections)) ⇒ failTest("Request was rejected with rejections " + rejections) - case None ⇒ failNeitherCompletedNorRejected() - } - } - - private[testkit] def handleResult(rr: RouteResult)(implicit ec: ExecutionContext): Unit = - synchronized { - if (result.isEmpty) { - result = rr match { - case RouteResult.Complete(response) ⇒ Some(Right(response)) - case RouteResult.Rejected(rejections) ⇒ Some(Left(RejectionHandler.applyTransformations(rejections))) - } - latch.countDown() - } else failTest("Route completed/rejected more than once") - } - - private[testkit] def awaitResult: this.type = { - latch.await(timeout.toMillis, MILLISECONDS) - this - } - - private[this] lazy val entityRecreator: () ⇒ ResponseEntity = - rawResponse.entity match { - case s: HttpEntity.Strict ⇒ () ⇒ s - - case HttpEntity.Default(contentType, contentLength, data) ⇒ - val dataChunks = awaitAllElements(data); { () ⇒ HttpEntity.Default(contentType, contentLength, Source(dataChunks)) } - - case HttpEntity.CloseDelimited(contentType, data) ⇒ - val dataChunks = awaitAllElements(data); { () ⇒ HttpEntity.CloseDelimited(contentType, Source(dataChunks)) } - - case HttpEntity.Chunked(contentType, data) ⇒ - val dataChunks = awaitAllElements(data); { () ⇒ HttpEntity.Chunked(contentType, Source(dataChunks)) } - } - - private def failNeitherCompletedNorRejected(): Nothing = - failTest("Request was neither completed nor rejected within " + timeout) - - private def awaitAllElements[T](data: Source[T, _]): immutable.Seq[T] = - data.limit(100000).runWith(Sink.seq).awaitResult(timeout) - } -} diff --git a/akka-http-testkit/src/main/scala/akka/http/scaladsl/testkit/RouteTestTimeout.scala b/akka-http-testkit/src/main/scala/akka/http/scaladsl/testkit/RouteTestTimeout.scala deleted file mode 100644 index 97d056f356..0000000000 --- a/akka-http-testkit/src/main/scala/akka/http/scaladsl/testkit/RouteTestTimeout.scala +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.testkit - -import scala.concurrent.duration._ -import akka.actor.ActorSystem -import akka.testkit._ - -case class RouteTestTimeout(duration: FiniteDuration) - -object RouteTestTimeout { - implicit def default(implicit system: ActorSystem) = RouteTestTimeout(1.second dilated) -} diff --git a/akka-http-testkit/src/main/scala/akka/http/scaladsl/testkit/ScalatestUtils.scala b/akka-http-testkit/src/main/scala/akka/http/scaladsl/testkit/ScalatestUtils.scala deleted file mode 100644 index bc97a0cb3f..0000000000 --- a/akka-http-testkit/src/main/scala/akka/http/scaladsl/testkit/ScalatestUtils.scala +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.testkit - -import scala.util.Try -import scala.concurrent.{ ExecutionContext, Future, Await } -import scala.concurrent.duration._ -import org.scalatest.Suite -import org.scalatest.matchers.Matcher -import akka.stream.Materializer -import akka.http.scaladsl.model.HttpEntity -import akka.http.scaladsl.unmarshalling.FromEntityUnmarshaller - -trait ScalatestUtils extends MarshallingTestUtils { - import org.scalatest.Matchers._ - def evaluateTo[T](value: T): Matcher[Future[T]] = - equal(value).matcher[T] compose (x ⇒ Await.result(x, 1.second)) - - def haveFailedWith(t: Throwable): Matcher[Future[_]] = - equal(t).matcher[Throwable] compose (x ⇒ Await.result(x.failed, 1.second)) - - def unmarshalToValue[T: FromEntityUnmarshaller](value: T)(implicit ec: ExecutionContext, mat: Materializer): Matcher[HttpEntity] = - equal(value).matcher[T] compose (unmarshalValue(_)) - - def unmarshalTo[T: FromEntityUnmarshaller](value: Try[T])(implicit ec: ExecutionContext, mat: Materializer): Matcher[HttpEntity] = - equal(value).matcher[Try[T]] compose (unmarshal(_)) -} - -trait ScalatestRouteTest extends RouteTest with TestFrameworkInterface.Scalatest with ScalatestUtils { this: Suite ⇒ } \ No newline at end of file diff --git a/akka-http-testkit/src/main/scala/akka/http/scaladsl/testkit/TestFrameworkInterface.scala b/akka-http-testkit/src/main/scala/akka/http/scaladsl/testkit/TestFrameworkInterface.scala deleted file mode 100644 index 6142bd62c2..0000000000 --- a/akka-http-testkit/src/main/scala/akka/http/scaladsl/testkit/TestFrameworkInterface.scala +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.testkit - -import org.scalatest.exceptions.TestFailedException -import org.scalatest.{ BeforeAndAfterAll, Suite } - -//# source-quote -trait TestFrameworkInterface { - - def cleanUp() - - def failTest(msg: String): Nothing -} -//# - -object TestFrameworkInterface { - - trait Scalatest extends TestFrameworkInterface with BeforeAndAfterAll { - this: Suite ⇒ - - def failTest(msg: String) = throw new TestFailedException(msg, 11) - - abstract override protected def afterAll(): Unit = { - cleanUp() - super.afterAll() - } - } - -} diff --git a/akka-http-testkit/src/main/scala/akka/http/scaladsl/testkit/WSProbe.scala b/akka-http-testkit/src/main/scala/akka/http/scaladsl/testkit/WSProbe.scala deleted file mode 100644 index 8cf1d97973..0000000000 --- a/akka-http-testkit/src/main/scala/akka/http/scaladsl/testkit/WSProbe.scala +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.testkit - -import akka.NotUsed - -import scala.concurrent.duration._ - -import akka.util.ByteString - -import akka.actor.ActorSystem - -import akka.stream.Materializer -import akka.stream.scaladsl.{ Keep, Source, Sink, Flow } -import akka.stream.testkit.{ TestPublisher, TestSubscriber } - -import akka.http.impl.util._ -import akka.http.scaladsl.model.ws.{ BinaryMessage, TextMessage, Message } - -/** - * A WSProbe is a probe that implements a `Flow[Message, Message, Unit]` for testing - * websocket code. - * - * Requesting elements is handled automatically. - */ -trait WSProbe { - def flow: Flow[Message, Message, NotUsed] - - /** - * Send the given messages out of the flow. - */ - def sendMessage(message: Message): Unit - - /** - * Send a text message containing the given string out of the flow. - */ - def sendMessage(text: String): Unit - - /** - * Send a binary message containing the given bytes out of the flow. - */ - def sendMessage(bytes: ByteString): Unit - - /** - * Complete the output side of the flow. - */ - def sendCompletion(): Unit - - /** - * Expect a message on the input side of the flow. - */ - def expectMessage(): Message - - /** - * Expect a text message on the input side of the flow and compares its payload with the given one. - * If the received message is streamed its contents are collected and then asserted against the given - * String. - */ - def expectMessage(text: String): Unit - - /** - * Expect a binary message on the input side of the flow and compares its payload with the given one. - * If the received message is streamed its contents are collected and then asserted against the given - * ByteString. - */ - def expectMessage(bytes: ByteString): Unit - - /** - * Expect no message on the input side of the flow. - */ - def expectNoMessage(): Unit - - /** - * Expect no message on the input side of the flow for the given maximum duration. - */ - def expectNoMessage(max: FiniteDuration): Unit - - /** - * Expect completion on the input side of the flow. - */ - def expectCompletion(): Unit - - /** - * The underlying probe for the ingoing side of this probe. Can be used if the methods - * on WSProbe don't allow fine enough control over the message flow. - */ - def inProbe: TestSubscriber.Probe[Message] - - /** - * The underlying probe for the outgoing side of this probe. Can be used if the methods - * on WSProbe don't allow fine enough control over the message flow. - */ - def outProbe: TestPublisher.Probe[Message] -} - -object WSProbe { - /** - * Creates a WSProbe to use in tests against websocket handlers. - * - * @param maxChunks The maximum number of chunks to collect for streamed messages. - * @param maxChunkCollectionMills The maximum time in milliseconds to collect chunks for streamed messages. - */ - def apply(maxChunks: Int = 1000, maxChunkCollectionMills: Long = 5000)(implicit system: ActorSystem, materializer: Materializer): WSProbe = - new WSProbe { - val subscriber = TestSubscriber.probe[Message]() - val publisher = TestPublisher.probe[Message]() - - def flow: Flow[Message, Message, NotUsed] = Flow.fromSinkAndSourceMat(Sink.fromSubscriber(subscriber), Source.fromPublisher(publisher))(Keep.none) - - def sendMessage(message: Message): Unit = publisher.sendNext(message) - def sendMessage(text: String): Unit = sendMessage(TextMessage(text)) - def sendMessage(bytes: ByteString): Unit = sendMessage(BinaryMessage(bytes)) - def sendCompletion(): Unit = publisher.sendComplete() - - def expectMessage(): Message = subscriber.requestNext() - def expectMessage(text: String): Unit = expectMessage() match { - case t: TextMessage ⇒ - val collectedMessage = collect(t.textStream)(_ + _) - assert(collectedMessage == text, s"""Expected TextMessage("$text") but got TextMessage("$collectedMessage")""") - case _ ⇒ throw new AssertionError(s"""Expected TextMessage("$text") but got BinaryMessage""") - } - def expectMessage(bytes: ByteString): Unit = expectMessage() match { - case t: BinaryMessage ⇒ - val collectedMessage = collect(t.dataStream)(_ ++ _) - assert(collectedMessage == bytes, s"""Expected BinaryMessage("$bytes") but got BinaryMessage("$collectedMessage")""") - case _ ⇒ throw new AssertionError(s"""Expected BinaryMessage("$bytes") but got TextMessage""") - } - - def expectNoMessage(): Unit = subscriber.expectNoMsg() - def expectNoMessage(max: FiniteDuration): Unit = subscriber.expectNoMsg(max) - - def expectCompletion(): Unit = subscriber.expectComplete() - - def inProbe: TestSubscriber.Probe[Message] = subscriber - def outProbe: TestPublisher.Probe[Message] = publisher - - private def collect[T](stream: Source[T, Any])(reduce: (T, T) ⇒ T): T = - stream.grouped(maxChunks) - .runWith(Sink.head) - .awaitResult(maxChunkCollectionMills.millis) - .reduce(reduce) - } -} diff --git a/akka-http-testkit/src/main/scala/akka/http/scaladsl/testkit/WSTestRequestBuilding.scala b/akka-http-testkit/src/main/scala/akka/http/scaladsl/testkit/WSTestRequestBuilding.scala deleted file mode 100644 index edf3ad041b..0000000000 --- a/akka-http-testkit/src/main/scala/akka/http/scaladsl/testkit/WSTestRequestBuilding.scala +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.testkit - -import akka.http.impl.engine.ws.InternalCustomHeader -import akka.http.scaladsl.model.headers.{ UpgradeProtocol, Upgrade, `Sec-WebSocket-Protocol` } -import akka.http.scaladsl.model.{ StatusCodes, HttpResponse, HttpRequest, Uri } -import akka.http.scaladsl.model.ws.{ UpgradeToWebSocket, Message } -import scala.collection.immutable -import akka.stream.{ Materializer, Graph, FlowShape } -import akka.stream.scaladsl.Flow - -trait WSTestRequestBuilding { - def WS(uri: Uri, clientSideHandler: Flow[Message, Message, Any], subprotocols: Seq[String] = Nil)(implicit materializer: Materializer): HttpRequest = - HttpRequest(uri = uri) - .addHeader(new InternalCustomHeader("UpgradeToWebSocketTestHeader") with UpgradeToWebSocket { - def requestedProtocols: immutable.Seq[String] = subprotocols.toList - - def handleMessages(handlerFlow: Graph[FlowShape[Message, Message], Any], subprotocol: Option[String]): HttpResponse = { - clientSideHandler.join(handlerFlow).run() - HttpResponse( - StatusCodes.SwitchingProtocols, - headers = - Upgrade(UpgradeProtocol("websocket") :: Nil) :: - subprotocol.map(p ⇒ `Sec-WebSocket-Protocol`(p :: Nil)).toList) - } - }) -} - -object WSTestRequestBuilding extends WSTestRequestBuilding diff --git a/akka-http-testkit/src/test/scala/akka/http/scaladsl/testkit/ScalatestRouteTestSpec.scala b/akka-http-testkit/src/test/scala/akka/http/scaladsl/testkit/ScalatestRouteTestSpec.scala deleted file mode 100644 index 2e8204b282..0000000000 --- a/akka-http-testkit/src/test/scala/akka/http/scaladsl/testkit/ScalatestRouteTestSpec.scala +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.testkit - -import scala.concurrent.duration._ -import org.scalatest.FreeSpec -import org.scalatest.Matchers -import akka.testkit.TestProbe -import akka.util.Timeout -import akka.pattern.ask -import akka.http.scaladsl.model.headers.RawHeader -import akka.http.scaladsl.server._ -import akka.http.scaladsl.model._ -import StatusCodes._ -import HttpMethods._ -import Directives._ - -class ScalatestRouteTestSpec extends FreeSpec with Matchers with ScalatestRouteTest { - - "The ScalatestRouteTest should support" - { - - "the most simple and direct route test" in { - Get() ~> complete(HttpResponse()) ~> { rr ⇒ rr.awaitResult; rr.response } shouldEqual HttpResponse() - } - - "a test using a directive and some checks" in { - val pinkHeader = RawHeader("Fancy", "pink") - Get() ~> addHeader(pinkHeader) ~> { - respondWithHeader(pinkHeader) { - complete("abc") - } - } ~> check { - status shouldEqual OK - responseEntity shouldEqual HttpEntity(ContentTypes.`text/plain(UTF-8)`, "abc") - header("Fancy") shouldEqual Some(pinkHeader) - } - } - - "proper rejection collection" in { - Post("/abc", "content") ~> { - (get | put) { - complete("naah") - } - } ~> check { - rejections shouldEqual List(MethodRejection(GET), MethodRejection(PUT)) - } - } - - "separation of route execution from checking" in { - val pinkHeader = RawHeader("Fancy", "pink") - - case object Command - val service = TestProbe() - val handler = TestProbe() - implicit def serviceRef = service.ref - implicit val askTimeout: Timeout = 1.second - - val result = - Get() ~> pinkHeader ~> { - respondWithHeader(pinkHeader) { - complete(handler.ref.ask(Command).mapTo[String]) - } - } ~> runRoute - - handler.expectMsg(Command) - handler.reply("abc") - - check { - status shouldEqual OK - responseEntity shouldEqual HttpEntity(ContentTypes.`text/plain(UTF-8)`, "abc") - header("Fancy") shouldEqual Some(pinkHeader) - }(result) - } - } - - // TODO: remove once RespondWithDirectives have been ported - def respondWithHeader(responseHeader: HttpHeader): Directive0 = - mapResponseHeaders(responseHeader +: _) -} diff --git a/akka-http-tests/build.sbt b/akka-http-tests/build.sbt deleted file mode 100644 index cf5b82307f..0000000000 --- a/akka-http-tests/build.sbt +++ /dev/null @@ -1,18 +0,0 @@ -import akka._ -import com.typesafe.sbt.SbtMultiJvm.MultiJvmKeys._ - -AkkaBuild.defaultSettings -AkkaBuild.dontPublishSettings -Formatting.formatSettings -Dependencies.httpTests - -// don't ignore Suites which is the default for the junit-interface -testOptions += Tests.Argument(TestFrameworks.JUnit, "--ignore-runners=") - -scalacOptions in Compile += "-language:_" -mainClass in run in Test := Some("akka.http.javadsl.SimpleServerApp") - -enablePlugins(ScaladocNoVerificationOfDiagrams) -enablePlugins(MultiNodeScalaTest) - -disablePlugins(MimaPlugin) diff --git a/akka-http-tests/lib/i have späces.jar b/akka-http-tests/lib/i have späces.jar deleted file mode 100644 index 3d009cbab1..0000000000 Binary files a/akka-http-tests/lib/i have späces.jar and /dev/null differ diff --git a/akka-http-tests/src/main/java/akka/http/javadsl/server/examples/petstore/Pet.java b/akka-http-tests/src/main/java/akka/http/javadsl/server/examples/petstore/Pet.java deleted file mode 100644 index c36617fa63..0000000000 --- a/akka-http-tests/src/main/java/akka/http/javadsl/server/examples/petstore/Pet.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server.examples.petstore; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -public class Pet { - private final int id; - private final String name; - - @JsonCreator - public Pet(@JsonProperty("id") int id, @JsonProperty("name") String name) { - this.id = id; - this.name = name; - } - - public int getId() { - return id; - } - - public String getName() { - return name; - } - -} diff --git a/akka-http-tests/src/main/java/akka/http/javadsl/server/examples/petstore/PetStoreController.java b/akka-http-tests/src/main/java/akka/http/javadsl/server/examples/petstore/PetStoreController.java deleted file mode 100644 index 4132901bf8..0000000000 --- a/akka-http-tests/src/main/java/akka/http/javadsl/server/examples/petstore/PetStoreController.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server.examples.petstore; - -import akka.http.javadsl.model.StatusCodes; -import static akka.http.javadsl.server.Directives.*; - -import akka.http.javadsl.server.Route; - -import java.util.Map; - -public class PetStoreController { - private Map dataStore; - - public PetStoreController(Map dataStore) { - this.dataStore = dataStore; - } - - public Route deletePet(int petId) { - dataStore.remove(petId); - return complete(StatusCodes.OK); - } -} diff --git a/akka-http-tests/src/main/java/akka/http/javadsl/server/examples/petstore/PetStoreExample.java b/akka-http-tests/src/main/java/akka/http/javadsl/server/examples/petstore/PetStoreExample.java deleted file mode 100644 index 05877a8f91..0000000000 --- a/akka-http-tests/src/main/java/akka/http/javadsl/server/examples/petstore/PetStoreExample.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server.examples.petstore; - -import akka.actor.ActorSystem; -import akka.http.javadsl.ConnectHttp; -import akka.http.javadsl.Http; -import akka.http.javadsl.marshallers.jackson.Jackson; -import akka.http.javadsl.model.StatusCodes; -import akka.http.javadsl.server.Route; -import akka.stream.ActorMaterializer; - -import java.io.IOException; -import java.util.Map; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Function; - -import static akka.http.javadsl.server.Directives.*; -import static akka.http.javadsl.unmarshalling.StringUnmarshallers.INTEGER; - -public class PetStoreExample { - - private static Route putPetHandler(Map pets, Pet thePet) { - pets.put(thePet.getId(), thePet); - return complete(StatusCodes.OK, thePet, Jackson.marshaller()); - } - - private static Route alternativeFuturePutPetHandler(Map pets, Pet thePet) { - pets.put(thePet.getId(), thePet); - CompletableFuture futurePet = CompletableFuture.supplyAsync(() -> thePet); - return completeOKWithFuture(futurePet, Jackson.marshaller()); - } - - public static Route appRoute(final Map pets) { - PetStoreController controller = new PetStoreController(pets); - - // Defined as Function in order to refere to [pets], but this could also be an ordinary method. - Function existingPet = petId -> { - Pet pet = pets.get(petId); - return (pet == null) ? reject() : complete(StatusCodes.OK, pet, Jackson.marshaller()); - }; - - // The directives here are statically imported, but you can also inherit from AllDirectives. - return - route( - path("", () -> - getFromResource("web/index.html") - ), - pathPrefix("pet", () -> - path(INTEGER, petId -> route( - // demonstrates different ways of handling requests: - - // 1. using a Function - get(() -> existingPet.apply(petId)), - - // 2. using a method - put(() -> - entity(Jackson.unmarshaller(Pet.class), thePet -> - putPetHandler(pets, thePet) - ) - ), - // 2.1. using a method, and internally handling a Future value - path("alternate", () -> - put(() -> - entity(Jackson.unmarshaller(Pet.class), thePet -> - putPetHandler(pets, thePet) - ) - ) - ), - - // 3. calling a method of a controller instance - delete(() -> controller.deletePet(petId)) - )) - ) - ); - } - - public static void main(String[] args) throws IOException { - Map pets = new ConcurrentHashMap<>(); - Pet dog = new Pet(0, "dog"); - Pet cat = new Pet(1, "cat"); - pets.put(0, dog); - pets.put(1, cat); - - final ActorSystem system = ActorSystem.create(); - final ActorMaterializer materializer = ActorMaterializer.create(system); - - final ConnectHttp host = ConnectHttp.toHost("127.0.0.1"); - - Http.get(system).bindAndHandle(appRoute(pets).flow(system, materializer), host, materializer); - - System.console().readLine("Type RETURN to exit..."); - system.terminate(); - } -} diff --git a/akka-http-tests/src/main/java/akka/http/javadsl/server/examples/simple/SimpleServerApp.java b/akka-http-tests/src/main/java/akka/http/javadsl/server/examples/simple/SimpleServerApp.java deleted file mode 100644 index f72846c71e..0000000000 --- a/akka-http-tests/src/main/java/akka/http/javadsl/server/examples/simple/SimpleServerApp.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server.examples.simple; - - -import akka.NotUsed; -import static akka.http.javadsl.server.PathMatchers.segment; - -import akka.actor.ActorSystem; -import akka.http.javadsl.ConnectHttp; -import akka.http.javadsl.ConnectionContext; -import akka.http.javadsl.Http; -import akka.http.javadsl.HttpsConnectionContext; -import akka.http.javadsl.model.HttpRequest; -import akka.http.javadsl.model.HttpResponse; -import akka.http.javadsl.server.*; -import akka.http.javadsl.unmarshalling.StringUnmarshallers; -import akka.stream.ActorMaterializer; -import akka.stream.javadsl.Flow; - -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManagerFactory; -import java.io.IOException; -import java.io.InputStream; -import java.security.*; -import java.security.cert.CertificateException; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; -import java.util.concurrent.Executor; -import java.util.function.BiFunction; -import java.util.function.Function; - -import static akka.http.javadsl.server.PathMatchers.integerSegment; -import static akka.http.javadsl.unmarshalling.Unmarshaller.entityToString; - -public class SimpleServerApp extends AllDirectives { // or import Directives.* - - - //#https-http-app - public Route multiply(int x, int y) { - int result = x * y; - return complete(String.format("%d * %d = %d", x, y, result)); - } - - public CompletionStage multiplyAsync(Executor ctx, int x, int y) { - return CompletableFuture.supplyAsync(() -> multiply(x, y), ctx); - } - - public Route createRoute() { - Route addHandler = parameter(StringUnmarshallers.INTEGER, "x", x -> - parameter(StringUnmarshallers.INTEGER, "y", y -> { - int result = x + y; - return complete(String.format("%d + %d = %d", x, y, result)); - }) - ); - - BiFunction subtractHandler = (x, y) -> { - int result = x - y; - return complete(String.format("%d - %d = %d", x, y, result)); - }; - - return - route( - // matches the empty path - pathSingleSlash(() -> - getFromResource("web/calculator.html") - ), - // matches paths like this: /add?x=42&y=23 - path("add", () -> addHandler), - path("subtract", () -> - parameter(StringUnmarshallers.INTEGER, "x", x -> - parameter(StringUnmarshallers.INTEGER, "y", y -> - subtractHandler.apply(x, y) - ) - ) - ), - // matches paths like this: /multiply/{x}/{y} - path(PathMatchers.segment("multiply").slash(integerSegment()).slash(integerSegment()), - this::multiply - ), - path(PathMatchers.segment("multiplyAsync").slash(integerSegment()).slash(integerSegment()), (x, y) -> - extractExecutionContext(ctx -> - onSuccess(() -> multiplyAsync(ctx, x, y), Function.identity()) - ) - ), - post(() -> - path("hello", () -> - entity(entityToString(), body -> - complete("Hello " + body + "!") - ) - ) - ) - ); - } - - // ** STARTING THE SERVER ** // - - public static void main(String[] args) throws IOException { - final ActorSystem system = ActorSystem.create("SimpleServerApp"); - final ActorMaterializer materializer = ActorMaterializer.create(system); - final Http http = Http.get(system); - - boolean useHttps = false; // pick value from anywhere - if ( useHttps ) { - HttpsConnectionContext https = useHttps(system); - http.setDefaultServerHttpContext(https); - } - - final SimpleServerApp app = new SimpleServerApp(); - final Flow flow = app.createRoute().flow(system, materializer); - - Http.get(system).bindAndHandle(flow, ConnectHttp.toHost("localhost", 8080), materializer); - - System.out.println("Type RETURN to exit"); - System.in.read(); - system.terminate(); - } - //# - - //#https-http-config - // ** CONFIGURING ADDITIONAL SETTINGS ** // - - public static HttpsConnectionContext useHttps(ActorSystem system) { - HttpsConnectionContext https = null; - try { - // initialise the keystore - // !!! never put passwords into code !!! - final char[] password = new char[]{'a', 'b', 'c', 'd', 'e', 'f'}; - - final KeyStore ks = KeyStore.getInstance("PKCS12"); - final InputStream keystore = SimpleServerApp.class.getClassLoader().getResourceAsStream("httpsDemoKeys/keys/server.p12"); - if (keystore == null) { - throw new RuntimeException("Keystore required!"); - } - ks.load(keystore, password); - - final KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509"); - keyManagerFactory.init(ks, password); - - final TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); - tmf.init(ks); - - final SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(keyManagerFactory.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom()); - - https = ConnectionContext.https(sslContext); - - } catch (NoSuchAlgorithmException | KeyManagementException e) { - system.log().error("Exception while configuring HTTPS.", e); - } catch (CertificateException | KeyStoreException | UnrecoverableKeyException | IOException e) { - system.log().error("Exception while ", e); - } - - return https; - } - //# -} diff --git a/akka-http-tests/src/main/java/akka/http/javadsl/server/examples/simple/SimpleServerHttpHttpsApp.java b/akka-http-tests/src/main/java/akka/http/javadsl/server/examples/simple/SimpleServerHttpHttpsApp.java deleted file mode 100644 index 22badc1d3b..0000000000 --- a/akka-http-tests/src/main/java/akka/http/javadsl/server/examples/simple/SimpleServerHttpHttpsApp.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server.examples.simple; - -import akka.NotUsed; -import akka.actor.ActorSystem; -import akka.http.javadsl.ConnectHttp; -import akka.http.javadsl.Http; -import akka.http.javadsl.HttpsConnectionContext; -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; - -public class SimpleServerHttpHttpsApp extends AllDirectives { // or import Directives.* - - public Route createRoute() { - return get( () -> complete("Hello World!") ); - } - - // ** STARTING THE SERVER ** // - - public static void main(String[] args) throws IOException { - final ActorSystem system = ActorSystem.create("SimpleServerHttpHttpsApp"); - final ActorMaterializer materializer = ActorMaterializer.create(system); - - final SimpleServerApp app = new SimpleServerApp(); - final Flow flow = app.createRoute().flow(system, materializer); - - //#both-https-and-http - final Http http = Http.get(system); - //Run HTTP server firstly - http.bindAndHandle(flow, ConnectHttp.toHost("localhost", 80), materializer); - - //get configured HTTPS context - HttpsConnectionContext https = SimpleServerApp.useHttps(system); - - // sets default context to HTTPS – all Http() bound servers for this ActorSystem will use HTTPS from now on - http.setDefaultServerHttpContext(https); - - //Then run HTTPS server - http.bindAndHandle(flow, ConnectHttp.toHost("localhost", 443), materializer); - //# - - System.out.println("Type RETURN to exit"); - System.in.read(); - system.terminate(); - } -} diff --git a/akka-http-tests/src/main/resources/httpsDemoKeys/keys/README.md b/akka-http-tests/src/main/resources/httpsDemoKeys/keys/README.md deleted file mode 100644 index 1353642d4e..0000000000 --- a/akka-http-tests/src/main/resources/httpsDemoKeys/keys/README.md +++ /dev/null @@ -1,57 +0,0 @@ -Keys for running Tls tests using the `ExampleHttpContexts` ----------------------------------------------------------- - -Instructions adapted from - - * http://datacenteroverlords.com/2012/03/01/creating-your-own-ssl-certificate-authority/ - * http://security.stackexchange.com/questions/9600/how-to-use-openssl-generated-keys-in-java - - -# Create a rootCA key: - -``` -openssl genrsa -out rootCA.key 2048 -``` - -# Self-sign CA: - -``` -openssl req -x509 -new -nodes -key rootCA.key -days 3560 -out rootCA.crt -``` - -# Create server key: - -``` -openssl genrsa -out server.key 2048 -``` - -# Create server CSR (you need to set the common name CN to "akka.example.org"): - -``` -openssl req -new -key server.key -out server.csr -``` - -# Create server certificate: - -``` -openssl x509 -req -in server.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out server.crt -days 3560 -``` - -# Create certificate chain: - -``` -cat server.crt rootCA.crt > chain.pem -``` - -# Convert certificate and key to pkcs12 (you need to provide a password manually, `ExampleHttpContexts` -# expects the password to be "abcdef"): - -``` -openssl pkcs12 -export -name servercrt -in chain.pem -inkey server.key -out server.p12 -``` - -# For investigating remote certs use: - -``` -openssl s_client -showcerts -connect 54.173.126.144:443 -``` diff --git a/akka-http-tests/src/main/resources/httpsDemoKeys/keys/chain.pem b/akka-http-tests/src/main/resources/httpsDemoKeys/keys/chain.pem deleted file mode 100644 index 766871eba2..0000000000 --- a/akka-http-tests/src/main/resources/httpsDemoKeys/keys/chain.pem +++ /dev/null @@ -1,40 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDITCCAgkCCQCo8H6OcPrArzANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB -VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 -cyBQdHkgTHRkMB4XDTE1MDcyMzA5NTEyMloXDTI1MDQyMTA5NTEyMlowYDELMAkG -A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0 -IFdpZGdpdHMgUHR5IEx0ZDEZMBcGA1UEAwwQYWtrYS5leGFtcGxlLm9yZzCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANMy/wgrSYVhVtu9OGbo2rSKauiz -5V56X4uCqtCHF9UeHtnVtFLCBMa+pimOS+UyUAT4mbBsxW22BhoNUBZ15KPxltyD -yEsqNCKwWGxL3r8AXQtze2MEpTl22Lvp/iCTXO1vbML/+9r3uqUjw/AAP9HwF9Wd -j/yOrs6q8WE4sfc48iOj6N60/h2pRfn2WNJmo9W9FLC53NznixfsG5oN6Jmb9RM+ -fMHYXLfL/Vt6NrgVX1uqHt9HvuoxfNKhhXE5VU8bNfFfzPYvIt4aZXGxO15vEqsq -OaZ7YJyKr1oFfJC8LmE5xPa3GHToCqmkdMXQK38mpslMQWlQLYnmkS5Qzv8CAwEA -ATANBgkqhkiG9w0BAQsFAAOCAQEAEPDd1gAF9q2LtoZqTdcwmeBjdbT7n0WDRSuI -BzQ/qKjvymwpFKQ0pZSPUyaw2qfRRiTQ/QTbqYep2mhvl5n+gW3ifTp83zgTGKH/ -3sDlX0HPSCBYCDy2gP/AOIgV/57ADMpEkTlz8yyLMH+pLDAoNFIPwy7blAkq+ULQ -y6TfEBmZXoemSaIh5tRnexCD+pTvL4MRrGlBEoxdejDnIAt4n6BxmF0b4hKg8uta -UvivA85lBKzWUoR/Vam5/SC8jtcyLt9RThRcNSj6zP6s5d+o+8PLznrSEadAtfD9 -0q+t4TYF81tClEEgGruVPNL4WIpDniOfw9AJgQNVJGfy5TKY1Q== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDXTCCAkWgAwIBAgIJANYwx08wP3STMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV -BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX -aWRnaXRzIFB0eSBMdGQwHhcNMTUwNzIzMDk0ODI2WhcNMjUwNDIxMDk0ODI2WjBF -MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 -ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEArk0K/Rn7uND2YGFBks5Sok1WvNdHQccPESEw2hNVF32ExAhbBXCrFaIl -Io0q4eYSbypeauEjDXB/NJXurEefL8ONXK62erJDKKQ0aTTYqsVifoNYA9ORWoGE -XhtAfOx4xvzr6vF1e3kz0PB/A4ftn0vvVygYnf/2E2bQZgaw8dXP5lIGasEzzigB -LX/qTEW/vBOL98Rxp6JvjwvYMbPSZGwNwSz+tI5W2psdE1Mga2Qnsv3j+STWlD9v -+JlgdN8r3PyR1sl3jC7gCj3AaOhv4RbAbqjwnZ9nrckx16PFiMtJiVRea7CQXN7g -191EVujQnlg1LOhiSMKwVsuoXr08ywIDAQABo1AwTjAdBgNVHQ4EFgQU2THI/ilU -M0xds3vZlV4CvhAZ1d8wHwYDVR0jBBgwFoAU2THI/ilUM0xds3vZlV4CvhAZ1d8w -DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAK9LO0HyIi0xbTISsc+A5 -LQyZowgRAGqsNNmni7NKDXauPLZrCfDVhvo/FPP1XSFShXo7ARvro9lul4AJlkNN -VgX0gbWtkiAx0uLqlbMsC6imj2L9boRse7mzI/Ymem5SNTn9GUnlMiZ74rca9UT4 -Dk9YytrT4FSpomiL6z8Xj604W3RuLSdEfpfcn3Jh2tFSZ9hyLwB7ATUTA/yuj1SU -G1gmoPMvlnPzNj2lIqyIdQxGdxt+L3mFO20CxBkeieWqQuNptpjwptliFjkZJJZP -wQlx9qLLvs/eFC2AUWj+hbsl37PuARR9hoeqbKRcUjwGtaXOqikrvX1qzPc2+ij9 -/w== ------END CERTIFICATE----- diff --git a/akka-http-tests/src/main/resources/httpsDemoKeys/keys/rootCA.crt b/akka-http-tests/src/main/resources/httpsDemoKeys/keys/rootCA.crt deleted file mode 100644 index 6ba9fb756c..0000000000 --- a/akka-http-tests/src/main/resources/httpsDemoKeys/keys/rootCA.crt +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDXTCCAkWgAwIBAgIJANYwx08wP3STMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV -BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX -aWRnaXRzIFB0eSBMdGQwHhcNMTUwNzIzMDk0ODI2WhcNMjUwNDIxMDk0ODI2WjBF -MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 -ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEArk0K/Rn7uND2YGFBks5Sok1WvNdHQccPESEw2hNVF32ExAhbBXCrFaIl -Io0q4eYSbypeauEjDXB/NJXurEefL8ONXK62erJDKKQ0aTTYqsVifoNYA9ORWoGE -XhtAfOx4xvzr6vF1e3kz0PB/A4ftn0vvVygYnf/2E2bQZgaw8dXP5lIGasEzzigB -LX/qTEW/vBOL98Rxp6JvjwvYMbPSZGwNwSz+tI5W2psdE1Mga2Qnsv3j+STWlD9v -+JlgdN8r3PyR1sl3jC7gCj3AaOhv4RbAbqjwnZ9nrckx16PFiMtJiVRea7CQXN7g -191EVujQnlg1LOhiSMKwVsuoXr08ywIDAQABo1AwTjAdBgNVHQ4EFgQU2THI/ilU -M0xds3vZlV4CvhAZ1d8wHwYDVR0jBBgwFoAU2THI/ilUM0xds3vZlV4CvhAZ1d8w -DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAK9LO0HyIi0xbTISsc+A5 -LQyZowgRAGqsNNmni7NKDXauPLZrCfDVhvo/FPP1XSFShXo7ARvro9lul4AJlkNN -VgX0gbWtkiAx0uLqlbMsC6imj2L9boRse7mzI/Ymem5SNTn9GUnlMiZ74rca9UT4 -Dk9YytrT4FSpomiL6z8Xj604W3RuLSdEfpfcn3Jh2tFSZ9hyLwB7ATUTA/yuj1SU -G1gmoPMvlnPzNj2lIqyIdQxGdxt+L3mFO20CxBkeieWqQuNptpjwptliFjkZJJZP -wQlx9qLLvs/eFC2AUWj+hbsl37PuARR9hoeqbKRcUjwGtaXOqikrvX1qzPc2+ij9 -/w== ------END CERTIFICATE----- diff --git a/akka-http-tests/src/main/resources/httpsDemoKeys/keys/rootCA.key b/akka-http-tests/src/main/resources/httpsDemoKeys/keys/rootCA.key deleted file mode 100644 index 119caf0dd1..0000000000 --- a/akka-http-tests/src/main/resources/httpsDemoKeys/keys/rootCA.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEArk0K/Rn7uND2YGFBks5Sok1WvNdHQccPESEw2hNVF32ExAhb -BXCrFaIlIo0q4eYSbypeauEjDXB/NJXurEefL8ONXK62erJDKKQ0aTTYqsVifoNY -A9ORWoGEXhtAfOx4xvzr6vF1e3kz0PB/A4ftn0vvVygYnf/2E2bQZgaw8dXP5lIG -asEzzigBLX/qTEW/vBOL98Rxp6JvjwvYMbPSZGwNwSz+tI5W2psdE1Mga2Qnsv3j -+STWlD9v+JlgdN8r3PyR1sl3jC7gCj3AaOhv4RbAbqjwnZ9nrckx16PFiMtJiVRe -a7CQXN7g191EVujQnlg1LOhiSMKwVsuoXr08ywIDAQABAoIBAQCSXAEpLMNRmq33 -mlMMqhF7VcPKyF5+Xl9Je/xgcjFWi0CLt5Ruyf/vJ3tVOwLSM3YxQHuN9cSQSXGX -P3rt0SpbWjJ+q/pwpvV7z/5uhUCWjS46m6GxfNsmC3GR8AJDo/F67fBQFTcYWlrn -TLrqxR4EUCgGoJWjPsZr3j6KHX5BYmzyTuJFBzxxipK42hnJQ7tMB8l6/5r4nRka -d6SGFpJDkyhO+Wl0sBXjxHu1E4g8asI061jEOhcROV1Dk4hp1CYhd8TBj//6FSBC -ttsIe2gxT0fk8bnNC78FuO0CUTCj4hFOWP7apr/NhLlxypu+4hj17NMhlptRvGxz -6pPlMVDJAoGBANPVTS5nkJpMyczA5vaHsyTF/nwunogwHVeVYsQQ3Bed28Ldp7gr -Dr4hgYFvGkEmlLvWOleHvGISuD3lHLd112LcPyLFMRrs8wX9vWTueZGYj5KDLS3C -i3GaYMqqYbuiFY1QYprF36zRQkLMKUiOomE2+baCasbhluAqqx32KEKvAoGBANKk -cG0X0svJ/TTQIE5nfDtKePDUA7wEPYGrQOO4vKKZUlytVhf+gEcYr575bPjkTl1h -5jrrhr4OWpFDmRyBpi7wB95Fe93Df+0o4KmiNtsioZsi/MA5Tga2rAZPBBuZ9+5l -alYl0fTo5PR3fOXJJoJ+w7+QI4N/9TGuBJoiEl6lAoGBAM8XapsBOIcApxB7TdCa -HXLH9eDlmqq9jxH+w022xdR4yU2acMtFnOYXz4oAWgRzeVihOOw1kN+4OVKZWBer -JuRJOZf+e+E84OFsjOnNkh/arBGqGFLyLGzlZdb79wv+i19ZxOxWojNLaKHxAjMi -7nBn1Hyux0CjbmK8lAl4iyeVAoGAT6r4BprTFFaiGN56yYykVPx2v4dAnlTwOmHe -GgLd/ZWFrB23CT4toDY6/iKST5Rx+ymy3SgFf06IfJaXi0uR4gDQyQV4sshlUvp5 -9k6u9rSjcLyL4dwKoclnSL+L6zCRsC3VSR3myf1n0vp6V6J7mTF+sa4/cFXuE8sg -XHd0gS0CgYAXNDcF+zYoSmbfdG7uM7qOPQwNRbr0pHvAg0NmtM9JOj8gZPoaeAy3 -3jEk9AMQrK0MNsRynAoMkhy+7WOU6TNLvyxXAKGZffOmABzSB9LEFgHkVPutl5/i -wL2pE1SoG2QwSqFYGv+rHgIpREJzDTNwbmSbl/Za50JrIZ3OFfTMDQ== ------END RSA PRIVATE KEY----- diff --git a/akka-http-tests/src/main/resources/httpsDemoKeys/keys/server.crt b/akka-http-tests/src/main/resources/httpsDemoKeys/keys/server.crt deleted file mode 100644 index 4395b589d5..0000000000 --- a/akka-http-tests/src/main/resources/httpsDemoKeys/keys/server.crt +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDITCCAgkCCQCo8H6OcPrArzANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB -VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 -cyBQdHkgTHRkMB4XDTE1MDcyMzA5NTEyMloXDTI1MDQyMTA5NTEyMlowYDELMAkG -A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0 -IFdpZGdpdHMgUHR5IEx0ZDEZMBcGA1UEAwwQYWtrYS5leGFtcGxlLm9yZzCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANMy/wgrSYVhVtu9OGbo2rSKauiz -5V56X4uCqtCHF9UeHtnVtFLCBMa+pimOS+UyUAT4mbBsxW22BhoNUBZ15KPxltyD -yEsqNCKwWGxL3r8AXQtze2MEpTl22Lvp/iCTXO1vbML/+9r3uqUjw/AAP9HwF9Wd -j/yOrs6q8WE4sfc48iOj6N60/h2pRfn2WNJmo9W9FLC53NznixfsG5oN6Jmb9RM+ -fMHYXLfL/Vt6NrgVX1uqHt9HvuoxfNKhhXE5VU8bNfFfzPYvIt4aZXGxO15vEqsq -OaZ7YJyKr1oFfJC8LmE5xPa3GHToCqmkdMXQK38mpslMQWlQLYnmkS5Qzv8CAwEA -ATANBgkqhkiG9w0BAQsFAAOCAQEAEPDd1gAF9q2LtoZqTdcwmeBjdbT7n0WDRSuI -BzQ/qKjvymwpFKQ0pZSPUyaw2qfRRiTQ/QTbqYep2mhvl5n+gW3ifTp83zgTGKH/ -3sDlX0HPSCBYCDy2gP/AOIgV/57ADMpEkTlz8yyLMH+pLDAoNFIPwy7blAkq+ULQ -y6TfEBmZXoemSaIh5tRnexCD+pTvL4MRrGlBEoxdejDnIAt4n6BxmF0b4hKg8uta -UvivA85lBKzWUoR/Vam5/SC8jtcyLt9RThRcNSj6zP6s5d+o+8PLznrSEadAtfD9 -0q+t4TYF81tClEEgGruVPNL4WIpDniOfw9AJgQNVJGfy5TKY1Q== ------END CERTIFICATE----- diff --git a/akka-http-tests/src/main/resources/httpsDemoKeys/keys/server.key b/akka-http-tests/src/main/resources/httpsDemoKeys/keys/server.key deleted file mode 100644 index 117cb40355..0000000000 --- a/akka-http-tests/src/main/resources/httpsDemoKeys/keys/server.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEogIBAAKCAQEA0zL/CCtJhWFW2704ZujatIpq6LPlXnpfi4Kq0IcX1R4e2dW0 -UsIExr6mKY5L5TJQBPiZsGzFbbYGGg1QFnXko/GW3IPISyo0IrBYbEvevwBdC3N7 -YwSlOXbYu+n+IJNc7W9swv/72ve6pSPD8AA/0fAX1Z2P/I6uzqrxYTix9zjyI6Po -3rT+HalF+fZY0maj1b0UsLnc3OeLF+wbmg3omZv1Ez58wdhct8v9W3o2uBVfW6oe -30e+6jF80qGFcTlVTxs18V/M9i8i3hplcbE7Xm8Sqyo5pntgnIqvWgV8kLwuYTnE -9rcYdOgKqaR0xdArfyamyUxBaVAtieaRLlDO/wIDAQABAoIBADfqTXkVNM7aWYut -yiv8xEJ+TxWy4ywjS/58psq0qYukANj9alNqyKbxvL5NzSwuKN9YDiCWe6KzSWRG -WAjKR7Fb+ewB+9pinxD8DT0GzT9WUkwA1A8AINpY68K8jaqEOVsnX+00prJvWfv0 -vyBggIUNgtHseD2ObRuMSIHL59oivxoBKmeRqFl26PCq+m6Dp1SsMwL8NE02rfUu -uVW0zSz0/A5ZK90l8St3N78Puw/qicvfrI4PrGi4kLKW9UKJKP5FzfPF7Kf9itVA -1VB3gd8Gs98vRnzHwZlwgjyAQkePzS/iEQid9uRA/Xys5ozcT1arYM00t3I7ZEUg -GJTKHBECgYEA+K/M6smzPrTAi0BEuI1NCb3zfxkjbBhC0cco9U4VIuhYVU+7Ukre -zi5yI+BQR8MPbftSeeosXV6eQaq04pKCrHWF+ql+3Io9Hojghd/EnNCOtGxjTGmI -Px8G7byeIr4+QyP+JSEdsVBfIEEQ9BJ8Up84RibsMfWcKe6ntzAMEmkCgYEA2Wj6 -DqPisPp4WwGi8bSvSRZsF3h3xu0saml+ug28j+b3kOa99Uz49kCi99sacJArYOWv -Dn+DPl2K2/lwYO0bfyXwWaLp8pd/MAmwhKZ2+qvoUnkZJFRU3yrUoPp7CURZSbcG -aD7IKotFH7wutqj8pZ50y8VGqKVACenhRSAH2ScCgYAuX7IJslUfg1tIXFK0S30r -LOXENK7bUGbdcZMcs1PTr5oRRo362YVU02prcD/oMeKlsrD9lQJy4tsGCcwzV/jQ -KhYy2PqUK58cG5AqxsCGMYn68R9PN3q1spZ7LKocdndr08FnsRY1Y3Rpslhz+yJ9 -0b0Pr+BprJBTbXKPAYGuyQKBgAJFu59djSgGZi2lVburBM4Bwv13z+CvZ/Bwy9dL -/3WNl3bXQpMGy+9e+5UVoDAfAaUQoYTIRmnndmUYNVl+APSSQ/Hb5xAXD0hEQakR -SFsUYuhBxcaAbyap/vDzzUdqhHhlxlZemZ8AN6e+Qsq793APuO7MUBHBMGsqG6Wq -UQqvAoGAINEINXhFXp2qVRDBUY57rRtpjQHajeNTMChgWTg30owfVNBY4evjRj8f -9XDuUkTumYcDcnOKmX3L6n9rg4noHlfNvxmn9pmG9vP0mG0MEOOxSxXFHVIuBw10 -wdTb0WE/i3FhyufdaRHLGhPAMQjaCeFSV3sMxMHuNePvCxnKD3E= ------END RSA PRIVATE KEY----- diff --git a/akka-http-tests/src/main/resources/httpsDemoKeys/keys/server.p12 b/akka-http-tests/src/main/resources/httpsDemoKeys/keys/server.p12 deleted file mode 100644 index d72cc9f3d4..0000000000 Binary files a/akka-http-tests/src/main/resources/httpsDemoKeys/keys/server.p12 and /dev/null differ diff --git a/akka-http-tests/src/main/resources/web/calculator.html b/akka-http-tests/src/main/resources/web/calculator.html deleted file mode 100644 index a32b054287..0000000000 --- a/akka-http-tests/src/main/resources/web/calculator.html +++ /dev/null @@ -1,23 +0,0 @@ - - -

Calculator

- -

Add

-
- - - -
- -

Subtract

-
- - - -
- -

Multiply

-/multiply/42/23 - - - \ No newline at end of file diff --git a/akka-http-tests/src/multi-jvm/scala/akka/http/AkkaHttpServerLatencyMultiNodeSpec.scala b/akka-http-tests/src/multi-jvm/scala/akka/http/AkkaHttpServerLatencyMultiNodeSpec.scala deleted file mode 100644 index 11cf3ec5a2..0000000000 --- a/akka-http-tests/src/multi-jvm/scala/akka/http/AkkaHttpServerLatencyMultiNodeSpec.scala +++ /dev/null @@ -1,391 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http - -import java.io.{ BufferedWriter, FileWriter } -import java.util.concurrent.TimeUnit - -import akka.NotUsed -import akka.actor.{ Actor, ActorIdentity, ActorRef, Identify, Props } -import akka.http.scaladsl.Http.ServerBinding -import akka.http.scaladsl.model.{ ContentTypes, HttpEntity } -import akka.http.scaladsl.server.{ Directives, Route } -import akka.http.scaladsl.{ Http, TestUtils } -import akka.remote.testkit.{ MultiNodeConfig, MultiNodeSpec } -import akka.stream.ActorMaterializer -import akka.stream.scaladsl.Source -import akka.testkit.{ ImplicitSender, LongRunningTest } -import akka.util.{ ByteString, Timeout } -import com.typesafe.config.ConfigFactory -import org.scalatest.concurrent.ScalaFutures -import org.scalatest.exceptions.TestPendingException - -import scala.annotation.tailrec -import scala.concurrent.duration._ -import scala.concurrent.{ Await, Promise } -import scala.util.Try - -object AkkaHttpServerLatencyMultiNodeSpec extends MultiNodeConfig { - - commonConfig(ConfigFactory.parseString( - """ - akka { - actor.default-mailbox.mailbox-type = "akka.dispatch.UnboundedMailbox" - actor.provider = remote - stream.materializer.debug.fuzzing-mode = off - - testconductor.barrier-timeout = 30m - - test.AkkaHttpServerLatencySpec { - enable = off - - rate = 10000 - duration = 30s - - totalRequestsFactor = 1.0 - } - } - """)) - - val server = role("server") - val loadGenerator = role("loadGenerator") - - private var _ifWrk2Available: Option[Boolean] = None - final def ifWrk2Available(test: ⇒ Unit): Unit = - if (isWrk2Available) test else throw new TestPendingException() - final def isWrk2Available: Boolean = - _ifWrk2Available getOrElse { - import scala.sys.process._ - val wrkExitCode = Try("""wrk""".!).getOrElse(-1) - - _ifWrk2Available = Some(wrkExitCode == 1) // app found, help displayed - isWrk2Available - } - - private var _abAvailable: Option[Boolean] = None - final def ifAbAvailable(test: ⇒ Unit): Unit = - if (isAbAvailable) test else throw new TestPendingException() - - final def isAbAvailable: Boolean = - _abAvailable getOrElse { - import scala.sys.process._ - val abExitCode = Try("""ab -h""".!).getOrElse(-1) - _abAvailable = Some(abExitCode == 22) // app found, help displayed (22 return code is when -h runs in ab, weird but true) - isAbAvailable - } - - final case class LoadGenCommand(cmd: String) - final case class LoadGenResults(results: String) { - def lines = results.split("\n") - } - final case class SetServerPort(port: Int) - class HttpLoadGeneratorActor(serverPort: Promise[Int]) extends Actor { - override def receive: Receive = { - case SetServerPort(port) ⇒ - serverPort.success(port) - context become ready(port) - case other ⇒ - throw new RuntimeException("No server port known! Initialize with SetServerPort() first! Got: " + other) - } - - import scala.sys.process._ - def ready(port: Int): Receive = { - case LoadGenCommand(cmd) if cmd startsWith "wrk" ⇒ - val res = - if (isWrk2Available) cmd.!! // blocking. DON'T DO THIS AT HOME, KIDS! - else "=== WRK NOT AVAILABLE ===" - sender() ! LoadGenResults(res) - - case LoadGenCommand(cmd) if cmd startsWith "ab" ⇒ - val res = - if (isAbAvailable) cmd.!! // blocking. DON'T DO THIS AT HOME, KIDS! - else "=== AB NOT AVAILABLE ===" - sender() ! LoadGenResults(res) - } - } -} - -class AkkaHttpServerLatencyMultiNodeSpecMultiJvmNode1 extends AkkaHttpServerLatencyMultiNodeSpec -class AkkaHttpServerLatencyMultiNodeSpecMultiJvmNode2 extends AkkaHttpServerLatencyMultiNodeSpec - -class AkkaHttpServerLatencyMultiNodeSpec extends MultiNodeSpec(AkkaHttpServerLatencyMultiNodeSpec) with STMultiNodeSpec - with ScalaFutures with ImplicitSender { - - import AkkaHttpServerLatencyMultiNodeSpec._ - - override implicit def patienceConfig: PatienceConfig = PatienceConfig(10.seconds, interval = 300.millis) - - def initialParticipants = 2 - - val MediumByteString = ByteString(Vector.fill(1024)(0.toByte): _*) - - val array_10x: Array[Byte] = Array(Vector.fill(10)(MediumByteString).flatten: _*) - val array_100x: Array[Byte] = Array(Vector.fill(100)(MediumByteString).flatten: _*) - val source_10x: Source[ByteString, NotUsed] = Source.repeat(MediumByteString).take(10) - val source_100x: Source[ByteString, NotUsed] = Source.repeat(MediumByteString).take(100) - val tenXResponseLength = array_10x.length - val hundredXResponseLength = array_100x.length - - // format: OFF - val routes: Route = { - import Directives._ - - path("ping") { - complete("PONG!") - } ~ - path("long-response-array" / IntNumber) { n => - if (n == 10) complete(HttpEntity(ContentTypes.`text/plain(UTF-8)`, array_10x)) - else if (n == 100) complete(HttpEntity(ContentTypes.`text/plain(UTF-8)`, array_10x)) - else throw new RuntimeException(s"Not implemented for ${n}") - } ~ - path("long-response-stream" / IntNumber) { n => - if (n == 10) complete(HttpEntity(ContentTypes.`text/plain(UTF-8)`, source_100x)) - else if (n == 100) complete(HttpEntity(ContentTypes.`text/plain(UTF-8)`, source_100x)) - else throw new RuntimeException(s"Not implemented for ${n}") - } - } - // format: ON - - val enableSpec = system.settings.config.getBoolean("akka.test.AkkaHttpServerLatencySpec.enable") - val totalRequestsFactor = system.settings.config.getDouble("akka.test.AkkaHttpServerLatencySpec.totalRequestsFactor") - val requests = Math.round(10000 * totalRequestsFactor) - val rate = system.settings.config.getInt("akka.test.AkkaHttpServerLatencySpec.rate") - val testDuration = system.settings.config.getDuration("akka.test.AkkaHttpServerLatencySpec.duration", TimeUnit.SECONDS) - val connections: Long = 10 - - override def binding = _binding - var _binding: Option[ServerBinding] = None - - val serverPortPromise: Promise[Int] = Promise() - def serverPort: Int = serverPortPromise.future.futureValue - def serverHost: String = node(server).address.host.get - - // --- urls - def url_ping = s"http://$serverHost:$serverPort/ping" - def url_longResponseStream(int: Int) = s"http://$serverHost:$serverPort/long-response-stream/$int" - def url_longResponseArray(int: Int) = s"http://$serverHost:$serverPort/long-response-array/$int" - // --- - - if (enableSpec) { - "Akka HTTP" must { - implicit val dispatcher = system.dispatcher - implicit val mat = ActorMaterializer() - - "start Akka HTTP" taggedAs LongRunningTest in { - enterBarrier("startup") - - runOn(loadGenerator) { - system.actorOf(Props(classOf[HttpLoadGeneratorActor], serverPortPromise), "load-gen") - } - enterBarrier("load-gen-ready") - - runOn(server) { - val (_, _, port) = TestUtils.temporaryServerHostnameAndPort() - info(s"Binding Akka HTTP Server to port: $port @ ${myself}") - val futureBinding = Http().bindAndHandle(routes, "0.0.0.0", port) - - _binding = Some(futureBinding.futureValue) - setServerPort(port) - } - - enterBarrier("http-server-running") - } - - "warmup" taggedAs LongRunningTest in ifWrk2Available { - val id = "warmup" - - val wrkOptions = s"""-d 30s -R $rate -c $connections -t $connections""" - runLoadTest(id)(s"""wrk $wrkOptions $url_ping""") - } - - "have good Latency on PONG response (keep-alive)" taggedAs LongRunningTest in ifWrk2Available { - val id = s"Latency_pong_R:${rate}_C:${connections}_p:" - - val wrkOptions = s"""-d ${testDuration}s -R $rate -c $connections -t $connections --u_latency""" - runLoadTest(id)(s"""wrk $wrkOptions $url_ping""") - } - - "have good Latency (ab) (short-lived connections)" taggedAs LongRunningTest in ifAbAvailable { - val id = s"Latency_AB-short-lived_pong_R:${rate}_C:${connections}_p:" - - val abOptions = s"-c $connections -n $requests" - runLoadTest(id)(s"""ab $abOptions $url_ping""") - } - - "have good Latency (ab) (long-lived connections)" taggedAs LongRunningTest in ifAbAvailable { - val id = s"Latency_AB_pong_long-lived_R:${rate}_C:${connections}_p:" - - val abOptions = s"-c $connections -n $requests -k" - runLoadTest(id)(s"""ab $abOptions $url_ping""") - } - - List( - 10 → tenXResponseLength, - 100 → hundredXResponseLength - ) foreach { - case (n, lenght) ⇒ - s"have good Latency (streaming-response($lenght), keep-alive)" taggedAs LongRunningTest in { - val id = s"Latency_stream($lenght)_R:${rate}_C:${connections}_p:" - - val wrkOptions = s"""-d ${testDuration}s -R $rate -c $connections -t $connections --u_latency""" - runLoadTest(id)(s"""wrk $wrkOptions ${url_longResponseStream(n)}""") - } - s"have good Latency (array-response($lenght), keep-alive)" taggedAs LongRunningTest in { - val id = s"Latency_array($lenght)_R:${rate}_C:${connections}_p:" - - val wrkOptions = s"""-d ${testDuration}s -R $rate -c $connections -t $connections --u_latency""" - runLoadTest(id)(s"""wrk $wrkOptions ${url_longResponseArray(n)}""") - } - } - } - } else { - "Akka HTTP" must { - "enable these performance tests by running with -Dakka.test.AkkaHttpServerLatencySpec.enable=on" in pending - } - } - - def runLoadTest(id: String)(cmd: String) = { - runOn(loadGenerator) { - info(s"${id} => running: $cmd") - import akka.pattern.ask - implicit val timeout = Timeout(30.minutes) // we don't want to timeout here - - val res = (loadGeneratorActor ? LoadGenCommand(cmd)).mapTo[LoadGenResults] - val results = Await.result(res, timeout.duration) - - if (id contains "warmup") () - else if (cmd startsWith "wrk") printWrkPercentiles(id, results.lines) - else if (cmd startsWith "ab") printAbPercentiles(id, results.lines) - else throw new NotImplementedError(s"Unable to handle [$cmd] results!") - } - - enterBarrier(s"load-test-complete-id:${id}") - } - - def setServerPort(p: Int): Unit = { - serverPortPromise.success(p) - loadGeneratorActor ! SetServerPort(p) - } - - lazy val loadGeneratorActor: ActorRef = { - if (isNode(loadGenerator)) { - system.actorSelection("/user/load-gen") ! Identify(None) - expectMsgType[ActorIdentity].ref.get - } else { - system.actorSelection(node(loadGenerator) / "user" / "load-gen") ! Identify(None) - expectMsgType[ActorIdentity].ref.get - } - } - - private def renderResults(prefix: String, titles: Seq[String], values: Seq[String]): Unit = { - println("====:" + titles.reverse.map(it ⇒ "\"" + it + "\"").mkString(",") + "\n") - println("====:" + values.reverse.map(it ⇒ "\"" + it + "\"").mkString(",") + "\n") - } - - private def durationAsMs(d: String): Long = { - val dd = d.replace("us", "µs") // Scala Duration does not parse "us" - val ddd = if (dd endsWith "m") dd.replace("m", " minutes") else dd - Duration(ddd).toMillis - } - - private def printWrkPercentiles(prefix: String, lines: Array[String]): Unit = { - val percentilesToPrint = 8 - - var i = 0 - val linesWithIndex = lines.zipWithIndex - val correctedDistributionStartsHere = linesWithIndex.find(p ⇒ p._1 contains "Latency Distribution").map(_._2).get - - var titles = List.empty[String] - var metrics = List.empty[String] - i = correctedDistributionStartsHere + 1 // skip header - while (i < correctedDistributionStartsHere + 1 + percentilesToPrint) { - val line = lines(i).trim - val percentile = line.takeWhile(_ != '%') - - val title = prefix + percentile + "_corrected" - val duration = durationAsMs(line.drop(percentile.length + 1).trim) - - titles ::= title - metrics ::= duration.toString - println(title + "," + duration) - - i += 1 - } - renderResults(prefix + "_corrected", titles, metrics) - - val uncorrectedDistributionStartsHere = linesWithIndex.find(p ⇒ p._1 contains "Uncorrected Latency").map(_._2).get - - titles = List.empty - metrics = List.empty - i = uncorrectedDistributionStartsHere + 1 // skip header - while (i < uncorrectedDistributionStartsHere + 1 + percentilesToPrint) { - val line = lines(i).trim - val percentile = line.takeWhile(_ != '%') - - val title = prefix + percentile + "_uncorrected" - val duration = durationAsMs(line.drop(percentile.length + 1).trim) - - titles ::= title - metrics ::= duration.toString - println(title + "," + duration) - - i += 1 - } - renderResults(prefix + "_uncorrected", titles, metrics) - - titles = List.empty - metrics = List.empty - val rpsLineNumber = linesWithIndex.find(p ⇒ p._1 contains "Requests/sec:").map(_._2).get - - i = rpsLineNumber - val rps = lines(i).replace("Requests/sec:", "").trim - - val rpsTitle = prefix + "requests-per-second" - titles ::= rpsTitle - metrics ::= rps.toDouble.toInt.toString - println(rpsTitle + "," + rps) - renderResults(prefix + "_RPS", titles, metrics) - - def transferAsBytes(s: String): Long = - ConfigFactory.parseString(s"it=${s}").getMemorySize("it").toBytes - - titles = List.empty - metrics = List.empty - val transferLineNumber = linesWithIndex.find(p ⇒ p._1 contains "Transfer/sec:").map(_._2).get - i = transferLineNumber - - val tps = lines(i).replace("Transfer/sec:", "").trim - - val tpsTitle = prefix + "transfer-per-second" - titles ::= tpsTitle - metrics ::= transferAsBytes(tps).toString - println(tpsTitle + "," + tps) - renderResults(prefix + "_TPS", titles, metrics) - } - - private def printAbPercentiles(prefix: String, lines: Array[String]): Unit = { - val percentilesToPrint = 9 - - var i = 0 - val correctedDistributionStartsHere = lines.zipWithIndex.find(p ⇒ p._1 contains "Percentage of the requests").map(_._2).get - - var titles = List.empty[String] - var metrics = List.empty[String] - i = correctedDistributionStartsHere + 1 // skip header - while (i < correctedDistributionStartsHere + 1 + percentilesToPrint) { - val line = lines(i).trim - val percentile = line.takeWhile(_ != '%') - val title = prefix + percentile - val duration = durationAsMs(line.drop(percentile.length + 1).replace("(longest request)", "").trim + "ms") - - titles ::= title - metrics ::= duration.toString - println(title + "," + duration) - - i += 1 - } - renderResults(prefix, titles, metrics) - } - -} diff --git a/akka-http-tests/src/multi-jvm/scala/akka/http/STMultiNodeSpec.scala b/akka-http-tests/src/multi-jvm/scala/akka/http/STMultiNodeSpec.scala deleted file mode 100644 index 86d04fac6b..0000000000 --- a/akka-http-tests/src/multi-jvm/scala/akka/http/STMultiNodeSpec.scala +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http - -import akka.http.scaladsl.Http.ServerBinding -import akka.remote.testkit.MultiNodeSpecCallbacks -import org.scalatest.concurrent.ScalaFutures -import org.scalatest.{ BeforeAndAfterAll, Matchers, WordSpecLike } - -trait STMultiNodeSpec extends MultiNodeSpecCallbacks with WordSpecLike with Matchers with BeforeAndAfterAll - with ScalaFutures { - - def binding: Option[ServerBinding] - - override def beforeAll() = - multiNodeSpecBeforeAll() - - override def afterAll() = { - binding foreach { _.unbind().futureValue } - multiNodeSpecAfterAll() - } - -} diff --git a/akka-http-tests/src/test/java/AllJavaTests.java b/akka-http-tests/src/test/java/AllJavaTests.java deleted file mode 100644 index 0f5db0cae3..0000000000 --- a/akka-http-tests/src/test/java/AllJavaTests.java +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -import akka.http.javadsl.server.HandlerBindingTest; -import docs.http.javadsl.server.HandlerExampleDocTest; -import org.junit.runner.RunWith; -import org.junit.runners.Suite; - -@RunWith(Suite.class) -@Suite.SuiteClasses({ - HandlerBindingTest.class, - HandlerExampleDocTest.class -}) -public class AllJavaTests { -} diff --git a/akka-http-tests/src/test/java/akka/http/javadsl/HttpAPIsTest.java b/akka-http-tests/src/test/java/akka/http/javadsl/HttpAPIsTest.java deleted file mode 100644 index 27765b7c5f..0000000000 --- a/akka-http-tests/src/test/java/akka/http/javadsl/HttpAPIsTest.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl; - -import akka.event.LoggingAdapter; -import akka.http.javadsl.*; -import akka.http.javadsl.model.HttpRequest; -import akka.http.javadsl.model.HttpResponse; -import akka.http.javadsl.testkit.JUnitRouteTest; -import akka.http.scaladsl.settings.ConnectionPoolSettings; -import akka.japi.Function; -import akka.stream.javadsl.Flow; -import org.junit.Test; - -import javax.net.ssl.SSLContext; -import java.util.concurrent.CompletionStage; - -import static akka.http.javadsl.ConnectHttp.toHost; -import static akka.http.javadsl.ConnectHttp.toHostHttps; - -@SuppressWarnings("ConstantConditions") -public class HttpAPIsTest extends JUnitRouteTest { - - @Test - public void placeholderCompileTimeOnlyTest() { - // fails if there are no test cases - } - - @SuppressWarnings("unused") - public void compileOnly() throws Exception { - final Http http = Http.get(system()); - - final ConnectionContext connectionContext = ConnectionContext.https(SSLContext.getDefault()); - final HttpConnectionContext httpContext = ConnectionContext.noEncryption(); - final HttpsConnectionContext httpsContext = ConnectionContext.https(SSLContext.getDefault()); - - String host = ""; - int port = 9090; - ConnectionPoolSettings conSettings = null; - LoggingAdapter log = null; - - http.bind(toHost("127.0.0.1", 8080), materializer()); - http.bind(toHost("127.0.0.1", 8080), materializer()); - http.bind(toHostHttps("127.0.0.1", 8080), materializer()); - - final Flow handler = null; - http.bindAndHandle(handler, toHost("127.0.0.1", 8080), materializer()); - http.bindAndHandle(handler, toHost("127.0.0.1", 8080), materializer()); - http.bindAndHandle(handler, toHostHttps("127.0.0.1", 8080).withCustomHttpsContext(httpsContext), materializer()); - - final Function> handler1 = null; - http.bindAndHandleAsync(handler1, toHost("127.0.0.1", 8080), materializer()); - http.bindAndHandleAsync(handler1, toHostHttps("127.0.0.1", 8080), materializer()); - - final Function handler2 = null; - http.bindAndHandleSync(handler2, toHost("127.0.0.1", 8080), materializer()); - http.bindAndHandleSync(handler2, toHostHttps("127.0.0.1", 8080), materializer()); - - final HttpRequest handler3 = null; - http.singleRequest(handler3, materializer()); - http.singleRequest(handler3, httpsContext, materializer()); - http.singleRequest(handler3, httpsContext, conSettings, log, materializer()); - - http.outgoingConnection("akka.io"); - http.outgoingConnection("akka.io:8080"); - http.outgoingConnection("https://akka.io"); - http.outgoingConnection("https://akka.io:8081"); - - http.outgoingConnection(toHost("akka.io")); - http.outgoingConnection(toHost("akka.io", 8080)); - http.outgoingConnection(toHost("https://akka.io")); - http.outgoingConnection(toHostHttps("akka.io")); // default ssl context (ssl-config) - http.outgoingConnection(toHostHttps("ssh://akka.io")); // throws, we explicitly require https or "" - http.outgoingConnection(toHostHttps("akka.io", 8081).withCustomHttpsContext(httpsContext)); - http.outgoingConnection(toHostHttps("akka.io", 8081).withCustomHttpsContext(httpsContext).withDefaultHttpsContext()); - http.outgoingConnection(toHostHttps("akka.io", 8081).withCustomHttpsContext(httpsContext).withDefaultHttpsContext()); - - // in future we can add modify(context -> Context) to "keep ssl-config defaults, but tweak them in code) - - http.newHostConnectionPool("akka.io", materializer()); - http.newHostConnectionPool("https://akka.io", materializer()); - http.newHostConnectionPool("https://akka.io:8080", materializer()); - http.newHostConnectionPool(toHost("akka.io"), materializer()); - http.newHostConnectionPool(toHostHttps("ftp://akka.io"), materializer()); // throws, we explicitly require https or "" - http.newHostConnectionPool(toHostHttps("https://akka.io:2222"), materializer()); - http.newHostConnectionPool(toHostHttps("akka.io"), materializer()); - http.newHostConnectionPool(toHost(""), conSettings, log, materializer()); - - - http.cachedHostConnectionPool("akka.io", materializer()); - http.cachedHostConnectionPool("https://akka.io", materializer()); - http.cachedHostConnectionPool("https://akka.io:8080", materializer()); - http.cachedHostConnectionPool(toHost("akka.io"), materializer()); - http.cachedHostConnectionPool(toHostHttps("smtp://akka.io"), materializer()); // throws, we explicitly require https or "" - http.cachedHostConnectionPool(toHostHttps("https://akka.io:2222"), materializer()); - http.cachedHostConnectionPool(toHostHttps("akka.io"), materializer()); - http.cachedHostConnectionPool(toHost("akka.io"), conSettings, log, materializer()); - - http.superPool(materializer()); - http.superPool(conSettings, log, materializer()); - http.superPool(conSettings, httpsContext, log, materializer()); - - final ConnectWithHttps connect = toHostHttps("akka.io", 8081).withCustomHttpsContext(httpsContext).withDefaultHttpsContext(); - connect.effectiveHttpsConnectionContext(http.defaultClientHttpsContext()); // usage by us internally - } - - @SuppressWarnings("unused") - public void compileOnlyBinding() throws Exception { - final Http http = Http.get(system()); - final HttpsConnectionContext httpsConnectionContext = null; - - http.bind(toHost("127.0.0.1"), materializer()); // 80 - http.bind(toHost("127.0.0.1", 8080), materializer()); // 8080 - - http.bind(toHost("https://127.0.0.1"), materializer()); // HTTPS 443 - http.bind(toHost("https://127.0.0.1", 9090), materializer()); // HTTPS 9090 - - http.bind(toHostHttps("127.0.0.1"), materializer()); // HTTPS 443 - http.bind(toHostHttps("127.0.0.1").withCustomHttpsContext(httpsConnectionContext), materializer()); // custom HTTPS 443 - - http.bind(toHostHttps("http://127.0.0.1"), materializer()); // throws - } -} \ No newline at end of file diff --git a/akka-http-tests/src/test/java/akka/http/javadsl/server/CompleteTest.java b/akka-http-tests/src/test/java/akka/http/javadsl/server/CompleteTest.java deleted file mode 100644 index fb16b3cf64..0000000000 --- a/akka-http-tests/src/test/java/akka/http/javadsl/server/CompleteTest.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server; - -import static akka.http.javadsl.unmarshalling.StringUnmarshallers.INTEGER; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; - -import org.junit.Test; - -import akka.http.javadsl.marshallers.jackson.Jackson; -import akka.http.javadsl.model.HttpRequest; -import akka.http.javadsl.model.MediaTypes; -import akka.http.javadsl.testkit.JUnitRouteTest; - -public class CompleteTest extends JUnitRouteTest { - @Test - public void completeWithString() { - Route route = complete("Everything OK!"); - - HttpRequest request = HttpRequest.create(); - - runRoute(route, request) - .assertStatusCode(200) - .assertMediaType(MediaTypes.TEXT_PLAIN) - .assertEntity("Everything OK!"); - } - - @Test - public void completeAsJacksonJson() { - - @SuppressWarnings("unused") // The getters are used reflectively by Jackson - class Person { - public String getFirstName() { return "Peter"; } - public String getLastName() { return "Parker"; } - public int getAge() { return 138; } - } - Route route = completeOK(new Person(), Jackson.marshaller()); - - HttpRequest request = HttpRequest.create(); - - runRoute(route, request) - .assertStatusCode(200) - .assertMediaType("application/json") - .assertEntity("{\"age\":138,\"firstName\":\"Peter\",\"lastName\":\"Parker\"}"); - } - - private CompletionStage doSlowCalculation(int x, int y) { - return CompletableFuture.supplyAsync(() -> { - int result = x + y; - return String.format("%d + %d = %d",x, y, result); - }); - } - - @Test - public void completeWithFuture() { - Route route = - parameter(INTEGER, "x", x -> - parameter(INTEGER, "y", y -> - onSuccess(() -> doSlowCalculation(x, y), Directives::complete) - ) - ); - - runRoute(route, HttpRequest.GET("add?x=42&y=23")) - .assertStatusCode(200) - .assertEntity("42 + 23 = 65"); - } -} diff --git a/akka-http-tests/src/test/java/akka/http/javadsl/server/HandlerBindingTest.java b/akka-http-tests/src/test/java/akka/http/javadsl/server/HandlerBindingTest.java deleted file mode 100644 index d6dc7abeeb..0000000000 --- a/akka-http-tests/src/test/java/akka/http/javadsl/server/HandlerBindingTest.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server; - -import static akka.http.javadsl.unmarshalling.StringUnmarshallers.INTEGER; - -import org.junit.Test; - -import akka.http.javadsl.testkit.JUnitRouteTest; -import akka.http.javadsl.testkit.TestRouteResult; -import akka.http.scaladsl.model.HttpRequest; - -public class HandlerBindingTest extends JUnitRouteTest { - - @Test - public void testHandlerWithoutExtractions() { - Route route = complete("Ok"); - TestRouteResult response = runRoute(route, HttpRequest.GET("/")); - response.assertEntity("Ok"); - } - @Test - public void testHandler1() { - Route route = parameter("a", a -> complete("Ok " + a)); - TestRouteResult response = runRoute(route, HttpRequest.GET("?a=23")); - response.assertStatusCode(200); - response.assertEntity("Ok 23"); - } - @Test - public void testHandler2() { - Route route = parameter(INTEGER, "a", a -> parameter(INTEGER, "b", b -> complete("Sum: " + (a + b)))); - TestRouteResult response = runRoute(route, HttpRequest.GET("?a=23&b=42")); - response.assertStatusCode(200); - response.assertEntity("Sum: 65"); - } - - public Route sum(int a, int b, int c, int d) { - return complete("Sum: " + (a + b + c + d)); - } - @Test - public void testHandlerMethod() { - Route route = parameter(INTEGER, "a", a -> - parameter(INTEGER, "b", b -> - parameter(INTEGER, "c", c -> - parameter(INTEGER, "d", d -> sum(a,b,c,d))))); - TestRouteResult response = runRoute(route, HttpRequest.GET("?a=23&b=42&c=30&d=45")); - response.assertStatusCode(200); - response.assertEntity("Sum: 140"); - } -} diff --git a/akka-http-tests/src/test/java/akka/http/javadsl/server/JavaRouteTest.java b/akka-http-tests/src/test/java/akka/http/javadsl/server/JavaRouteTest.java deleted file mode 100644 index e447f90eb5..0000000000 --- a/akka-http-tests/src/test/java/akka/http/javadsl/server/JavaRouteTest.java +++ /dev/null @@ -1,338 +0,0 @@ -/* - * Copyright (C) 2009-2016 Typesafe Inc. - */ -package akka.http.javadsl.server; - -import static akka.http.javadsl.server.PathMatchers.*; - -import java.math.BigDecimal; -import java.util.Arrays; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; -import java.util.function.Function; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import akka.http.javadsl.marshalling.Marshaller; -import akka.http.javadsl.model.*; -import akka.http.javadsl.model.headers.Accept; -import akka.http.javadsl.model.headers.RawHeader; -import akka.http.javadsl.unmarshalling.StringUnmarshallers; -import akka.http.javadsl.unmarshalling.Unmarshaller; -import org.junit.Test; - -import akka.http.javadsl.testkit.JUnitRouteTest; -//FIXME discuss how to provide a javadsl.CustomHeader where render() is either pre-implemented or trivial to write in Java -import akka.http.scaladsl.model.headers.CustomHeader; -import akka.japi.pf.PFBuilder; -import akka.util.ByteString; - -public class JavaRouteTest extends JUnitRouteTest { - private final Route route = getRoute(); - private static final Unmarshaller BIG_DECIMAL_PARAM = - Unmarshaller.sync(BigDecimal::new); - - private final Unmarshaller BIG_DECIMAL_BODY = - Unmarshaller.entityToString().thenApply(BigDecimal::new); - - private final Unmarshaller UUID_FROM_JSON_BODY = - Unmarshaller.forMediaType(MediaTypes.APPLICATION_JSON, - Unmarshaller.entityToString()) - .thenApply(s -> { - // just a fake JSON parser, assuming it's {"id":"..."} - // A real implementation could easily invoke Jackson here instead. - Pattern regex = Pattern.compile("\"id\":\"(.+)\""); - Matcher matcher = regex.matcher(s); - matcher.find(); - return UUID.fromString(matcher.group(1)); - }); - - private final Unmarshaller UUID_FROM_XML_BODY = - Unmarshaller.forMediaTypes(Arrays.asList(MediaTypes.TEXT_XML, MediaTypes.APPLICATION_XML), - Unmarshaller.entityToString()) - .thenApply(s -> { - // just a fake XML parser, assuming it's ... - // A real implementation could easily invoke JAXB here instead. - Pattern regex = Pattern.compile("(.+)"); - Matcher matcher = regex.matcher(s); - matcher.find(); - return UUID.fromString(matcher.group(1)); - }); - - private final Unmarshaller UUID_FROM_BODY = - Unmarshaller.firstOf(UUID_FROM_JSON_BODY, UUID_FROM_XML_BODY); - - private final Marshaller UUID_TO_JSON = Marshaller.wrapEntity( - (UUID u) -> "{\"id\":\"" + u + "\"}", - Marshaller.stringToEntity(), - MediaTypes.APPLICATION_JSON - ); - - private Marshaller UUID_TO_XML(ContentType xmlType) { - return Marshaller.byteStringMarshaller(xmlType).compose( - (UUID u) -> ByteString.fromString("" + u + "")); - } - - private final Marshaller UUID_TO_ENTITY = - Marshaller.oneOf( - UUID_TO_JSON, - UUID_TO_XML(MediaTypes.APPLICATION_XML.toContentType(HttpCharsets.UTF_8)), - UUID_TO_XML(MediaTypes.TEXT_XML.toContentType(HttpCharsets.UTF_8)) - ); - - private static boolean isUUID(String s) { - try { - UUID.fromString(s); - return true; - } catch (IllegalArgumentException x) { - return false; - } - } - - private Route uuidHeaderValue(Function inner) { - return headerValueByName("UUID", value -> { - return isUUID(value) ? inner.apply(new UUIDHeader(UUID.fromString(value))) - : reject(Rejections.malformedHeader("UUID", "must be a valid UUID")); - }); - } - - private class UUIDHeader extends CustomHeader { - private final UUID value; - - public UUIDHeader(UUID value) { - this.value = value; - } - - @Override - public String name() { - return "UUID"; - } - - @Override - public String value() { - return value.toString(); - } - - public UUID uuid() { - return value; - } - - @Override - public boolean renderInRequests() { - return true; - } - - @Override - public boolean renderInResponses() { - return true; - } - } - - @Test - public void pathCanMatchUuid() { - runRoute(route, HttpRequest.GET("/documents/359e4920-a6a2-4614-9355-113165d600fb")) - .assertEntity("document 359e4920-a6a2-4614-9355-113165d600fb"); - } - - @Test - public void pathCanMatchElement() { - runRoute(route, HttpRequest.GET("/people/john")) - .assertEntity("person john"); - } - - @Test - public void paramIsExtracted() { - runRoute(route, HttpRequest.GET("/cookies?amount=5")) - .assertEntity("cookies 5"); - } - - @Test - public void requiredParamCausesRejectionWhenMissing() { - // The rejection on "amount" appears twice because we have two route alternatives both requiring it. - runRouteUnSealed(route, HttpRequest.GET("/cookies")) - .assertRejections(Rejections.missingQueryParam("amount"), Rejections.missingQueryParam("amount")); - } - - @Test - public void wrongParamTypeCausesNextRouteToBeEvaluated() { - runRoute(route, HttpRequest.GET("/cookies?amount=one")) - .assertEntity("cookies (string) one"); - } - - @Test - public void requiredParamCauses404OnSealedRoute() { - runRoute(route, HttpRequest.GET("/cookies")) - .assertStatusCode(StatusCodes.NOT_FOUND) - .assertEntity("Request is missing required query parameter 'amount'"); - } - - @Test - public void customParamTypeCanBeExtracted() { - runRoute(route, HttpRequest.GET("/cakes?amount=5")) - .assertEntity("cakes 5"); - } - - @Test - public void entityCanBeUnmarshalled() { - runRoute(route, HttpRequest.POST("/bigdecimal").withEntity("1234")) - .assertEntity("body 1234"); - } - - @Test - public void entityCanBeUnmarshalledWhenPickingJsonUnmarshaller() { - runRoute(route, HttpRequest - .PUT("/uuid") - .withEntity(ContentTypes.create(MediaTypes.APPLICATION_JSON), - "{\"id\":\"76b38659-1dec-4ee6-86d0-9ca787bf578c\"}")) - .assertEntity("uuid 76b38659-1dec-4ee6-86d0-9ca787bf578c"); - } - - @Test - public void entityCanBeUnmarshalledWhenPickingXmlUnmarshaller() { - runRoute(route, HttpRequest - .PUT("/uuid") - .withEntity( - ContentTypes.create(MediaTypes.APPLICATION_XML, HttpCharsets.UTF_8), - "76b38659-1dec-4ee6-86d0-9ca787bf578c")) - .assertEntity("uuid 76b38659-1dec-4ee6-86d0-9ca787bf578c"); - } - - @Test - public void entityCanBeMarshalledWhenJsonIsAccepted() { - runRoute(route, HttpRequest.GET("/uuid").addHeader(Accept.create(MediaRanges.create(MediaTypes.APPLICATION_JSON)))) - .assertEntity("{\"id\":\"80a05eee-652e-4458-9bee-19b69dbe1dee\"}") - .assertContentType(MediaTypes.APPLICATION_JSON.toContentType()); - } - - @Test - public void entityCanBeMarshalledWhenXmlIsAccepted() { - runRoute(route, HttpRequest.GET("/uuid").addHeader(Accept.create(MediaRanges.create(MediaTypes.TEXT_XML)))) - .assertEntity("80a05eee-652e-4458-9bee-19b69dbe1dee") - .assertContentType(MediaTypes.TEXT_XML.toContentType(HttpCharsets.UTF_8)); - } - - @Test - public void requestIsRejectedIfNoMarshallerFitsAcceptedType() { - runRoute(route, HttpRequest.GET("/uuid").addHeader(Accept.create(MediaRanges.create(MediaTypes.TEXT_PLAIN)))) - .assertStatusCode(StatusCodes.NOT_ACCEPTABLE); - } - - @Test - public void firstMarshallerIsPickedAndStatusCodeAppliedIfNoAcceptHeaderPresent() { - runRoute(route, HttpRequest.GET("/uuid")) - .assertContentType(MediaTypes.APPLICATION_JSON.toContentType()) - .assertStatusCode(StatusCodes.FOUND); - } - - @Test - public void exceptionHandlersAreAppliedEvenIfTheRouteThrowsInFuture() { - runRoute(route, HttpRequest.GET("/shouldnotfail")) - .assertEntity("no problem!"); - } - - @Test - public void routeWithRequiredHeaderFailsWhenHeaderIsAbsent() { - runRouteUnSealed(route, HttpRequest.GET("/requiredheader")) - .assertRejections(Rejections.missingHeader("UUID")); - } - - @Test - public void routeWithRequiredHeaderFailsWhenHeaderIsMalformed() { - runRouteUnSealed(route, HttpRequest.GET("/requiredheader").addHeader(RawHeader.create("UUID", "monkeys"))) - .assertRejections(Rejections.malformedHeader("UUID", "must be a valid UUID")); - } - - @Test - public void routeWithRequiredHeaderSucceedsWhenHeaderIsPresentAsRawHeader() { - runRoute(route, HttpRequest.GET("/requiredheader").addHeader(RawHeader.create("UUID", "98610fcb-7b19-4639-8dfa-08db8ac19320"))) - .assertEntity("has header: 98610fcb-7b19-4639-8dfa-08db8ac19320"); - } - - @Test - public void routeWithRequiredHeaderSucceedsWhenHeaderIsPresentAsCustomType() { - runRoute(route, HttpRequest.GET("/requiredheader").addHeader(new UUIDHeader(UUID.fromString("98610fcb-7b19-4639-8dfa-08db8ac19320")))) - .assertEntity("has header: 98610fcb-7b19-4639-8dfa-08db8ac19320"); - } - - private final ExceptionHandler xHandler = ExceptionHandler.of(new PFBuilder() - .match(IllegalArgumentException.class, x -> complete("no problem!")) - .build()); - - private CompletionStage throwExceptionInFuture() { - return CompletableFuture.supplyAsync(() -> { - throw new IllegalArgumentException("always failing"); - }); - } - - - public Route getRoute() { - return route( - path(segment("hello").slash("world"), () -> - complete("hello, world") - ), - path(segment("number").slash(integerSegment()), i -> - complete("you said: " + (i * i)) - ), - pathPrefix("documents", () -> - path(uuidSegment(), id -> - complete("document " + id) - ) - ), - pathPrefix("people", () -> - path(name -> - complete("person " + name) - ) - ), - path("notreally", () -> - reject(Rejections.missingFormField("always failing")) - ), - path("shouldnotfail", () -> - handleExceptions(xHandler, () -> - onSuccess(() -> throwExceptionInFuture(), value -> - complete("never reaches here") - ) - ) - ), - path("cookies", () -> - parameter(StringUnmarshallers.INTEGER, "amount", amount -> - complete("cookies " + amount) - ) - ), - path("cookies", () -> - parameter("amount", (String amount) -> - complete("cookies (string) " + amount) - ) - ), - path("custom_response", () -> - complete(HttpResponse.create().withStatus(StatusCodes.ACCEPTED)) - ), - path("bigdecimal", () -> - entity(BIG_DECIMAL_BODY, value -> - complete("body " + value) - ) - ), - path("uuid", () -> route( - put(() -> - entity(UUID_FROM_BODY, value -> - complete("uuid " + value) - ) - ), - get(() -> { - UUID id = UUID.fromString("80a05eee-652e-4458-9bee-19b69dbe1dee"); - return complete(StatusCodes.FOUND, id, UUID_TO_ENTITY); - }) - )), - path("cakes", () -> - parameter(BIG_DECIMAL_PARAM, "amount", amount -> - complete("cakes " + amount) - ) - ), - path("requiredheader", () -> - uuidHeaderValue(h -> - complete("has header: " + h.uuid()) - ) - ) - ); - } -} diff --git a/akka-http-tests/src/test/java/akka/http/javadsl/server/JavaTestServer.java b/akka-http-tests/src/test/java/akka/http/javadsl/server/JavaTestServer.java deleted file mode 100644 index 761a8e2ba9..0000000000 --- a/akka-http-tests/src/test/java/akka/http/javadsl/server/JavaTestServer.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.javadsl.server; - -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.common.EntityStreamingSupport; -import akka.http.javadsl.marshallers.jackson.Jackson; -import akka.http.javadsl.model.HttpRequest; -import akka.http.javadsl.model.HttpResponse; -import akka.http.javadsl.model.StatusCodes; -import akka.http.javadsl.unmarshalling.StringUnmarshallers; -import akka.http.javadsl.unmarshalling.Unmarshaller; -import akka.stream.ActorMaterializer; -import akka.stream.javadsl.Flow; -import akka.stream.javadsl.Source; -import akka.util.ByteString; -import scala.concurrent.duration.Duration; -import scala.runtime.BoxedUnit; - -import java.util.Optional; -import java.util.concurrent.*; -import java.util.function.Function; - -public class JavaTestServer extends AllDirectives { // or import static Directives.*; - - public Route createRoute() { - final Duration timeout = Duration.create(1, TimeUnit.SECONDS); - - final Route index = path("", () -> - withRequestTimeout(timeout, this::mkTimeoutResponse, () -> { - silentSleep(5000); // too long, but note that this will NOT activate withRequestTimeout, see below - return complete(index()); - }) - ); - - final Route requestTimeout = path("timeout", () -> - withRequestTimeout(timeout, this::mkTimeoutResponse, () -> { - // here timeout will work - return completeOKWithFutureString(neverEndingFuture(index())); - }) - ); - - final Function, Optional> handleAuth = (maybeCreds) -> { - if (maybeCreds.isPresent() && maybeCreds.get().verify("pa$$word")) // some secure hash + check - return Optional.of(maybeCreds.get().identifier()); - else return Optional.empty(); - }; - - final Route secure = path("secure", () -> - authenticateBasic("My basic secure site", handleAuth, (login) -> - complete(String.format("Hello, %s!", login)) - ) - ); - - final Route ping = path("ping", () -> - complete("PONG!") - ); - - final Route crash = path("crash", () -> - path("scala", () -> completeOKWithFutureString(akka.dispatch.Futures.failed(new Exception("Boom!")))).orElse( - path("java", () -> completeOKWithFutureString(CompletableFuture.supplyAsync(() -> { throw new RuntimeException("Boom!"); })))) - ); - - final Unmarshaller JavaTweets = Jackson.byteStringUnmarshaller(JavaTweet.class); - final Route tweets = path("tweets", () -> - get(() -> - parameter(StringUnmarshallers.INTEGER, "n", n -> { - final Source tws = Source.repeat(new JavaTweet("Hello World!")).take(n); - return completeOKWithSource(tws, Jackson.marshaller(), EntityStreamingSupport.json()); - }) - ).orElse( - post(() -> - extractMaterializer(mat -> - entityAsSourceOf(JavaTweets, null, sourceOfTweets -> { - final CompletionStage tweetsCount = sourceOfTweets.runFold(0, (acc, tweet) -> acc + 1, mat); - return onComplete(tweetsCount, c -> complete("Total number of tweets: " + c)); - }) - ) - )) - ); - - final Route inner = path("inner", () -> - getFromResourceDirectory("someDir") - ); - - - return index - .orElse(secure) - .orElse(ping) - .orElse(crash) - .orElse(inner) - .orElse(requestTimeout) - .orElse(tweets) - ; - } - - private void silentSleep(int millis) { - try { - Thread.sleep(millis); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - - private CompletableFuture neverEndingFuture(String futureContent) { - return new CompletableFuture<>().thenApply((string) -> futureContent); - } - - private HttpResponse mkTimeoutResponse(HttpRequest request) { - return HttpResponse.create() - .withStatus(StatusCodes.ENHANCE_YOUR_CALM) - .withEntity("Unable to serve response within time limit, please enhance your calm."); - } - - private String index() { - return " \n" + - " \n" + - "

Say hello to akka-http-core!

\n" + - "

Defined resources:

\n" + - "
    \n" + - "
  • /ping
  • \n" + - "
  • /secure Use any username and '<username>-password' as credentials
  • \n" + - "
  • /crash
  • \n" + - "
  • /timeout Demonstrates timeout
  • \n" + - "
\n" + - " \n" + - " \n"; - } - - public static void main(String[] args) throws InterruptedException { - final JavaTestServer server = new JavaTestServer(); - server.run(); - } - - private void run() throws InterruptedException { - final ActorSystem system = ActorSystem.create(); - final ActorMaterializer mat = ActorMaterializer.create(system); - - final Flow flow = createRoute().flow(system, mat); - final CompletionStage binding = - Http.get(system).bindAndHandle(flow, ConnectHttp.toHost("127.0.0.1", 8080), mat); - - System.console().readLine("Press [ENTER] to quit..."); - shutdown(binding); - } - - private CompletionStage shutdown(CompletionStage binding) { - return binding.thenAccept(b -> { - System.out.println(String.format("Unbinding from %s", b.localAddress())); - - final CompletionStage unbound = b.unbind(); - try { - unbound.toCompletableFuture().get(3, TimeUnit.SECONDS); // block... - } catch (TimeoutException | InterruptedException | ExecutionException e) { - throw new RuntimeException(e); - } - }); - } - - private static final class JavaTweet { - private String message; - - public JavaTweet(String message) { - this.message = message; - } - - public void setMessage(String message) { - this.message = message; - } - - public String getMessage() { - return message; - } - - } -} diff --git a/akka-http-tests/src/test/java/akka/http/javadsl/server/MarshallerTest.java b/akka-http-tests/src/test/java/akka/http/javadsl/server/MarshallerTest.java deleted file mode 100644 index cdfcbab480..0000000000 --- a/akka-http-tests/src/test/java/akka/http/javadsl/server/MarshallerTest.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server; - - -import java.util.function.Function; - -import akka.http.javadsl.marshalling.Marshaller; -import akka.http.javadsl.model.*; -import akka.http.javadsl.model.headers.*; -import akka.http.javadsl.unmarshalling.StringUnmarshallers; -import org.junit.Test; - -import akka.http.javadsl.testkit.JUnitRouteTest; -import akka.http.javadsl.testkit.TestRoute; -import akka.util.ByteString; - -public class MarshallerTest extends JUnitRouteTest { - - @Test - public void testCustomToStringMarshaller() { - final Marshaller numberAsNameMarshaller = - Marshaller.wrapEntity((Integer param) -> { - switch (param) { - case 0: - return "null"; - case 1: - return "eins"; - case 2: - return "zwei"; - case 3: - return "drei"; - case 4: - return "vier"; - case 5: - return "fünf"; - default: - return "wat?"; - } - }, Marshaller.stringToEntity(), MediaTypes.TEXT_X_SPEECH); - - - final Function nummerHandler = integer -> completeOK(integer, numberAsNameMarshaller); - - TestRoute route = - testRoute( - get(() -> - path("nummer", () -> - parameter(StringUnmarshallers.INTEGER, "n", nummerHandler) - ) - ) - ); - - route.run(HttpRequest.GET("/nummer?n=1")) - .assertStatusCode(StatusCodes.OK) - .assertMediaType(MediaTypes.TEXT_X_SPEECH) - .assertEntity("eins"); - - route.run(HttpRequest.GET("/nummer?n=6")) - .assertStatusCode(StatusCodes.OK) - .assertMediaType(MediaTypes.TEXT_X_SPEECH) - .assertEntity("wat?"); - - route.run(HttpRequest.GET("/nummer?n=5")) - .assertStatusCode(StatusCodes.OK) - .assertEntityBytes(ByteString.fromString("fünf", "utf8")); - - route.run( - HttpRequest.GET("/nummer?n=5") - .addHeader(AcceptCharset.create(HttpCharsets.ISO_8859_1.toRange()))) - .assertStatusCode(StatusCodes.OK) - .assertEntityBytes(ByteString.fromString("fünf", "ISO-8859-1")); - } - - @Test - public void testCustomToByteStringMarshaller() { - final Marshaller numberAsJsonListMarshaller = - Marshaller.wrapEntity((Integer param) -> { - switch (param) { - case 1: - return ByteString.fromString("[1]"); - case 5: - return ByteString.fromString("[1,2,3,4,5]"); - default: - return ByteString.fromString("[]"); - } - }, Marshaller.byteStringToEntity(), MediaTypes.APPLICATION_JSON); - - final Function nummerHandler = integer -> completeOK(integer, numberAsJsonListMarshaller); - - TestRoute route = - testRoute( - get(() -> - path("nummer", () -> - parameter(StringUnmarshallers.INTEGER, "n", nummerHandler) - ) - ) - ); - - route.run(HttpRequest.GET("/nummer?n=1")) - .assertStatusCode(StatusCodes.OK) - .assertMediaType(MediaTypes.APPLICATION_JSON) - .assertEntity("[1]"); - - route.run(HttpRequest.GET("/nummer?n=5")) - .assertStatusCode(StatusCodes.OK) - .assertEntity("[1,2,3,4,5]"); - - route.run( - HttpRequest.GET("/nummer?n=5").addHeader(Accept.create(MediaTypes.TEXT_PLAIN.toRange()))) - .assertStatusCode(StatusCodes.NOT_ACCEPTABLE); - } - - @Test - public void testCustomToEntityMarshaller() { - final Marshaller numberAsJsonListMarshaller = - Marshaller.withFixedContentType(MediaTypes.APPLICATION_JSON.toContentType(), (Integer param) -> { - switch (param) { - case 1: - return HttpEntities.create(MediaTypes.APPLICATION_JSON.toContentType(), "[1]"); - case 5: - return HttpEntities.create(MediaTypes.APPLICATION_JSON.toContentType(), "[1,2,3,4,5]"); - default: - return HttpEntities.create(MediaTypes.APPLICATION_JSON.toContentType(), "[]"); - } - }); - - final Function nummerHandler = integer -> completeOK(integer, numberAsJsonListMarshaller); - - TestRoute route = - testRoute( - get(() -> - path("nummer", () -> - parameter(StringUnmarshallers.INTEGER, "n", nummerHandler) - ) - ).seal(system(), materializer()) // needed to get the content negotiation, maybe - ); - - route.run(HttpRequest.GET("/nummer?n=1")) - .assertStatusCode(StatusCodes.OK) - .assertMediaType(MediaTypes.APPLICATION_JSON) - .assertEntity("[1]"); - - route.run(HttpRequest.GET("/nummer?n=5")) - .assertStatusCode(StatusCodes.OK) - .assertEntity("[1,2,3,4,5]"); - - route.run( - HttpRequest.GET("/nummer?n=5").addHeader(Accept.create(MediaTypes.TEXT_PLAIN.toRange()))) - .assertStatusCode(StatusCodes.NOT_ACCEPTABLE); - } - - @Test - public void testCustomToResponseMarshaller() { - final Marshaller numberAsJsonListMarshaller = - Marshaller.withFixedContentType(MediaTypes.APPLICATION_JSON.toContentType(), (Integer param) -> { - switch (param) { - case 1: - return HttpResponse.create().withEntity(MediaTypes.APPLICATION_JSON.toContentType(), "[1]"); - case 5: - return HttpResponse.create().withEntity(MediaTypes.APPLICATION_JSON.toContentType(), "[1,2,3,4,5]"); - default: - return HttpResponse.create().withStatus(StatusCodes.NOT_FOUND); - } - }); - - final Function nummerHandler = integer -> complete(integer, numberAsJsonListMarshaller); - - TestRoute route = - testRoute( - get(() -> - path("nummer", () -> - parameter(StringUnmarshallers.INTEGER, "n", nummerHandler) - ) - ) - ); - - route.run(HttpRequest.GET("/nummer?n=1")) - .assertStatusCode(StatusCodes.OK) - .assertMediaType(MediaTypes.APPLICATION_JSON) - .assertEntity("[1]"); - - route.run(HttpRequest.GET("/nummer?n=5")) - .assertStatusCode(StatusCodes.OK) - .assertEntity("[1,2,3,4,5]"); - - route.run(HttpRequest.GET("/nummer?n=6")) - .assertStatusCode(StatusCodes.NOT_FOUND); - - route.run(HttpRequest.GET("/nummer?n=5").addHeader(Accept.create(MediaTypes.TEXT_PLAIN.toRange()))) - .assertStatusCode(StatusCodes.NOT_ACCEPTABLE); - } -} diff --git a/akka-http-tests/src/test/java/akka/http/javadsl/server/UnmarshallerTest.java b/akka-http-tests/src/test/java/akka/http/javadsl/server/UnmarshallerTest.java deleted file mode 100644 index d7692cdee5..0000000000 --- a/akka-http-tests/src/test/java/akka/http/javadsl/server/UnmarshallerTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2009-2016 Typesafe Inc. - */ -package akka.http.javadsl.server; - -import akka.http.javadsl.model.*; -import akka.http.javadsl.testkit.JUnitRouteTest; -import akka.http.javadsl.unmarshalling.Unmarshaller; -import org.junit.Test; - -import java.util.Arrays; -import java.util.concurrent.CompletionStage; -import java.util.concurrent.TimeUnit; - -import static org.junit.Assert.assertEquals; - -public class UnmarshallerTest extends JUnitRouteTest { - - @Test - public void canChooseOneOfManyUnmarshallers() throws Exception { - Unmarshaller jsonUnmarshaller = - Unmarshaller.forMediaType(MediaTypes.APPLICATION_JSON, Unmarshaller.entityToString()).thenApply((str) -> "json"); - Unmarshaller xmlUnmarshaller = - Unmarshaller.forMediaType(MediaTypes.TEXT_XML, Unmarshaller.entityToString()).thenApply((str) -> "xml"); - - final Unmarshaller both = Unmarshaller.firstOf(jsonUnmarshaller, xmlUnmarshaller); - - { - CompletionStage resultStage = - both.unmarshall( - HttpEntities.create(ContentTypes.TEXT_XML_UTF8, ""), - system().dispatcher(), - materializer()); - - assertEquals("xml", resultStage.toCompletableFuture().get(3, TimeUnit.SECONDS)); - } - - - { - CompletionStage resultStage = - both.unmarshall( - HttpEntities.create(ContentTypes.APPLICATION_JSON, "{}"), - system().dispatcher(), - materializer()); - - assertEquals("json", resultStage.toCompletableFuture().get(3, TimeUnit.SECONDS)); - } - } - - @Test - public void oneMarshallerCanHaveMultipleMediaTypes() throws Exception { - Unmarshaller xmlUnmarshaller = - Unmarshaller.forMediaTypes( - Arrays.asList(MediaTypes.APPLICATION_XML, MediaTypes.TEXT_XML), - Unmarshaller.entityToString()).thenApply((str) -> "xml"); - - { - CompletionStage resultStage = - xmlUnmarshaller.unmarshall( - HttpEntities.create(ContentTypes.TEXT_XML_UTF8, ""), - system().dispatcher(), - materializer()); - - assertEquals("xml", resultStage.toCompletableFuture().get(3, TimeUnit.SECONDS)); - } - - { - CompletionStage resultStage = - xmlUnmarshaller.unmarshall( - HttpEntities.create(ContentTypes.create(MediaTypes.APPLICATION_XML, HttpCharsets.UTF_8), ""), - system().dispatcher(), - materializer()); - - assertEquals("xml", resultStage.toCompletableFuture().get(3, TimeUnit.SECONDS)); - } - } -} diff --git a/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/CodingDirectivesTest.java b/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/CodingDirectivesTest.java deleted file mode 100644 index dacee6cc78..0000000000 --- a/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/CodingDirectivesTest.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server.directives; - -import akka.http.javadsl.coding.Coder; -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.util.ByteString; - -import org.junit.*; - -import akka.http.javadsl.testkit.*; - -import java.util.Arrays; -import java.util.Collections; -import java.util.concurrent.TimeUnit; - -public class CodingDirectivesTest extends JUnitRouteTest { - - @Test - public void testAutomaticEncodingWhenNoEncodingRequested() throws Exception { - TestRoute route = - testRoute( - encodeResponse(() -> - complete("TestString") - ) - ); - - TestRouteResult response = route.run(HttpRequest.create()); - response - .assertStatusCode(200); - - Assert.assertEquals("TestString", response.entityBytes().utf8String()); - } - - @Test - public void testAutomaticEncodingWhenDeflateRequested() throws Exception { - TestRoute route = - testRoute( - encodeResponse(() -> - complete("tester") - ) - ); - - HttpRequest request = HttpRequest.create().addHeader(AcceptEncoding.create(HttpEncodings.DEFLATE)); - TestRouteResult response = route.run(request); - response - .assertStatusCode(200) - .assertHeaderExists(ContentEncoding.create(HttpEncodings.DEFLATE)); - - ByteString decompressed = - Coder.Deflate.decode(response.entityBytes(), materializer()).toCompletableFuture().get(3, TimeUnit.SECONDS); - Assert.assertEquals("tester", decompressed.utf8String()); - } - - @Test - public void testEncodingWhenDeflateRequestedAndGzipSupported() { - TestRoute route = - testRoute( - encodeResponseWith(Arrays.asList(Coder.Gzip), () -> - complete("tester") - ) - ); - - HttpRequest request = HttpRequest.create().addHeader(AcceptEncoding.create(HttpEncodings.DEFLATE)); - route.run(request) - .assertStatusCode(406) - .assertEntity("Resource representation is only available with these Content-Encodings:\ngzip"); - } - - @Test - public void testAutomaticDecoding() { - TestRoute route = - testRoute( - decodeRequest(() -> - extractEntity(entity -> complete(entity)) - ) - ); - - HttpRequest deflateRequest = - HttpRequest.POST("/") - .addHeader(ContentEncoding.create(HttpEncodings.DEFLATE)) - .withEntity(Coder.Deflate.encode(ByteString.fromString("abcdef"))); - route.run(deflateRequest) - .assertStatusCode(200) - .assertEntity("abcdef"); - - HttpRequest gzipRequest = - HttpRequest.POST("/") - .addHeader(ContentEncoding.create(HttpEncodings.GZIP)) - .withEntity(Coder.Gzip.encode(ByteString.fromString("hijklmnopq"))); - route.run(gzipRequest) - .assertStatusCode(200) - .assertEntity("hijklmnopq"); - } - - @Test - public void testGzipDecoding() { - TestRoute route = - testRoute( - decodeRequestWith(Collections.singleton(Coder.Gzip), () -> - extractEntity(entity -> complete(entity)) - ) - ); - - HttpRequest gzipRequest = - HttpRequest.POST("/") - .addHeader(ContentEncoding.create(HttpEncodings.GZIP)) - .withEntity(Coder.Gzip.encode(ByteString.fromString("hijklmnopq"))); - route.run(gzipRequest) - .assertStatusCode(200) - .assertEntity("hijklmnopq"); - - HttpRequest deflateRequest = - HttpRequest.POST("/") - .addHeader(ContentEncoding.create(HttpEncodings.DEFLATE)) - .withEntity(Coder.Deflate.encode(ByteString.fromString("abcdef"))); - route.run(deflateRequest) - .assertStatusCode(400) - .assertEntity("The request's Content-Encoding is not supported. Expected:\ngzip"); - } -} diff --git a/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/CookieDirectivesTest.java b/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/CookieDirectivesTest.java deleted file mode 100644 index d7809e8661..0000000000 --- a/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/CookieDirectivesTest.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server.directives; - -import akka.http.javadsl.model.HttpRequest; -import akka.http.javadsl.model.headers.HttpCookie; -import akka.http.javadsl.testkit.JUnitRouteTest; -import akka.http.javadsl.testkit.TestRoute; -import org.junit.Test; - -public class CookieDirectivesTest extends JUnitRouteTest { - @Test - public void testCookieValue() { - TestRoute route = - testRoute( - cookie("userId", userId -> complete(userId.value())) - ); - - route.run(HttpRequest.create()) - .assertStatusCode(400) - .assertEntity("Request is missing required cookie 'userId'"); - - route.run(HttpRequest.create().addHeader(akka.http.javadsl.model.headers.Cookie.create("userId", "12345"))) - .assertStatusCode(200) - .assertEntity("12345"); - } - - @Test - public void testCookieOptionalValue() { - TestRoute route = - testRoute( - optionalCookie("userId", opt -> complete(opt.toString())) - ); - - route.run(HttpRequest.create()) - .assertStatusCode(200) - .assertEntity("Optional.empty"); - - route.run(HttpRequest.create().addHeader(akka.http.javadsl.model.headers.Cookie.create("userId", "12345"))) - .assertStatusCode(200) - .assertEntity("Optional[userId=12345]"); - } - - @Test - public void testCookieSet() { - TestRoute route = - testRoute( - setCookie(HttpCookie.create("userId", "12"), () -> complete("OK!")) - ); - - route.run(HttpRequest.create()) - .assertStatusCode(200) - .assertHeaderExists("Set-Cookie", "userId=12") - .assertEntity("OK!"); - } - - @Test - public void testDeleteCookie() { - TestRoute route = - testRoute( - deleteCookie("userId", () -> complete("OK!")) - ); - - route.run(HttpRequest.create()) - .assertStatusCode(200) - .assertHeaderExists("Set-Cookie", "userId=deleted; Expires=Wed, 01 Jan 1800 00:00:00 GMT") - .assertEntity("OK!"); - } -} diff --git a/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/ExecutionDirectivesTest.java b/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/ExecutionDirectivesTest.java deleted file mode 100644 index 2bc1c7dcc9..0000000000 --- a/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/ExecutionDirectivesTest.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server.directives; - -import org.junit.Test; - -import akka.http.javadsl.model.HttpRequest; -import akka.http.javadsl.model.StatusCodes; -import akka.http.javadsl.server.ExceptionHandler; -import akka.http.javadsl.server.RejectionHandler; -import akka.http.javadsl.server.Route; -import akka.http.javadsl.unmarshalling.StringUnmarshallers; -import akka.http.javadsl.testkit.JUnitRouteTest; -import akka.http.javadsl.testkit.TestRoute; -import akka.http.scaladsl.server.MethodRejection; -import akka.http.scaladsl.server.Rejection; - -public class ExecutionDirectivesTest extends JUnitRouteTest { - @Test - public void testCatchExceptionThrownFromHandler() { - Route divide = - path("divide", () -> - parameter(StringUnmarshallers.INTEGER, "a", a -> - parameter(StringUnmarshallers.INTEGER, "b", b -> - complete("The result is: " + (a / b))))); - - - ExceptionHandler handleDivByZero = ExceptionHandler.newBuilder() - .match(ArithmeticException.class, t -> complete(StatusCodes.BAD_REQUEST, "Congratulations you provoked a division by zero!")) - .build(); - - TestRoute route = - testRoute( - handleExceptions(handleDivByZero, () -> divide) - ); - - route.run(HttpRequest.GET("/divide?a=10&b=5")) - .assertEntity("The result is: 2"); - - route.run(HttpRequest.GET("/divide?a=10&b=0")) - .assertStatusCode(StatusCodes.BAD_REQUEST) - .assertEntity("Congratulations you provoked a division by zero!"); - } - - @Test - public void testHandleMethodRejection() { - RejectionHandler rejectionHandler = RejectionHandler.newBuilder() - .handle(MethodRejection.class, r -> complete(StatusCodes.BAD_REQUEST, "Whoopsie! Unsupported method. Supported would have been " + r.supported().value())) - .build(); - - TestRoute route = - testRoute( - handleRejections(rejectionHandler, () -> - get(() -> complete("Successful!")) - ) - ); - - route.run(HttpRequest.GET("/")) - .assertStatusCode(StatusCodes.OK) - .assertEntity("Successful!"); - - route.run(HttpRequest.POST("/")) - .assertStatusCode(StatusCodes.BAD_REQUEST) - .assertEntity("Whoopsie! Unsupported method. Supported would have been GET"); - } - - public static final class TooManyRequestsRejection implements Rejection { - final public String message; - - TooManyRequestsRejection(String message) { - this.message = message; - } - } - - private final Route testRoute = extractUri(uri -> { - if (uri.path().startsWith("/test")) - return complete("Successful!"); - else - return reject(new TooManyRequestsRejection("Too many requests for busy path!")); - }); - - @Test - public void testHandleCustomRejection() { - RejectionHandler rejectionHandler = RejectionHandler.newBuilder() - .handle(TooManyRequestsRejection.class, rej -> complete(StatusCodes.TOO_MANY_REQUESTS, rej.message)) - .build(); - - TestRoute route = testRoute(handleRejections(rejectionHandler, () -> testRoute)); - - route.run(HttpRequest.GET("/test")) - .assertStatusCode(StatusCodes.OK); - - route.run(HttpRequest.GET("/other")) - .assertStatusCode(StatusCodes.TOO_MANY_REQUESTS) - .assertEntity("Too many requests for busy path!"); - } -} diff --git a/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/HeaderDirectivesTest.java b/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/HeaderDirectivesTest.java deleted file mode 100644 index fe9ecad53f..0000000000 --- a/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/HeaderDirectivesTest.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.javadsl.server.directives; - -import akka.http.javadsl.model.HttpHeader; -import akka.http.javadsl.model.HttpRequest; -import akka.http.javadsl.model.StatusCodes; -import akka.http.javadsl.model.headers.*; -import akka.http.javadsl.testkit.JUnitRouteTest; -import akka.http.javadsl.testkit.TestRoute; -import akka.japi.pf.PFBuilder; -import org.junit.Test; - -import java.util.Optional; - -public class HeaderDirectivesTest extends JUnitRouteTest { - - @Test - public void testHeaderValue() { - TestRoute route = testRoute(headerValue((header) -> { - if (header.name().equals("X-Test-Header")) { - if (header.value().equals("bad value")) throw new RuntimeException("bad value"); - else return Optional.of(header.value()); - } - else return Optional.empty(); - }, - this::complete)); - - route - .run(HttpRequest.create().addHeader(RawHeader.create("X-Test-Header", "woho!"))) - .assertStatusCode(StatusCodes.OK) - .assertEntity("woho!"); - - route - .run(HttpRequest.create().addHeader(RawHeader.create("X-Test-Header", "bad value"))) - .assertStatusCode(StatusCodes.BAD_REQUEST); - - - route - .run(HttpRequest.create()) - .assertStatusCode(StatusCodes.NOT_FOUND); - } - - @Test - public void testHeaderValuePF() { - TestRoute route = testRoute(headerValuePF( - new PFBuilder().match( - Host.class, Host::value - ).build(), - this::complete)); - - route - .run(HttpRequest.create().addHeader(Host.create("example.com"))) - .assertStatusCode(StatusCodes.OK) - .assertEntity("example.com"); - - route - .run(HttpRequest.create()) - .assertStatusCode(StatusCodes.NOT_FOUND); - } - - @Test - public void testHeaderValueByName() { - TestRoute route = testRoute(headerValueByName("X-Test-Header", this::complete)); - - route - .run(HttpRequest.create().addHeader(RawHeader.create("X-Test-Header", "woho!"))) - .assertStatusCode(StatusCodes.OK) - .assertEntity("woho!"); - - route - .run(HttpRequest.create()) - .assertStatusCode(StatusCodes.BAD_REQUEST); - } - - @Test - public void testHeaderValueByType() { - TestRoute route = testRoute(headerValueByType(Server.class, - (Server s) -> complete(s.getProducts().iterator().next().product()))); - - route - .run(HttpRequest.create().addHeader(Server.create(ProductVersion.create("such-service", "0.6")))) - .assertStatusCode(StatusCodes.OK) - .assertEntity("such-service"); - - route - .run(HttpRequest.create()) - .assertStatusCode(StatusCodes.BAD_REQUEST); - } - - @Test - public void testOptionalHeaderValue() { - TestRoute route = testRoute(optionalHeaderValue((header) -> { - if (header.name().equals("X-Test-Header")) { - if (header.value().equals("bad value")) throw new RuntimeException("bad value"); - else return Optional.of(header.value()); - } - else return Optional.empty(); - }, - opt -> complete(opt.toString()))); - - route - .run(HttpRequest.create().addHeader(RawHeader.create("X-Test-Header", "woho!"))) - .assertStatusCode(StatusCodes.OK) - .assertEntity("Optional[woho!]"); - - route - .run(HttpRequest.create().addHeader(RawHeader.create("X-Test-Header", "bad value"))) - .assertStatusCode(StatusCodes.BAD_REQUEST); - - - route - .run(HttpRequest.create()) - .assertStatusCode(StatusCodes.OK) - .assertEntity("Optional.empty"); - } - - @Test - public void testOptionalHeaderValuePF() { - TestRoute route = testRoute(optionalHeaderValuePF( - new PFBuilder().match( - Host.class, Host::value - ).build(), - (opt) -> complete(opt.toString()))); - - route - .run(HttpRequest.create().addHeader(Host.create("example.com"))) - .assertStatusCode(StatusCodes.OK) - .assertEntity("Optional[example.com]"); - - route - .run(HttpRequest.create()) - .assertStatusCode(StatusCodes.OK) - .assertEntity("Optional.empty"); - } - - - @Test - public void testOptionalHeaderValueByName() { - TestRoute route = testRoute(optionalHeaderValueByName("X-Test-Header", (opt) -> complete(opt.toString()))); - - route - .run(HttpRequest.create().addHeader(RawHeader.create("X-Test-Header", "woho!"))) - .assertStatusCode(StatusCodes.OK) - .assertEntity("Optional[woho!]"); - - route - .run(HttpRequest.create()) - .assertStatusCode(StatusCodes.OK) - .assertEntity("Optional.empty"); - } - - @Test - public void testOptionalHeaderValueByType() { - TestRoute route = testRoute(optionalHeaderValueByType(Server.class, - (Optional s) -> complete(((Boolean)s.isPresent()).toString()))); - - route - .run(HttpRequest.create().addHeader(Server.create(ProductVersion.create("such-service", "0.6")))) - .assertStatusCode(StatusCodes.OK) - .assertEntity("true"); - - route - .run(HttpRequest.create()) - .assertStatusCode(StatusCodes.OK) - .assertEntity("false"); - } - - @Test - public void testValueByTypeHandlesCustomHeaders() { - TestRoute route = testRoute(headerValueByType(SampleCustomHeader.class, - (SampleCustomHeader m) -> complete(m.value()))); - - route - .run(HttpRequest.create().addHeader(RawHeader.create("X-Sample-Custom-Header", "such header"))) - .assertStatusCode(StatusCodes.OK) - .assertEntity("X-Sample-Custom-Header: such header"); - - route - .run(HttpRequest.create()) - .assertStatusCode(StatusCodes.BAD_REQUEST); - } - - @Test - public void testCheckSameOrigin() { - final HttpOrigin validOriginHeader = HttpOrigin.create("http://localhost", Host.create("8080")); - - final HttpOriginRange validOriginRange = HttpOriginRange.create(validOriginHeader); - - 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); - } - - @Test - public void testCheckSameOriginGivenALL() { - final HttpOrigin validOriginHeader = HttpOrigin.create("http://localhost", Host.create("8080")); - - // not very interesting case, however here we check that the directive simply avoids performing the check - final HttpOriginRange everythingGoes = HttpOriginRanges.ALL; - - final TestRoute route = testRoute(checkSameOrigin(everythingGoes, () -> complete("Result"))); - - route - .run(HttpRequest.create().addHeader(Origin.create(validOriginHeader))) - .assertStatusCode(StatusCodes.OK) - .assertEntity("Result"); - - route - .run(HttpRequest.create()) - .assertStatusCode(StatusCodes.OK) - .assertEntity("Result"); - - final HttpOrigin otherOriginHeader = HttpOrigin.create("http://invalid.com", Host.create("8080")); - - route - .run(HttpRequest.create().addHeader(Origin.create(otherOriginHeader))) - .assertStatusCode(StatusCodes.OK) - .assertEntity("Result"); - } - -} diff --git a/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/HostDirectivesTest.java b/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/HostDirectivesTest.java deleted file mode 100644 index 7e3c96de71..0000000000 --- a/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/HostDirectivesTest.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server.directives; - -import akka.http.javadsl.model.HttpRequest; -import akka.http.javadsl.model.StatusCodes; -import akka.http.javadsl.model.Uri; -import akka.http.javadsl.testkit.JUnitRouteTest; -import akka.http.javadsl.testkit.TestRoute; -import org.junit.Test; - -import java.util.ArrayList; -import java.util.regex.Pattern; - -public class HostDirectivesTest extends JUnitRouteTest { - @Test - public void testHostFilterBySingleName() { - TestRoute route = testRoute(host("example.org", () -> complete("OK!"))); - - route - .run(HttpRequest.create().withUri(Uri.create("http://example.org"))) - .assertStatusCode(StatusCodes.OK) - .assertEntity("OK!"); - - route - .run(HttpRequest.create().withUri(Uri.create("https://other.org"))) - .assertStatusCode(StatusCodes.NOT_FOUND); - } - - @Test - public void testHostFilterByNames() { - ArrayList hosts = new ArrayList(); - hosts.add("example.org"); - hosts.add("example2.org"); - TestRoute route = testRoute(host(hosts, () -> complete("OK!"))); - - route - .run(HttpRequest.create().withUri(Uri.create("http://example.org"))) - .assertStatusCode(StatusCodes.OK) - .assertEntity("OK!"); - - route - .run(HttpRequest.create().withUri(Uri.create("http://example2.org"))) - .assertStatusCode(StatusCodes.OK) - .assertEntity("OK!"); - - route - .run(HttpRequest.create().withUri(Uri.create("https://other.org"))) - .assertStatusCode(404); - } - - @Test - public void testHostFilterByPredicate() { - TestRoute route = testRoute(host(hostName -> hostName.contains("ample"), () -> complete("OK!"))); - - route - .run(HttpRequest.create().withUri(Uri.create("http://example.org"))) - .assertStatusCode(StatusCodes.OK) - .assertEntity("OK!"); - - route - .run(HttpRequest.create().withUri(Uri.create("https://other.org"))) - .assertStatusCode(StatusCodes.NOT_FOUND); - } - - - @Test - public void testHostExtraction() { - TestRoute route = testRoute(extractHost(this::complete)); - - route - .run(HttpRequest.create().withUri(Uri.create("http://example.org"))) - .assertStatusCode(StatusCodes.OK) - .assertEntity("example.org"); - } - - @Test - public void testHostPatternExtraction() { - TestRoute route = - testRoute(host(Pattern.compile(".*\\.([^.]*)"), this::complete)); - - route - .run(HttpRequest.create().withUri(Uri.create("http://example.org"))) - .assertStatusCode(StatusCodes.OK) - .assertEntity("org"); - - route - .run(HttpRequest.create().withUri(Uri.create("http://example.de"))) - .assertStatusCode(StatusCodes.OK) - .assertEntity("de"); - } - -} diff --git a/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/MarshallingDirectivesTest.java b/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/MarshallingDirectivesTest.java deleted file mode 100644 index e1475aa402..0000000000 --- a/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/MarshallingDirectivesTest.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server.directives; - -import akka.http.javadsl.model.HttpRequest; -import akka.http.javadsl.model.RemoteAddress; -import akka.http.javadsl.model.StatusCodes; -import akka.http.javadsl.model.Uri; -import akka.http.javadsl.model.headers.RawHeader; -import akka.http.javadsl.model.headers.XForwardedFor; -import akka.http.javadsl.testkit.JUnitRouteTest; -import akka.http.javadsl.testkit.TestRoute; - -import org.junit.Test; -import akka.http.javadsl.unmarshalling.Unmarshaller; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.regex.Pattern; - -public class MarshallingDirectivesTest extends JUnitRouteTest { - - @Test - public void testEntityAsString() { - TestRoute route = - testRoute( - entity(Unmarshaller.entityToString(), this::complete) - ); - - HttpRequest request = - HttpRequest.POST("/") - .withEntity("abcdef"); - route.run(request) - .assertStatusCode(StatusCodes.OK) - .assertEntity("abcdef"); - } -} diff --git a/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/MiscDirectivesTest.java b/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/MiscDirectivesTest.java deleted file mode 100644 index ad802b20d3..0000000000 --- a/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/MiscDirectivesTest.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server.directives; - -import akka.http.javadsl.model.HttpRequest; -import akka.http.javadsl.model.RemoteAddress; -import akka.http.javadsl.model.StatusCodes; -import akka.http.javadsl.model.Uri; -import akka.http.javadsl.model.headers.RawHeader; -import akka.http.javadsl.model.headers.XForwardedFor; -import akka.http.javadsl.model.headers.XRealIp; -import akka.http.javadsl.unmarshalling.Unmarshaller; -import akka.http.javadsl.testkit.JUnitRouteTest; -import akka.http.javadsl.testkit.TestRoute; -import org.junit.Test; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.Arrays; - -public class MiscDirectivesTest extends JUnitRouteTest { - - static boolean isShort(String str) { - return str.length() < 5; - } - - static boolean hasShortPath(Uri uri) { - return uri.path().toString().length() < 5; - } - - @Test - public void testValidateUri() { - TestRoute route = testRoute( - extractUri(uri -> - validate(() -> hasShortPath(uri), "Path too long!", - () -> complete("OK!") - ) - ) - ); - - route - .run(HttpRequest.create().withUri(Uri.create("/abc"))) - .assertStatusCode(StatusCodes.OK) - .assertEntity("OK!"); - - route - .run(HttpRequest.create().withUri(Uri.create("/abcdefghijkl"))) - .assertStatusCode(StatusCodes.BAD_REQUEST) - .assertEntity("Path too long!"); - } - - @Test - public void testClientIpExtraction() throws UnknownHostException { - TestRoute route = testRoute(extractClientIP(ip -> complete(ip.toString()))); - - route - .run(HttpRequest.create().addHeader(XForwardedFor.create(RemoteAddress.create(InetAddress.getByName("127.0.0.2"))))) - .assertStatusCode(StatusCodes.OK) - .assertEntity("127.0.0.2"); - - route - .run(HttpRequest.create().addHeader(akka.http.javadsl.model.headers.RemoteAddress.create(RemoteAddress.create(InetAddress.getByName("127.0.0.3"))))) - .assertStatusCode(StatusCodes.OK) - .assertEntity("127.0.0.3"); - - route - .run(HttpRequest.create().addHeader(XRealIp.create(RemoteAddress.create(InetAddress.getByName("127.0.0.4"))))) - .assertStatusCode(StatusCodes.OK) - .assertEntity("127.0.0.4"); - - route - .run(HttpRequest.create()) - .assertStatusCode(StatusCodes.OK); - } - - @Test - public void testWithSizeLimit() { - TestRoute route = testRoute(withSizeLimit(500, () -> - entity(Unmarshaller.entityToString(), (entity) -> complete("ok")) - )); - - route - .run(withEntityOfSize(500)) - .assertStatusCode(StatusCodes.OK); - - route - .run(withEntityOfSize(501)) - .assertStatusCode(StatusCodes.BAD_REQUEST); - - } - - private HttpRequest withEntityOfSize(int sizeLimit) { - char[] charArray = new char[sizeLimit]; - Arrays.fill(charArray, '0'); - return HttpRequest.POST("/").withEntity(new String(charArray)); - } - -} diff --git a/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/ParameterDirectivesTest.java b/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/ParameterDirectivesTest.java deleted file mode 100644 index 1084bee84e..0000000000 --- a/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/ParameterDirectivesTest.java +++ /dev/null @@ -1,374 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server.directives; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.Map; - -import org.junit.Test; - -import akka.http.javadsl.model.HttpRequest; -import akka.http.javadsl.unmarshalling.StringUnmarshallers; -import akka.http.javadsl.testkit.JUnitRouteTest; -import akka.http.javadsl.testkit.TestRoute; - -public class ParameterDirectivesTest extends JUnitRouteTest { - - @Test - public void testStringParameterExtraction() { - TestRoute route = testRoute(parameter("stringParam", value -> complete(value))); - - route - .run(HttpRequest.create().withUri("/abc")) - .assertStatusCode(404) - .assertEntity("Request is missing required query parameter 'stringParam'"); - - route - .run(HttpRequest.create().withUri("/abc?stringParam=john")) - .assertStatusCode(200) - .assertEntity("john"); - } - - @Test - public void testByteParameterExtraction() { - TestRoute route = testRoute(parameter(StringUnmarshallers.BYTE, "byteParam", value -> complete(value.toString()))); - - route - .run(HttpRequest.create().withUri("/abc")) - .assertStatusCode(404) - .assertEntity("Request is missing required query parameter 'byteParam'"); - - route - .run(HttpRequest.create().withUri("/abc?byteParam=test")) - .assertStatusCode(400) - .assertEntity("The query parameter 'byteParam' was malformed:\n'test' is not a valid 8-bit signed integer value"); - - route - .run(HttpRequest.create().withUri("/abc?byteParam=1000")) - .assertStatusCode(400) - .assertEntity("The query parameter 'byteParam' was malformed:\n'1000' is not a valid 8-bit signed integer value"); - - route - .run(HttpRequest.create().withUri("/abc?byteParam=48")) - .assertStatusCode(200) - .assertEntity("48"); - } - - @Test - public void testShortParameterExtraction() { - TestRoute route = testRoute(parameter(StringUnmarshallers.SHORT, "shortParam", value -> complete(value.toString()))); - - route - .run(HttpRequest.create().withUri("/abc")) - .assertStatusCode(404) - .assertEntity("Request is missing required query parameter 'shortParam'"); - - route - .run(HttpRequest.create().withUri("/abc?shortParam=test")) - .assertStatusCode(400) - .assertEntity("The query parameter 'shortParam' was malformed:\n'test' is not a valid 16-bit signed integer value"); - - route - .run(HttpRequest.create().withUri("/abc?shortParam=100000")) - .assertStatusCode(400) - .assertEntity("The query parameter 'shortParam' was malformed:\n'100000' is not a valid 16-bit signed integer value"); - - route - .run(HttpRequest.create().withUri("/abc?shortParam=1234")) - .assertStatusCode(200) - .assertEntity("1234"); - } - - @Test - public void testIntegerParameterExtraction() { - TestRoute route = testRoute(parameter(StringUnmarshallers.INTEGER, "intParam", value -> complete(value.toString()))); - - route - .run(HttpRequest.create().withUri("/abc")) - .assertStatusCode(404) - .assertEntity("Request is missing required query parameter 'intParam'"); - - route - .run(HttpRequest.create().withUri("/abc?intParam=test")) - .assertStatusCode(400) - .assertEntity("The query parameter 'intParam' was malformed:\n'test' is not a valid 32-bit signed integer value"); - - route - .run(HttpRequest.create().withUri("/abc?intParam=48")) - .assertStatusCode(200) - .assertEntity("48"); - } - - @Test - public void testLongParameterExtraction() { - TestRoute route = testRoute(parameter(StringUnmarshallers.LONG, "longParam", value -> complete(value.toString()))); - - route - .run(HttpRequest.create().withUri("/abc")) - .assertStatusCode(404) - .assertEntity("Request is missing required query parameter 'longParam'"); - - route - .run(HttpRequest.create().withUri("/abc?longParam=test")) - .assertStatusCode(400) - .assertEntity("The query parameter 'longParam' was malformed:\n'test' is not a valid 64-bit signed integer value"); - - route - .run(HttpRequest.create().withUri("/abc?longParam=123456")) - .assertStatusCode(200) - .assertEntity("123456"); - } - - @Test - public void testFloatParameterExtraction() { - TestRoute route = testRoute(parameter(StringUnmarshallers.FLOAT, "floatParam", value -> complete(value.toString()))); - - route - .run(HttpRequest.create().withUri("/abc")) - .assertStatusCode(404) - .assertEntity("Request is missing required query parameter 'floatParam'"); - - route - .run(HttpRequest.create().withUri("/abc?floatParam=test")) - .assertStatusCode(400) - .assertEntity("The query parameter 'floatParam' was malformed:\n'test' is not a valid 32-bit floating point value"); - - route - .run(HttpRequest.create().withUri("/abc?floatParam=48")) - .assertStatusCode(200) - .assertEntity("48.0"); - } - - @Test - public void testDoubleParameterExtraction() { - TestRoute route = testRoute(parameter(StringUnmarshallers.DOUBLE, "doubleParam", value -> complete(value.toString()))); - - route - .run(HttpRequest.create().withUri("/abc")) - .assertStatusCode(404) - .assertEntity("Request is missing required query parameter 'doubleParam'"); - - route - .run(HttpRequest.create().withUri("/abc?doubleParam=test")) - .assertStatusCode(400) - .assertEntity("The query parameter 'doubleParam' was malformed:\n'test' is not a valid 64-bit floating point value"); - - route - .run(HttpRequest.create().withUri("/abc?doubleParam=48")) - .assertStatusCode(200) - .assertEntity("48.0"); - } - - @Test - public void testHexByteParameterExtraction() { - TestRoute route = testRoute(parameter(StringUnmarshallers.BYTE_HEX, "hexByteParam", value -> complete(value.toString()))); - - route - .run(HttpRequest.create().withUri("/abc")) - .assertStatusCode(404) - .assertEntity("Request is missing required query parameter 'hexByteParam'"); - - route - .run(HttpRequest.create().withUri("/abc?hexByteParam=test")) - .assertStatusCode(400) - .assertEntity("The query parameter 'hexByteParam' was malformed:\n'test' is not a valid 8-bit hexadecimal integer value"); - - route - .run(HttpRequest.create().withUri("/abc?hexByteParam=1000")) - .assertStatusCode(400) - .assertEntity("The query parameter 'hexByteParam' was malformed:\n'1000' is not a valid 8-bit hexadecimal integer value"); - - route - .run(HttpRequest.create().withUri("/abc?hexByteParam=48")) - .assertStatusCode(200) - .assertEntity(Integer.toString(0x48)); - } - - @Test - public void testHexShortParameterExtraction() { - TestRoute route = testRoute(parameter(StringUnmarshallers.SHORT_HEX, "hexShortParam", value -> complete(value.toString()))); - - route - .run(HttpRequest.create().withUri("/abc")) - .assertStatusCode(404) - .assertEntity("Request is missing required query parameter 'hexShortParam'"); - - route - .run(HttpRequest.create().withUri("/abc?hexShortParam=test")) - .assertStatusCode(400) - .assertEntity("The query parameter 'hexShortParam' was malformed:\n'test' is not a valid 16-bit hexadecimal integer value"); - - route - .run(HttpRequest.create().withUri("/abc?hexShortParam=100000")) - .assertStatusCode(400) - .assertEntity("The query parameter 'hexShortParam' was malformed:\n'100000' is not a valid 16-bit hexadecimal integer value"); - - route - .run(HttpRequest.create().withUri("/abc?hexShortParam=1234")) - .assertStatusCode(200) - .assertEntity(Integer.toString(0x1234)); - } - - @Test - public void testHexIntegerParameterExtraction() { - TestRoute route = testRoute(parameter(StringUnmarshallers.INTEGER_HEX, "hexIntParam", value -> complete(value.toString()))); - - route - .run(HttpRequest.create().withUri("/abc")) - .assertStatusCode(404) - .assertEntity("Request is missing required query parameter 'hexIntParam'"); - - route - .run(HttpRequest.create().withUri("/abc?hexIntParam=test")) - .assertStatusCode(400) - .assertEntity("The query parameter 'hexIntParam' was malformed:\n'test' is not a valid 32-bit hexadecimal integer value"); - - route - .run(HttpRequest.create().withUri("/abc?hexIntParam=12345678")) - .assertStatusCode(200) - .assertEntity(Integer.toString(0x12345678)); - } - - @Test - public void testHexLongParameterExtraction() { - TestRoute route = testRoute(parameter(StringUnmarshallers.LONG_HEX, "hexLongParam", value -> complete(value.toString()))); - - route - .run(HttpRequest.create().withUri("/abc")) - .assertStatusCode(404) - .assertEntity("Request is missing required query parameter 'hexLongParam'"); - - route - .run(HttpRequest.create().withUri("/abc?hexLongParam=test")) - .assertStatusCode(400) - .assertEntity("The query parameter 'hexLongParam' was malformed:\n'test' is not a valid 64-bit hexadecimal integer value"); - - route - .run(HttpRequest.create().withUri("/abc?hexLongParam=123456789a")) - .assertStatusCode(200) - .assertEntity(Long.toString(0x123456789aL)); - } - - @Test - public void testParametersAsMapExtraction() { - TestRoute route = testRoute( - parameterMap(paramMap -> { - ArrayList keys = new ArrayList(paramMap.keySet()); - Collections.sort(keys); - StringBuilder res = new StringBuilder(); - res.append(paramMap.size()).append(": ["); - for (String key : keys) - res.append(key).append(" -> ").append(paramMap.get(key)).append(", "); - res.append(']'); - return complete(res.toString()); - })); - - route - .run(HttpRequest.create().withUri("/abc")) - .assertStatusCode(200) - .assertEntity("0: []"); - - route - .run(HttpRequest.create().withUri("/abc?a=b")) - .assertStatusCode(200) - .assertEntity("1: [a -> b, ]"); - - route - .run(HttpRequest.create().withUri("/abc?a=b&c=d")) - .assertStatusCode(200) - .assertEntity("2: [a -> b, c -> d, ]"); - } - - @Test - public void testParametersAsMultiMapExtraction() { - TestRoute route = testRoute( - parameterMultiMap(paramMap -> { - ArrayList keys = new ArrayList(paramMap.keySet()); - Collections.sort(keys); - StringBuilder res = new StringBuilder(); - res.append(paramMap.size()).append(": ["); - for (String key : keys) { - res.append(key).append(" -> ["); - ArrayList values = new ArrayList(paramMap.get(key)); - Collections.sort(values); - for (String value : values) - res.append(value).append(", "); - res.append("], "); - } - res.append(']'); - return complete(res.toString()); - })); - - route - .run(HttpRequest.create().withUri("/abc")) - .assertStatusCode(200) - .assertEntity("0: []"); - - route - .run(HttpRequest.create().withUri("/abc?a=b")) - .assertStatusCode(200) - .assertEntity("1: [a -> [b, ], ]"); - - route - .run(HttpRequest.create().withUri("/abc?a=b&c=d&a=a")) - .assertStatusCode(200) - .assertEntity("2: [a -> [a, b, ], c -> [d, ], ]"); - } - - @Test - public void testParametersAsCollectionExtraction() { - TestRoute route = testRoute( - parameterList(paramEntries -> { - ArrayList> entries = new ArrayList>(paramEntries); - Collections.sort(entries, new Comparator>() { - @Override - public int compare(Map.Entry e1, Map.Entry e2) { - int res = e1.getKey().compareTo(e2.getKey()); - return res == 0 ? e1.getValue().compareTo(e2.getValue()) : res; - } - }); - - StringBuilder res = new StringBuilder(); - res.append(paramEntries.size()).append(": ["); - for (Map.Entry entry : entries) - res.append(entry.getKey()).append(" -> ").append(entry.getValue()).append(", "); - res.append(']'); - return complete(res.toString()); - })); - - route - .run(HttpRequest.create().withUri("/abc")) - .assertStatusCode(200) - .assertEntity("0: []"); - - route - .run(HttpRequest.create().withUri("/abc?a=b&e=f&c=d")) - .assertStatusCode(200) - .assertEntity("3: [a -> b, c -> d, e -> f, ]"); - - route - .run(HttpRequest.create().withUri("/abc?a=b&e=f&c=d&a=z")) - .assertStatusCode(200) - .assertEntity("4: [a -> b, a -> z, c -> d, e -> f, ]"); - } - - @Test - public void testOptionalIntParameterExtraction() { - TestRoute route = testRoute(parameterOptional(StringUnmarshallers.INTEGER, "optionalIntParam", value -> complete(value.toString()))); - - route - .run(HttpRequest.create().withUri("/abc")) - .assertStatusCode(200) - .assertEntity("Optional.empty"); - - route - .run(HttpRequest.create().withUri("/abc?optionalIntParam=23")) - .assertStatusCode(200) - .assertEntity("Optional[23]"); - } - -} diff --git a/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/PathDirectivesTest.java b/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/PathDirectivesTest.java deleted file mode 100644 index 3954d27e3a..0000000000 --- a/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/PathDirectivesTest.java +++ /dev/null @@ -1,378 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server.directives; - -import static akka.http.javadsl.server.PathMatchers.*; - -import org.junit.Test; - -import akka.http.javadsl.model.StatusCodes; -import akka.http.javadsl.model.headers.Host; -import akka.http.javadsl.server.PathMatchers; -import akka.http.javadsl.testkit.JUnitRouteTest; -import akka.http.javadsl.testkit.TestRoute; -import akka.http.scaladsl.model.HttpRequest; - -public class PathDirectivesTest extends JUnitRouteTest { - @Test - public void testPathPrefixAndPath() { - TestRoute route = testRoute( - pathPrefix("pet", () -> route( - path("cat", () -> complete("The cat!")), - path("dog", () -> complete("The dog!")), - pathSingleSlash(() -> complete("Here are only pets.")) - )) - ); - - route.run(HttpRequest.GET("/pet/")) - .assertEntity("Here are only pets."); - - route.run(HttpRequest.GET("/pet")) // missing trailing slash - .assertStatusCode(404); - - route.run(HttpRequest.GET("/pet/cat")) - .assertEntity("The cat!"); - - route.run(HttpRequest.GET("/pet/dog")) - .assertEntity("The dog!"); - } - - @Test - public void testRawPathPrefix() { - TestRoute route1 = testRoute( - rawPathPrefix(separateOnSlashes("/pet//cat"), () -> complete("The cat!")) - ); - - route1.run(HttpRequest.GET("/pet//cat")) - .assertEntity("The cat!"); - - // any suffix allowed - route1.run(HttpRequest.GET("/pet//cat/abcdefg")) - .assertEntity("The cat!"); - - TestRoute route2 = testRoute( - rawPathPrefix(separateOnSlashes("/pet//cat"), () -> - pathEnd(() -> complete("The cat!")) - ) - ); - - route2.run(HttpRequest.GET("/pet//cat")) - .assertEntity("The cat!"); - - route2.run(HttpRequest.GET("/pet//cat/abcdefg")) - .assertStatusCode(404); - } - - @Test - public void testSegment() { - TestRoute route = - testRoute( - pathPrefix("hey", () -> path(name -> complete(name) ) ) - ); - - route.run(HttpRequest.GET("/hey/jude")) - .assertEntity("jude"); - } - - @Test - public void testPathEnd() { - TestRoute route = - testRoute( - pathPrefix("test", () -> route( - pathEnd(() -> complete("end")), - path("abc", () -> complete("abc")) - )) - ); - - route.run(HttpRequest.GET("/test")) - .assertEntity("end"); - - route.run(HttpRequest.GET("/test/abc")) - .assertEntity("abc"); - - route.run(HttpRequest.GET("/xyz")) - .assertStatusCode(404); - } - - @Test - public void testSingleSlash() { - TestRoute route = - testRoute( - pathPrefix("test", () -> - pathSingleSlash(() -> complete("Ok")) - ) - ); - - route.run(HttpRequest.GET("/test/")) - .assertEntity("Ok"); - - route.run(HttpRequest.GET("/test")) - .assertStatusCode(404); - } - - @Test - public void testPathEndOrSingleSlash() { - TestRoute route = - testRoute( - pathPrefix("test", () -> - pathEndOrSingleSlash(() -> complete("Ok")) - ) - ); - - route.run(HttpRequest.GET("/test")) - .assertEntity("Ok"); - - route.run(HttpRequest.GET("/test/")) - .assertEntity("Ok"); - - route.run(HttpRequest.GET("/abc")) - .assertStatusCode(404); - } - - @Test - public void testRawPathPrefixTest() { - TestRoute route = - testRoute( - rawPathPrefixTest(slash().concat(segment("abc")), () -> - extractUnmatchedPath(s -> complete(s)) - ) - ); - - route.run(HttpRequest.GET("/abc")) - .assertEntity("/abc"); - - route.run(HttpRequest.GET("/abc/def")) - .assertEntity("/abc/def"); - - route.run(HttpRequest.GET("/abcd/ef")) - .assertEntity("/abcd/ef"); - - route.run(HttpRequest.GET("/xyz/def")) - .assertStatusCode(404); - } - - @Test - public void testPathPrefixTest() { - TestRoute route = - testRoute( - pathPrefixTest("abc", () -> extractUnmatchedPath(s -> complete(s))) - ); - - route.run(HttpRequest.GET("/abc")) - .assertEntity("/abc"); - - route.run(HttpRequest.GET("/abc/def")) - .assertEntity("/abc/def"); - - route.run(HttpRequest.GET("/abcd/ef")) - .assertEntity("/abcd/ef"); - - route.run(HttpRequest.GET("/xyz/def")) - .assertStatusCode(404); - } - - @Test - public void testPathSuffix() { - TestRoute route = - testRoute( - pathSuffix(PathMatchers.slash().concat(segment("abc")), () -> extractUnmatchedPath(s -> complete(s))) - ); - - route.run(HttpRequest.GET("/test/abc/")) - .assertEntity("/test/"); - - route.run(HttpRequest.GET("/abc/")) - .assertEntity("/"); - - route.run(HttpRequest.GET("/abc/def")) - .assertStatusCode(404); - - route.run(HttpRequest.GET("/abc")) - .assertStatusCode(404); - } - - @Test - public void testPathSuffixTest() { - TestRoute route = - testRoute( - pathSuffixTest("abc", () -> extractUnmatchedPath(s -> complete(s))) - ); - - route.run(HttpRequest.GET("/test/abc")) - .assertEntity("/test/abc"); - - route.run(HttpRequest.GET("/abc")) - .assertEntity("/abc"); - - route.run(HttpRequest.GET("/abc/def")) - .assertStatusCode(404); - } - - @Test - public void testIntegerMatcher() { - TestRoute route = - testRoute( - path(segment("age").slash(integerSegment()), value -> complete(value.toString())) - ); - - route.run(HttpRequest.GET("/age/38")) - .assertEntity("38"); - - route.run(HttpRequest.GET("/age/abc")) - .assertStatusCode(404); - } - - @Test - public void testIntegerConcatMatcher() { - TestRoute route = - testRoute( - // this is testing that we can express the same path using slash() and concat() as with nexting inside slash() - path(segment("age").slash().concat(integerSegment()), value -> complete(value.toString())) - ); - - route.run(HttpRequest.GET("/age/38")) - .assertEntity("38"); - - route.run(HttpRequest.GET("/age/abc")) - .assertStatusCode(404); - } - - @Test - public void testTwoVals() { - // tests that `x` and `y` have different identities which is important for - // retrieving the values - - TestRoute route = - testRoute( - path(segment("multiply").slash(integerSegment()).slash("with").slash(integerSegment()), (x, y) -> - complete(String.format("%d * %d = %d", x, y, x * y)) - ) - ); - - route.run(HttpRequest.GET("/multiply/3/with/6")) - .assertEntity("3 * 6 = 18"); - } - - @Test - public void testHexIntegerMatcher() { - TestRoute route = - testRoute( - path(segment("color").slash(hexIntegerSegment()), color -> complete(color.toString())) - ); - - route.run(HttpRequest.GET("/color/a0c2ef")) - .assertEntity(Integer.toString(0xa0c2ef)); - } - - @Test - public void testLongMatcher() { - TestRoute route = - testRoute( - path(segment("bigage").slash(longSegment()), bigAge -> complete(bigAge.toString())) - ); - - route.run(HttpRequest.GET("/bigage/12345678901")) - .assertEntity("12345678901"); - } - - @Test - public void testSegmentMatcher() { - TestRoute route = - testRoute( - path(segment("string").slash(segment()), bigAge -> complete(bigAge)) - ); - - route.run(HttpRequest.GET("/string/hello-it-is-me")) - .assertEntity("hello-it-is-me"); - } - - @Test - public void testHexLongMatcher() { - TestRoute route = - testRoute( - path(segment("code").slash(hexLongSegment()), code -> complete(code.toString())) - ); - - route.run(HttpRequest.GET("/code/a0b1c2d3e4f5")) - .assertEntity(Long.toString(0xa0b1c2d3e4f5L)); - } - - @Test - public void testRemainingMatcher() { - TestRoute route = - testRoute( - path(remaining(), remainingPath -> complete(remainingPath)) - ); - - route.run(HttpRequest.GET("/pets/afdaoisd/asda/sfasfasf/asf")) - .assertEntity("pets/afdaoisd/asda/sfasfasf/asf"); - } - - @Test - public void testRemainingMatcherInsidePath() { - TestRoute route = - testRoute( - pathPrefix("pets", () -> - path(remaining(), remainingPath -> complete(remainingPath))) - ); - - route.run(HttpRequest.GET("/pets/afdaoisd/asda/sfasfasf/asf")) - .assertEntity("afdaoisd/asda/sfasfasf/asf"); - } - - @Test - public void testUUIDMatcher() { - TestRoute route = - testRoute( - path(segment("by-uuid").slash(uuidSegment()), uuid -> complete(uuid.toString())) - ); - - route.run(HttpRequest.GET("/by-uuid/6ba7b811-9dad-11d1-80b4-00c04fd430c8")) - .assertEntity("6ba7b811-9dad-11d1-80b4-00c04fd430c8"); - } - - @Test - public void testSegmentsMatcher() { - TestRoute route = - testRoute( - path(segment("pets").slash(segments()), segments -> complete(segments.toString())) - ); - - route.run(HttpRequest.GET("/pets/cat/dog")) - .assertEntity("[cat, dog]"); - } - - @Test - public void testRedirectToTrailingSlashIfMissing() { - TestRoute route = - testRoute( - redirectToTrailingSlashIfMissing(StatusCodes.FOUND, () -> complete("Ok")) - ); - - route.run(HttpRequest.GET("/home").addHeader(Host.create("example.com"))) - .assertStatusCode(302) - .assertHeaderExists("Location", "http://example.com/home/"); - - route.run(HttpRequest.GET("/home/")) - .assertStatusCode(200) - .assertEntity("Ok"); - } - - @Test - public void testRedirectToNoTrailingSlashIfPresent() { - TestRoute route = - testRoute( - redirectToNoTrailingSlashIfPresent(StatusCodes.FOUND, () -> complete("Ok")) - ); - - route.run(HttpRequest.GET("/home/").addHeader(Host.create("example.com"))) - .assertStatusCode(302) - .assertHeaderExists("Location", "http://example.com/home"); - - route.run(HttpRequest.GET("/home")) - .assertStatusCode(200) - .assertEntity("Ok"); - } -} diff --git a/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/RouteDirectivesTest.java b/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/RouteDirectivesTest.java deleted file mode 100644 index 0b7da8b6d0..0000000000 --- a/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/RouteDirectivesTest.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server.directives; - -import akka.http.javadsl.model.HttpRequest; -import akka.http.javadsl.model.StatusCodes; -import akka.http.javadsl.model.Uri; -import akka.http.javadsl.model.headers.Location; -import akka.http.javadsl.testkit.JUnitRouteTest; -import akka.http.javadsl.testkit.TestRoute; -import akka.stream.javadsl.Sink; -import akka.util.ByteString; -import org.junit.Test; - -public class RouteDirectivesTest extends JUnitRouteTest { - - @Test - public void testRedirection() { - Uri targetUri = Uri.create("http://example.com"); - TestRoute route = - testRoute( - redirect(targetUri, StatusCodes.FOUND) - ); - - route - .run(HttpRequest.create()) - .assertStatusCode(302) - .assertHeaderExists(Location.create(targetUri)); - } - - @Test - public void testEntitySizeNoLimit() { - TestRoute route = - testRoute( - path("no-limit", () -> - extractEntity(entity -> - extractMaterializer(mat -> - onSuccess(() -> entity - .withoutSizeLimit() - .getDataBytes() - .runWith(Sink.head(), mat), - bytes -> complete(bytes.utf8String()) - ) - ) - ) - ) - ); - - route - .run(HttpRequest.create("/no-limit").withEntity("1234567890")) - .assertStatusCode(StatusCodes.OK) - .assertEntity("1234567890"); - } - - private TestRoute routeWithLimit() { - return testRoute( - path("limit-5", () -> - extractEntity(entity -> - extractMaterializer(mat -> - onSuccess(() -> entity - .withSizeLimit(5) - .getDataBytes() - .runWith(Sink.head(), mat), - bytes -> complete(bytes.utf8String()) - ) - ) - ) - ) - ); - } - - @Test - public void testEntitySizeWithinLimit() { - TestRoute route = routeWithLimit(); - - route - .run(HttpRequest.create("/limit-5").withEntity("12345")) - .assertStatusCode(StatusCodes.OK) - .assertEntity("12345"); - } - - @Test - public void testEntitySizeLargerThanLimit() { - TestRoute route = routeWithLimit(); - - route - .run(HttpRequest.create("/limit-5").withEntity("1234567890")) - .assertStatusCode(StatusCodes.INTERNAL_SERVER_ERROR) - .assertEntity("There was an internal server error."); - } -} diff --git a/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/SchemeDirectivesTest.java b/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/SchemeDirectivesTest.java deleted file mode 100644 index e017d0a8b4..0000000000 --- a/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/SchemeDirectivesTest.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server.directives; - -import akka.http.javadsl.model.HttpRequest; -import akka.http.javadsl.model.StatusCodes; -import akka.http.javadsl.model.Uri; -import akka.http.javadsl.testkit.JUnitRouteTest; -import akka.http.javadsl.testkit.TestRoute; -import org.junit.Test; - -public class SchemeDirectivesTest extends JUnitRouteTest { - @Test - public void testSchemeFilter() { - TestRoute route = testRoute(scheme("http", () -> complete("OK!"))); - - route - .run(HttpRequest.create().withUri(Uri.create("http://example.org"))) - .assertStatusCode(StatusCodes.OK) - .assertEntity("OK!"); - - route - .run(HttpRequest.create().withUri(Uri.create("https://example.org"))) - .assertStatusCode(StatusCodes.BAD_REQUEST) - .assertEntity("Uri scheme not allowed, supported schemes: http"); - } - - @Test - public void testSchemeExtraction() { - TestRoute route = testRoute(extractScheme(this::complete)); - - route - .run(HttpRequest.create().withUri(Uri.create("http://example.org"))) - .assertStatusCode(StatusCodes.OK) - .assertEntity("http"); - - route - .run(HttpRequest.create().withUri(Uri.create("https://example.org"))) - .assertStatusCode(StatusCodes.OK) - .assertEntity("https"); - } - - -} diff --git a/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/SecurityDirectivesTest.java b/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/SecurityDirectivesTest.java deleted file mode 100644 index 659ac718bc..0000000000 --- a/akka-http-tests/src/test/java/akka/http/javadsl/server/directives/SecurityDirectivesTest.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server.directives; - -import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; -import akka.http.javadsl.model.StatusCodes; -import org.junit.Test; - -import scala.util.Left; -import scala.util.Right; -import akka.http.javadsl.server.*; -import akka.http.javadsl.model.HttpRequest; -import akka.http.javadsl.model.headers.Authorization; -import akka.http.javadsl.model.headers.BasicHttpCredentials; -import akka.http.javadsl.model.headers.HttpChallenge; -import akka.http.javadsl.testkit.*; - -import static akka.http.javadsl.server.PathMatchers.*; - -public class SecurityDirectivesTest extends JUnitRouteTest { - - // These authenticators don't have to be async; they're just written that way to test the API. - private CompletionStage> authenticateUser(Optional creds) { - return CompletableFuture.completedFuture( - creds.filter(c -> - c.identifier().equals("sina") && c.verify("1234") - ).map(c -> - "sina" - )); - } - - private CompletionStage> authenticateToken(Optional creds) { - System.out.println(creds); - - return CompletableFuture.completedFuture( - creds.filter(c -> - c.verify("myToken") - ).map(c -> - "myToken" - )); - } - - public Route securedRoute(String identifier) { - return complete("Identified as " + identifier + "!"); - } - - TestRoute route = - testRoute( - path("basicSecure", () -> - authenticateBasicAsync("test-realm", this::authenticateUser, this::securedRoute) - ), - path("oauthSecure", () -> - authenticateOAuth2Async("test-realm", this::authenticateToken, this::securedRoute) - ), - path(segment("authorize").slash(integerSegment()), (n) -> - authorize(() -> n == 1, () -> complete("authorized")) - ), - path(segment("authorizeAsync").slash(integerSegment()), (n) -> - authorizeAsync(() -> CompletableFuture.completedFuture(n == 1), () -> complete("authorized")) - ), - path(segment("authorizeWithRequestContext").slash(integerSegment()), (n) -> - authorizeWithRequestContext((ctx) -> n == 1, () -> complete("authorized")) - ), - path(segment("authorizeAsyncWithRequestContext").slash(integerSegment()), (n) -> - authorizeAsyncWithRequestContext((ctx) -> CompletableFuture.completedFuture(n == 1), () -> complete("authorized")) - ) - ); - - @Test - public void testCorrectUser() { - HttpRequest authenticatedRequest = - HttpRequest.GET("/basicSecure") - .addHeader(Authorization.basic("sina", "1234")); - - route.run(authenticatedRequest) - .assertStatusCode(StatusCodes.OK) - .assertEntity("Identified as sina!"); - } - - @Test - public void testCorrectToken() { - HttpRequest authenticatedRequest = - HttpRequest.GET("/oauthSecure") - .addHeader(Authorization.oauth2("myToken")); - - route.run(authenticatedRequest) - .assertStatusCode(StatusCodes.OK) - .assertEntity("Identified as myToken!"); - } - - @Test - public void testRejectAnonymousAccess() { - route.run(HttpRequest.GET("/basicSecure")) - .assertStatusCode(StatusCodes.UNAUTHORIZED) - .assertEntity("The resource requires authentication, which was not supplied with the request") - .assertHeaderExists("WWW-Authenticate", "Basic realm=\"test-realm\""); - } - - @Test - public void testRejectUnknownUser() { - HttpRequest authenticatedRequest = - HttpRequest.GET("/basicSecure") - .addHeader(Authorization.basic("joe", "0000")); - - route.run(authenticatedRequest) - .assertStatusCode(StatusCodes.UNAUTHORIZED) - .assertEntity("The supplied authentication is invalid"); - } - - @Test - public void testRejectWrongPassword() { - HttpRequest authenticatedRequest = - HttpRequest.GET("/basicSecure") - .addHeader(Authorization.basic("sina", "1235")); - - route.run(authenticatedRequest) - .assertStatusCode(StatusCodes.UNAUTHORIZED) - .assertEntity("The supplied authentication is invalid"); - } - - @Test - public void testAuthenticateOrRejectWithChallenge() { - TestRoute route = testRoute( - path("basicSecure", () -> - authenticateOrRejectWithChallenge(BasicHttpCredentials.class, cred -> { - if (cred.isPresent()) { - return CompletableFuture.completedFuture(Right.apply(cred.get().token())); - } else { - return CompletableFuture.completedFuture(Left.apply(HttpChallenge.create("Basic", "test-realm"))); - } - }, this::securedRoute) - ) - ); - - Authorization auth = Authorization.basic("sina", "1234"); - HttpRequest authenticatedRequest = - HttpRequest.GET("/basicSecure") - .addHeader(auth); - - route.run(authenticatedRequest) - .assertStatusCode(StatusCodes.OK) - .assertEntity("Identified as " + auth.credentials().token() + "!"); - } - - @Test - public void testAuthorize() { - route.run(HttpRequest.GET("/authorize/1")) - .assertStatusCode(StatusCodes.OK) - .assertEntity("authorized"); - - route.run(HttpRequest.GET("/authorize/0")) - .assertStatusCode(StatusCodes.FORBIDDEN); - } - - @Test - public void testAuthorizeAsync() { - route.run(HttpRequest.GET("/authorizeAsync/1")) - .assertStatusCode(StatusCodes.OK) - .assertEntity("authorized"); - - route.run(HttpRequest.GET("/authorizeAsync/0")) - .assertStatusCode(StatusCodes.FORBIDDEN); - } - - @Test - public void testAuthorizeWithRequestContext() { - route.run(HttpRequest.GET("/authorizeWithRequestContext/1")) - .assertStatusCode(StatusCodes.OK) - .assertEntity("authorized"); - - route.run(HttpRequest.GET("/authorizeWithRequestContext/0")) - .assertStatusCode(StatusCodes.FORBIDDEN); - } - - - @Test - public void testAuthorizeAsyncWithRequestContext() { - route.run(HttpRequest.GET("/authorizeAsyncWithRequestContext/1")) - .assertStatusCode(StatusCodes.OK) - .assertEntity("authorized"); - - route.run(HttpRequest.GET("/authorizeAsyncWithRequestContext/0")) - .assertStatusCode(StatusCodes.FORBIDDEN); - - } - -} diff --git a/akka-http-tests/src/test/java/akka/http/javadsl/server/examples/petstore/PetStoreAPITest.java b/akka-http-tests/src/test/java/akka/http/javadsl/server/examples/petstore/PetStoreAPITest.java deleted file mode 100644 index 6488824ba9..0000000000 --- a/akka-http-tests/src/test/java/akka/http/javadsl/server/examples/petstore/PetStoreAPITest.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server.examples.petstore; - -import akka.http.javadsl.marshallers.jackson.Jackson; -import akka.http.javadsl.model.HttpRequest; -import akka.http.javadsl.model.MediaTypes; -import akka.http.javadsl.model.StatusCodes; -import akka.http.javadsl.testkit.*; - -import static org.junit.Assert.*; - -import akka.http.javadsl.testkit.TestRoute; - -import org.junit.Test; - -import java.util.HashMap; -import java.util.Map; - -public class PetStoreAPITest extends JUnitRouteTest { - @Test - public void testGetPet() { - TestRouteResult response = createRoute().run(HttpRequest.GET("/pet/1")); - - response - .assertStatusCode(StatusCodes.OK) - .assertMediaType("application/json"); - - Pet pet = response.entity(Jackson.unmarshaller(Pet.class)); - assertEquals("cat", pet.getName()); - assertEquals(1, pet.getId()); - } - - @Test - public void testGetMissingPet() { - createRoute().run(HttpRequest.GET("/pet/999")) - .assertStatusCode(StatusCodes.NOT_FOUND); - } - - @Test - public void testPutPet() { - HttpRequest request = - HttpRequest.PUT("/pet/1") - .withEntity(MediaTypes.APPLICATION_JSON.toContentType(), "{\"id\": 1, \"name\": \"giraffe\"}"); - - TestRouteResult response = createRoute().run(request); - - response.assertStatusCode(StatusCodes.OK); - - Pet pet = response.entity(Jackson.unmarshaller(Pet.class)); - assertEquals("giraffe", pet.getName()); - assertEquals(1, pet.getId()); - } - - @Test - public void testDeletePet() { - Map data = createData(); - - HttpRequest request = HttpRequest.DELETE("/pet/0"); - - createRoute(data).run(request) - .assertStatusCode(StatusCodes.OK); - - // test actual deletion from data store - assertFalse(data.containsKey(0)); - } - - private TestRoute createRoute() { - return createRoute(createData()); - } - - private TestRoute createRoute(Map pets) { - return testRoute(PetStoreExample.appRoute(pets)); - } - - private Map createData() { - Map pets = new HashMap(); - Pet dog = new Pet(0, "dog"); - Pet cat = new Pet(1, "cat"); - pets.put(0, dog); - pets.put(1, cat); - - return pets; - } -} diff --git a/akka-http-tests/src/test/java/akka/http/javadsl/server/examples/simple/SimpleServerTest.java b/akka-http-tests/src/test/java/akka/http/javadsl/server/examples/simple/SimpleServerTest.java deleted file mode 100644 index 3ca43c73ba..0000000000 --- a/akka-http-tests/src/test/java/akka/http/javadsl/server/examples/simple/SimpleServerTest.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server.examples.simple; - -import akka.http.javadsl.model.HttpRequest; -import akka.http.javadsl.testkit.*; - -import org.junit.Test; - -public class SimpleServerTest extends JUnitRouteTest { - TestRoute route = testRoute(new SimpleServerApp().createRoute()); - - @Test - public void testAdd() { - TestRouteResult response = route.run(HttpRequest.GET("/add?x=42&y=23")); - - response - .assertStatusCode(200) - .assertEntity("42 + 23 = 65"); - } - - @Test - public void testMultiplyAsync() { - TestRouteResult response = route.run(HttpRequest.GET("/multiplyAsync/42/23")); - - response - .assertStatusCode(200) - .assertEntity("42 * 23 = 966"); - } - - @Test - public void testPostWithBody() { - TestRouteResult response = route.run(HttpRequest.POST("/hello").withEntity("John")); - - response - .assertStatusCode(200) - .assertEntity("Hello John!"); - } -} diff --git a/akka-http-tests/src/test/java/akka/http/javadsl/server/values/FormFieldsTest.java b/akka-http-tests/src/test/java/akka/http/javadsl/server/values/FormFieldsTest.java deleted file mode 100644 index df296552f2..0000000000 --- a/akka-http-tests/src/test/java/akka/http/javadsl/server/values/FormFieldsTest.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server.values; - -import org.junit.Test; - -import akka.http.javadsl.model.HttpCharsets; -import akka.http.javadsl.model.HttpRequest; -import akka.http.javadsl.model.MediaTypes; -import akka.http.javadsl.unmarshalling.StringUnmarshallers; -import akka.http.javadsl.testkit.JUnitRouteTest; -import akka.http.javadsl.testkit.TestRoute; -import akka.japi.Pair; - -public class FormFieldsTest extends JUnitRouteTest { - - private Pair param(String name, String value) { - return Pair.create(name, value); - } - @SafeVarargs - final private HttpRequest urlEncodedRequest(Pair... params) { - StringBuilder sb = new StringBuilder(); - boolean next = false; - for (Pair param: params) { - if (next) { - sb.append('&'); - } - next = true; - sb.append(param.first()); - sb.append('='); - sb.append(param.second()); - } - - return - HttpRequest.POST("/test") - .withEntity(MediaTypes.APPLICATION_X_WWW_FORM_URLENCODED.toContentType(HttpCharsets.UTF_8), sb.toString()); - } - private HttpRequest singleParameterUrlEncodedRequest(String name, String value) { - return urlEncodedRequest(param(name, value)); - } - - @Test - public void testStringFormFieldExtraction() { - TestRoute route = testRoute(formField("stringParam", value -> complete(value))); - - route - .run(HttpRequest.create().withUri("/abc")) - .assertStatusCode(400) - .assertEntity("Request is missing required form field 'stringParam'"); - - route - .run(singleParameterUrlEncodedRequest("stringParam", "john")) - .assertStatusCode(200) - .assertEntity("john"); - } - - @Test - public void testByteFormFieldExtraction() { - TestRoute route = testRoute(formField(StringUnmarshallers.BYTE, "byteParam", value -> complete(value.toString()))); - - route - .run(HttpRequest.create().withUri("/abc")) - .assertStatusCode(400) - .assertEntity("Request is missing required form field 'byteParam'"); - - route - .run(singleParameterUrlEncodedRequest("byteParam", "test")) - .assertStatusCode(400) - .assertEntity("The form field 'byteParam' was malformed:\n'test' is not a valid 8-bit signed integer value"); - - route - .run(singleParameterUrlEncodedRequest("byteParam", "1000")) - .assertStatusCode(400) - .assertEntity("The form field 'byteParam' was malformed:\n'1000' is not a valid 8-bit signed integer value"); - - route - .run(singleParameterUrlEncodedRequest("byteParam", "48")) - .assertStatusCode(200) - .assertEntity("48"); - } - - @Test - public void testOptionalIntFormFieldExtraction() { - TestRoute route = testRoute(formFieldOptional(StringUnmarshallers.INTEGER, "optionalIntParam", value -> complete(value.toString()))); - - route - .run(HttpRequest.create().withUri("/abc")) - .assertStatusCode(200) - .assertEntity("Optional.empty"); - - route - .run(singleParameterUrlEncodedRequest("optionalIntParam", "23")) - .assertStatusCode(200) - .assertEntity("Optional[23]"); - } - -} diff --git a/akka-http-tests/src/test/java/akka/http/javadsl/server/values/HeadersTest.java b/akka-http-tests/src/test/java/akka/http/javadsl/server/values/HeadersTest.java deleted file mode 100644 index 514597b3f8..0000000000 --- a/akka-http-tests/src/test/java/akka/http/javadsl/server/values/HeadersTest.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server.values; - -import com.typesafe.config.Config; -import com.typesafe.config.ConfigFactory; -import org.junit.Test; - -import akka.http.javadsl.testkit.JUnitRouteTest; -import akka.http.javadsl.testkit.TestRoute; - -import akka.http.javadsl.model.*; -import akka.http.javadsl.model.headers.Age; -import akka.http.javadsl.model.headers.RawHeader; - -public class HeadersTest extends JUnitRouteTest { - final RawHeader testHeaderInstance = RawHeader.create("X-Test-Header", "abcdef-test"); - final Age ageHeaderInstance = Age.create(1000); - - @Test - public void testValueByName() { - TestRoute route = testRoute(headerValueByName("X-Test-Header", value -> complete(value))); - - route - .run(HttpRequest.create()) - .assertStatusCode(400) - .assertEntity("Request is missing required HTTP header 'X-Test-Header'"); - - route - .run(HttpRequest.create().addHeader(testHeaderInstance)) - .assertStatusCode(200) - .assertEntity("abcdef-test"); - } - - @Test - public void testOptionalValueByName() { - TestRoute route = testRoute(optionalHeaderValueByName("X-Test-Header", value -> complete(value.toString()))); - - route - .run(HttpRequest.create()) - .assertStatusCode(200) - .assertEntity("Optional.empty"); - - route - .run(HttpRequest.create().addHeader(testHeaderInstance)) - .assertStatusCode(200) - .assertEntity("Optional[abcdef-test]"); - } - - @Test - public void testValueByClass() { - TestRoute route = testRoute(headerValueByType(Age.class, age -> complete(age.value()))); - - route - .run(HttpRequest.create()) - .assertStatusCode(400) - .assertEntity("Request is missing required HTTP header 'Age'"); - - route - .run(HttpRequest.create().addHeader(ageHeaderInstance)) - .assertStatusCode(200) - .assertEntity("1000"); - } - - @Test - public void testOptionalValueByClass() { - TestRoute route = testRoute(optionalHeaderValueByType(Age.class, age -> complete(age.toString()))); - - route - .run(HttpRequest.create()) - .assertStatusCode(200) - .assertEntity("Optional.empty"); - - route - .run(HttpRequest.create().addHeader(ageHeaderInstance)) - .assertStatusCode(200) - .assertEntity("Optional[Age: 1000]"); - } -} diff --git a/akka-http-tests/src/test/java/docs/http/javadsl/server/HandlerExampleDocTest.java b/akka-http-tests/src/test/java/docs/http/javadsl/server/HandlerExampleDocTest.java deleted file mode 100644 index 219c33007b..0000000000 --- a/akka-http-tests/src/test/java/docs/http/javadsl/server/HandlerExampleDocTest.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package docs.http.javadsl.server; - -import static akka.http.javadsl.server.PathMatchers.*; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; -import java.util.concurrent.Executor; -import java.util.function.BiFunction; -import java.util.function.Function; - -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.server.Route; -import akka.http.javadsl.unmarshalling.StringUnmarshallers; -import akka.http.javadsl.testkit.JUnitRouteTest; -import akka.http.javadsl.testkit.TestRoute; - -public class HandlerExampleDocTest extends JUnitRouteTest { - @Test - public void testSimpleHandler() { - //#simple-handler-example-full - class TestHandler extends akka.http.javadsl.server.AllDirectives { - //#simple-handler - Route handlerString = extractMethod(method -> - extractUri(uri -> - complete(String.format("This was a %s request to %s", method.name(), uri)) - ) - ); - - Route handlerResponse = extractMethod(method -> - extractUri(uri -> { - // with full control over the returned HttpResponse: - final HttpResponse response = HttpResponse.create() - .withEntity(String.format("Accepted %s request to %s", method.name(), uri)) - .withStatus(StatusCodes.ACCEPTED); - return complete(response); - }) - ); - //#simple-handler - - Route createRoute() { - return route( - get(() -> - handlerString - ), - post(() -> - path("abc", () -> - handlerResponse - ) - ) - ); - } - } - - // actual testing code - TestRoute r = testRoute(new TestHandler().createRoute()); - r.run(HttpRequest.GET("/test")) - .assertStatusCode(StatusCodes.OK) - .assertEntity("This was a GET request to http://example.com/test"); - - r.run(HttpRequest.POST("/test")) - .assertStatusCode(StatusCodes.NOT_FOUND); - - r.run(HttpRequest.POST("/abc")) - .assertStatusCode(StatusCodes.ACCEPTED) - .assertEntity("Accepted POST request to http://example.com/abc"); - //#simple-handler-example-full - } - - @Test - public void testCalculator() { - //#handler2-example-full - class TestHandler extends akka.http.javadsl.server.AllDirectives { - - final Route multiplyXAndYParam = - parameter(StringUnmarshallers.INTEGER, "x", x -> - parameter(StringUnmarshallers.INTEGER, "y", y -> - complete("x * y = " + (x * y)) - ) - ); - - final Route pathMultiply = - path(integerSegment().slash(integerSegment()), (x, y) -> - complete("x * y = " + (x * y)) - ); - - Route subtract(int x, int y) { - return complete("x - y = " + (x - y)); - } - - //#handler2 - - Route createRoute() { - return route( - get(() -> - pathPrefix("calculator", () -> route( - path("multiply", () -> - multiplyXAndYParam - ), - pathPrefix("path-multiply", () -> - pathMultiply - ), - // handle by lifting method - path(segment("subtract").slash(integerSegment()).slash(integerSegment()), this::subtract) - )) - ) - ); - } - } - - // actual testing code - TestRoute r = testRoute(new TestHandler().createRoute()); - r.run(HttpRequest.GET("/calculator/multiply?x=12&y=42")) - .assertStatusCode(StatusCodes.OK) - .assertEntity("x * y = 504"); - - r.run(HttpRequest.GET("/calculator/path-multiply/23/5")) - .assertStatusCode(StatusCodes.OK) - .assertEntity("x * y = 115"); - - r.run(HttpRequest.GET("/calculator/subtract/42/12")) - .assertStatusCode(StatusCodes.OK) - .assertEntity("x - y = 30"); - //#handler2-example-full - } - - @Test - public void testDeferredResultAsyncHandler() { - //#async-example-full - //#async-service-definition - class CalculatorService { - public CompletionStage multiply(final int x, final int y) { - return CompletableFuture.supplyAsync(() -> x * y); - } - - public CompletionStage add(final int x, final int y) { - return CompletableFuture.supplyAsync(() -> x + y); - } - } - //#async-service-definition - - class TestHandler extends akka.http.javadsl.server.AllDirectives { - - /** - * Returns a route that applies the (required) request parameters "x" and "y", as integers, to - * the inner function. - */ - Route paramXY(BiFunction inner) { - return - parameter(StringUnmarshallers.INTEGER, "x", x -> - parameter(StringUnmarshallers.INTEGER, "y", y -> - inner.apply(x, y) - ) - ); - } - - - //#async-handler-1 - // would probably be injected or passed at construction time in real code - CalculatorService calculatorService = new CalculatorService(); - - public CompletionStage multiplyAsync(Executor ctx, int x, int y) { - CompletionStage result = calculatorService.multiply(x, y); - return result.thenApplyAsync(product -> complete("x * y = " + product), ctx); - } - - Route multiplyAsyncRoute = - extractExecutionContext(ctx -> - path("multiply", () -> - paramXY((x, y) -> - onSuccess(() -> multiplyAsync(ctx, x, y), Function.identity()) - ) - ) - ); - //#async-handler-1 - - //#async-handler-2 - - public Route addAsync(int x, int y) { - CompletionStage result = calculatorService.add(x, y); - - return onSuccess(() -> result, sum -> complete("x + y = " + sum)); - } - - Route addAsyncRoute = - path("add", () -> - paramXY(this::addAsync) - ); - //#async-handler-2 - - Route createRoute() { - return route( - get(() -> - pathPrefix("calculator", () -> route( - multiplyAsyncRoute, - addAsyncRoute - )) - ) - ); - } - } - - // testing code - TestRoute r = testRoute(new TestHandler().createRoute()); - r.run(HttpRequest.GET("/calculator/multiply?x=12&y=42")) - .assertStatusCode(StatusCodes.OK) - .assertEntity("x * y = 504"); - - r.run(HttpRequest.GET("/calculator/add?x=23&y=5")) - .assertStatusCode(StatusCodes.OK) - .assertEntity("x + y = 28"); - //#async-example-full - } -} diff --git a/akka-http-tests/src/test/resources/dirWithLink/linked-dir b/akka-http-tests/src/test/resources/dirWithLink/linked-dir deleted file mode 120000 index 1ac44d7bd4..0000000000 --- a/akka-http-tests/src/test/resources/dirWithLink/linked-dir +++ /dev/null @@ -1 +0,0 @@ -../subDirectory \ No newline at end of file diff --git a/akka-http-tests/src/test/resources/reference.conf b/akka-http-tests/src/test/resources/reference.conf deleted file mode 100644 index 429ce1804e..0000000000 --- a/akka-http-tests/src/test/resources/reference.conf +++ /dev/null @@ -1,9 +0,0 @@ -akka { - loggers = ["akka.testkit.TestEventListener"] - actor { - serialize-creators = off - serialize-messages = off - default-dispatcher.throughput = 1 - } - stream.materializer.debug.fuzzing-mode = on -} \ No newline at end of file diff --git a/akka-http-tests/src/test/resources/sample.html b/akka-http-tests/src/test/resources/sample.html deleted file mode 100644 index 10dbdec8c5..0000000000 --- a/akka-http-tests/src/test/resources/sample.html +++ /dev/null @@ -1 +0,0 @@ -

Lorem ipsum!

\ No newline at end of file diff --git a/akka-http-tests/src/test/resources/sample.xyz b/akka-http-tests/src/test/resources/sample.xyz deleted file mode 100644 index ce42064770..0000000000 --- a/akka-http-tests/src/test/resources/sample.xyz +++ /dev/null @@ -1 +0,0 @@ -XyZ \ No newline at end of file diff --git a/akka-http-tests/src/test/resources/someDir/fileA.txt b/akka-http-tests/src/test/resources/someDir/fileA.txt deleted file mode 100644 index d800886d9c..0000000000 --- a/akka-http-tests/src/test/resources/someDir/fileA.txt +++ /dev/null @@ -1 +0,0 @@ -123 \ No newline at end of file diff --git a/akka-http-tests/src/test/resources/someDir/fileB.xml b/akka-http-tests/src/test/resources/someDir/fileB.xml deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/akka-http-tests/src/test/resources/someDir/sub/file.html b/akka-http-tests/src/test/resources/someDir/sub/file.html deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/akka-http-tests/src/test/resources/subDirectory/empty.pdf b/akka-http-tests/src/test/resources/subDirectory/empty.pdf deleted file mode 100644 index d800886d9c..0000000000 --- a/akka-http-tests/src/test/resources/subDirectory/empty.pdf +++ /dev/null @@ -1 +0,0 @@ -123 \ No newline at end of file diff --git a/akka-http-tests/src/test/resources/subDirectory/fileA.txt b/akka-http-tests/src/test/resources/subDirectory/fileA.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/akka-http-tests/src/test/resources/sübdir/sample späce.PDF b/akka-http-tests/src/test/resources/sübdir/sample späce.PDF deleted file mode 100644 index fdfab78488..0000000000 --- a/akka-http-tests/src/test/resources/sübdir/sample späce.PDF +++ /dev/null @@ -1 +0,0 @@ -This is PDF \ No newline at end of file diff --git a/akka-http-tests/src/test/scala/akka/http/javadsl/DirectivesConsistencySpec.scala b/akka-http-tests/src/test/scala/akka/http/javadsl/DirectivesConsistencySpec.scala deleted file mode 100644 index c4cbd5b53c..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/javadsl/DirectivesConsistencySpec.scala +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl - -import java.lang.reflect.{ Modifier, Method } - -import akka.http.javadsl.server.AllDirectives -import akka.http.javadsl.server.directives.CorrespondsTo -import org.scalatest.exceptions.TestPendingException -import org.scalatest.{ Matchers, WordSpec } -import scala.reflect.runtime.{ universe ⇒ ru } - -import scala.util.control.NoStackTrace - -class DirectivesConsistencySpec extends WordSpec with Matchers { - - val scalaDirectivesClazz = classOf[akka.http.scaladsl.server.Directives] - val javaDirectivesClazz = classOf[akka.http.javadsl.server.AllDirectives] - - val ignore = - Set("equals", "hashCode", "notify", "notifyAll", "wait", "toString", "getClass") ++ - Set("productArity", "canEqual", "productPrefix", "copy", "productIterator", "productElement", - "concat", "route") ++ // TODO this fails on jenkins but not locally, no idea why, disabling to get Java DSL in - // param extractions in ScalaDSL - Set("DoubleNumber", "HexIntNumber", "HexLongNumber", "IntNumber", "JavaUUID", "LongNumber", - "Neutral", "PathEnd", "Remaining", "Segment", "Segments", "Slash", "RemainingPath") // TODO do we cover these? - - def prepareDirectivesList(in: Array[Method]): List[Method] = { - in.toSet[Method] - .toList - .foldLeft[List[Method]](Nil) { - (l, s) ⇒ - { - val test = l find { _.getName.toLowerCase == s.getName.toLowerCase } - if (test.isEmpty) s :: l else l - } - } - .sortBy(_.getName) - .iterator - .filterNot(m ⇒ Modifier.isStatic(m.getModifiers)) - .filterNot(m ⇒ ignore(m.getName)) - .filterNot(m ⇒ m.getName.contains("$")) - .filterNot(m ⇒ m.getName.startsWith("_")) - .toList - } - - val scalaDirectives = { - prepareDirectivesList(scalaDirectivesClazz.getMethods) - } - val javaDirectives = { - prepareDirectivesList(javaDirectivesClazz.getMethods) - } - - val correspondingScalaMethods = { - val javaToScalaMappings = - for { - // using Scala annotations - Java annotations were magically not present in certain places... - d ← javaDirectives - if d.isAnnotationPresent(classOf[CorrespondsTo]) - annot = d.getAnnotation(classOf[CorrespondsTo]) - } yield d.getName → annot.value() - - Map(javaToScalaMappings.toList: _*) - } - - val correspondingJavaMethods = Map() ++ correspondingScalaMethods.map(_.swap) - - /** Left(@CorrespondsTo(...) or Right(normal name) */ - def correspondingScalaMethodName(m: Method): Either[String, String] = - correspondingScalaMethods.get(m.getName) match { - case Some(correspondent) ⇒ Left(correspondent) - case _ ⇒ Right(m.getName) - } - - /** Left(@CorrespondsTo(...) or Right(normal name) */ - def correspondingJavaMethodName(m: Method): Either[String, String] = - correspondingJavaMethods.get(m.getName) match { - case Some(correspondent) ⇒ Left(correspondent) - case _ ⇒ Right(m.getName) - } - - val allowMissing: Map[Class[_], Set[String]] = Map( - scalaDirectivesClazz → Set( - "route", "request", - "completeOK", // solved by raw complete() in Scala - "defaultDirectoryRenderer", "defaultContentTypeResolver" // solved by implicits in Scala - ), - javaDirectivesClazz → Set( - "as", - "instanceOf", - "pass", - - // TODO PENDING -> - "extractRequestContext", "nothingMatcher", "separateOnSlashes", - "textract", "tprovide", "withExecutionContext", "withRequestTimeoutResponse", - "withSettings", - "provide", "withMaterializer", "recoverRejectionsWith", - "mapSettings", "mapRequestContext", "mapInnerRoute", "mapRouteResultFuture", - "mapRouteResultWith", - "mapRouteResult", "handleWith", - "mapRouteResultWithPF", "mapRouteResultPF", - "route", "request", - "completeOrRecoverWith", "completeWith", - // TODO <- END OF PENDING - "parameters", "formFields", // since we can't do magnet-style "arbitrary arity" - - "authenticateOAuth2PF", "authenticateOAuth2PFAsync", - "authenticateBasicPF", "authenticateBasicPFAsync")) - - def assertHasMethod(c: Class[_], name: String, alternativeName: String): Unit = { - // include class name to get better error message - if (!allowMissing.getOrElse(c, Set.empty).exists(n ⇒ n == name || n == alternativeName)) { - val methods = c.getMethods.collect { case m if !ignore(m.getName) ⇒ c.getName + "." + m.getName } - - def originClazz = { - // look in the "opposite" class - // traversal is different in scala/java - in scala its traits, so we need to look at interfaces - // in hava we have a huge inheritance chain so we unfold it - c match { - case `javaDirectivesClazz` ⇒ - val all = scalaDirectivesClazz - (for { - i ← all.getInterfaces - m ← i.getDeclaredMethods - if m.getName == name || m.getName == alternativeName - } yield i).headOption - .map(_.getName) - .getOrElse(throw new Exception(s"Unable to locate method [$name] on source class $all")) - - case `scalaDirectivesClazz` ⇒ - val all = javaDirectivesClazz - - var is = List.empty[Class[_]] - var c: Class[_] = all - while (c != classOf[java.lang.Object]) { - is = c :: is - c = c.getSuperclass - } - - (for { - i ← is - m ← i.getDeclaredMethods - if m.getName == name || m.getName == alternativeName - } yield i).headOption - .map(_.getName) - .getOrElse(throw new Exception(s"Unable to locate method [$name] on source class $all")) - } - } - - if (methods.contains(c.getName + "." + name) && name == alternativeName) () - else if (methods.contains(c.getName + "." + alternativeName)) () - else throw new AssertionError(s"Method [$originClazz#$name] was not defined on class: ${c.getName}") with NoStackTrace - } else { - // allowed missing - we mark as pending, perhaps we'll want that method eventually - throw new TestPendingException - } - } - - "DSL Stats" should { - info("Scala Directives: ~" + scalaDirectives.map(_.getName).filterNot(ignore).size) - info("Java Directives: ~" + javaDirectives.map(_.getName).filterNot(ignore).size) - } - - "Directive aliases" should { - info("Aliases: ") - correspondingScalaMethods.foreach { case (k, v) ⇒ info(s" $k => $v") } - } - - "Consistency scaladsl -> javadsl" should { - for { - m ← scalaDirectives - name = m.getName - targetName = correspondingJavaMethodName(m) match { case Left(l) ⇒ l case Right(r) ⇒ r } - text = if (name == targetName) name else s"$name (alias: $targetName)" - } s"""define Scala directive [$text] for JavaDSL too""" in { - assertHasMethod(javaDirectivesClazz, name, targetName) - } - } - - "Consistency javadsl -> scaladsl" should { - for { - m ← javaDirectives - name = m.getName - targetName = correspondingScalaMethodName(m) match { case Left(l) ⇒ l case Right(r) ⇒ r } - text = if (name == targetName) name else s"$name (alias for: $targetName)" - } s"""define Java directive [$text] for ScalaDSL too""" in { - assertHasMethod(scalaDirectivesClazz, name, targetName) - } - } - -} diff --git a/akka-http-tests/src/test/scala/akka/http/javadsl/server/directives/SampleCustomHeader.scala b/akka-http-tests/src/test/scala/akka/http/javadsl/server/directives/SampleCustomHeader.scala deleted file mode 100644 index e03359cd3e..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/javadsl/server/directives/SampleCustomHeader.scala +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.javadsl.server.directives - -import akka.http.scaladsl.model.headers.{ ModeledCustomHeader, ModeledCustomHeaderCompanion } - -import scala.util.{ Success, Try } - -// no support for modeled headers in the Java DSL yet, so this has to live here - -object SampleCustomHeader extends ModeledCustomHeaderCompanion[SampleCustomHeader] { - override def name: String = "X-Sample-Custom-Header" - override def parse(value: String): Try[SampleCustomHeader] = Success(new SampleCustomHeader(value)) -} - -class SampleCustomHeader(val value: String) extends ModeledCustomHeader[SampleCustomHeader] { - override def companion: ModeledCustomHeaderCompanion[SampleCustomHeader] = SampleCustomHeader - override def renderInResponses(): Boolean = true - override def renderInRequests(): Boolean = true -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/CustomMediaTypesSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/CustomMediaTypesSpec.scala deleted file mode 100644 index f89a8734fc..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/CustomMediaTypesSpec.scala +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl - -import akka.http.scaladsl.client.RequestBuilding -import akka.http.scaladsl.model.MediaType.WithFixedCharset -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers._ -import akka.http.scaladsl.server.{ Directives, RoutingSpec } -import akka.http.scaladsl.settings.{ ParserSettings, ServerSettings } -import akka.stream.ActorMaterializer -import akka.testkit.AkkaSpec -import akka.util.ByteString -import org.scalatest.concurrent.ScalaFutures -import scala.concurrent.duration._ - -class CustomMediaTypesSpec extends AkkaSpec with ScalaFutures - with Directives with RequestBuilding { - - implicit val mat = ActorMaterializer() - - "Http" should { - "allow registering custom media type" in { - import system.dispatcher - val (_, host, port) = TestUtils.temporaryServerHostnameAndPort() - - //#application-custom - - // similarily in Java: `akka.http.javadsl.settings.[...]` - import akka.http.scaladsl.settings.ParserSettings - import akka.http.scaladsl.settings.ServerSettings - - // define custom media type: - val utf8 = HttpCharsets.`UTF-8` - val `application/custom`: WithFixedCharset = - MediaType.customWithFixedCharset("application", "custom", utf8) - - // add custom media type to parser settings: - val parserSettings = ParserSettings(system).withCustomMediaTypes(`application/custom`) - val serverSettings = ServerSettings(system).withParserSettings(parserSettings) - - val routes = extractRequest { r ⇒ - complete(r.entity.contentType.toString + " = " + r.entity.contentType.getClass) - } - val binding = Http().bindAndHandle(routes, host, port, settings = serverSettings) - //#application-custom - - val request = Get(s"http://$host:$port/").withEntity(HttpEntity(`application/custom`, "~~example~=~value~~")) - val response = Http().singleRequest(request).futureValue - - response.status should ===(StatusCodes.OK) - val responseBody = response.toStrict(1.second).futureValue.entity.dataBytes.runFold(ByteString.empty)(_ ++ _).futureValue.utf8String - responseBody should ===("application/custom = class akka.http.scaladsl.model.ContentType$WithFixedCharset") - } - } -} - diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/FormDataSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/FormDataSpec.scala deleted file mode 100644 index 2cec74a209..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/FormDataSpec.scala +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl - -import scala.concurrent.duration._ -import org.scalatest.concurrent.ScalaFutures -import akka.actor.ActorSystem -import akka.stream.ActorMaterializer -import akka.http.scaladsl.unmarshalling.Unmarshal -import akka.http.scaladsl.marshalling.Marshal -import akka.http.scaladsl.model._ -import scala.concurrent.Await -import akka.testkit.AkkaSpec - -class FormDataSpec extends AkkaSpec { - implicit val materializer = ActorMaterializer() - import system.dispatcher - - val formData = FormData(Map("surname" → "Smith", "age" → "42")) - - "The FormData infrastructure" should { - "properly round-trip the fields of www-urlencoded forms" in { - Marshal(formData).to[HttpEntity] - .flatMap(Unmarshal(_).to[FormData]).futureValue shouldEqual formData - } - - "properly marshal www-urlencoded forms containing special chars" in { - Marshal(FormData(Map("name" → "Smith&Wesson"))).to[HttpEntity] - .flatMap(Unmarshal(_).to[String]).futureValue shouldEqual "name=Smith%26Wesson" - - Marshal(FormData(Map("name" → "Smith+Wesson; hopefully!"))).to[HttpEntity] - .flatMap(Unmarshal(_).to[String]).futureValue shouldEqual "name=Smith%2BWesson%3B+hopefully%21" - } - } - -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/Main.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/Main.scala deleted file mode 100644 index 4a9bf71c9e..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/Main.scala +++ /dev/null @@ -1,5 +0,0 @@ -package akka.http.scaladsl - -class Main { - -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/TestSingleRequest.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/TestSingleRequest.scala deleted file mode 100644 index c7b4c0eb62..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/TestSingleRequest.scala +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl - -import akka.http.scaladsl.model.{ HttpRequest, StatusCodes, HttpResponse } -import akka.util.ByteString -import com.typesafe.config.{ ConfigFactory, Config } -import akka.actor.ActorSystem -import akka.stream._ -import scala.concurrent.Await -import scala.concurrent.duration._ -import scala.io.StdIn - -object TestSingleRequest extends App { - val testConf: Config = ConfigFactory.parseString(""" - akka.loglevel = INFO - akka.log-dead-letters = off - akka.stream.materializer.debug.fuzzing-mode = off - """) - implicit val system = ActorSystem("ServerTest", testConf) - implicit val materializer = ActorMaterializer() - import system.dispatcher - - val url = StdIn.readLine("url? ") - - val x = Http().singleRequest(HttpRequest(uri = url)) - - val res = Await.result(x, 10.seconds) - - val response = res.entity.dataBytes.runFold(ByteString.empty)(_ ++ _).map(_.utf8String) - - println(" ------------ RESPONSE ------------") - println(Await.result(response, 10.seconds)) - println(" -------- END OF RESPONSE ---------") - - system.terminate() -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/TestUtils.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/TestUtils.scala deleted file mode 100644 index e362387207..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/TestUtils.scala +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl - -import java.io.{ FileOutputStream, File } -import java.net.InetSocketAddress -import java.nio.channels.ServerSocketChannel - -object TestUtils { - def writeAllText(text: String, file: File): Unit = { - val fos = new FileOutputStream(file) - try { - fos.write(text.getBytes("UTF-8")) - } finally fos.close() - } - - // TODO duplicated code from akka-http-core-tests - def temporaryServerAddress(interface: String = "127.0.0.1"): InetSocketAddress = { - val serverSocket = ServerSocketChannel.open() - try { - serverSocket.socket.bind(new InetSocketAddress(interface, 0)) - val port = serverSocket.socket.getLocalPort - new InetSocketAddress(interface, port) - } finally serverSocket.close() - } - - // TODO duplicated code from akka-http-core-tests - def temporaryServerHostnameAndPort(interface: String = "127.0.0.1"): (InetSocketAddress, String, Int) = { - val socketAddress = temporaryServerAddress(interface) - (socketAddress, socketAddress.getHostName, socketAddress.getPort) - } -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/coding/CodecSpecSupport.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/coding/CodecSpecSupport.scala deleted file mode 100644 index 0fc5cac662..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/coding/CodecSpecSupport.scala +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.coding - -import scala.concurrent.duration._ -import org.scalatest.{ Suite, BeforeAndAfterAll, Matchers } -import akka.actor.ActorSystem -import akka.stream.ActorMaterializer -import akka.util.ByteString -import scala.concurrent.Await - -trait CodecSpecSupport extends Matchers with BeforeAndAfterAll { self: Suite ⇒ - - def readAs(string: String, charset: String = "UTF8") = equal(string).matcher[String] compose { (_: ByteString).decodeString(charset) } - def hexDump(bytes: ByteString) = bytes.map("%02x".format(_)).mkString - def fromHexDump(dump: String) = dump.grouped(2).toArray.map(chars ⇒ Integer.parseInt(new String(chars), 16).toByte) - - def printBytes(i: Int, id: String) = { - def byte(i: Int) = (i & 0xFF).toHexString - println(id + ": " + byte(i) + ":" + byte(i >> 8) + ":" + byte(i >> 16) + ":" + byte(i >> 24)) - i - } - - lazy val emptyTextBytes = ByteString(emptyText, "UTF8") - lazy val smallTextBytes = ByteString(smallText, "UTF8") - lazy val largeTextBytes = ByteString(largeText, "UTF8") - - val emptyText = "" - val smallText = "Yeah!" - val largeText = - """Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore -magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd -gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing -elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos -et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor -sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et -dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd -gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. - -Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat -nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis -dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh -euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. - -Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo -consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu -feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit -augue duis dolore te feugait nulla facilisi. - -Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim -assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet -dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit -lobortis nisl ut aliquip ex ea commodo consequat. - -Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat -nulla facilisis. - -At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem -ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt -ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. -Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, -consetetur sadipscing elitr, At accusam aliquyam diam diam dolore dolores duo eirmod eos erat, et nonumy sed tempor et -et invidunt justo labore Stet clita ea et gubergren, kasd magna no rebum. sanctus sea sed takimata ut vero voluptua. -est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor -invidunt ut labore et dolore magna aliquyam erat. - -Consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam -voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus -est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy e""".replace("\r\n", "\n") - - implicit val system = ActorSystem(getClass.getSimpleName) - implicit val materializer = ActorMaterializer() - - override def afterAll() = { - Await.result(system.terminate(), 10.seconds) - } -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/coding/CoderSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/coding/CoderSpec.scala deleted file mode 100644 index 1663f081e1..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/coding/CoderSpec.scala +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.coding - -import java.io.{ OutputStream, InputStream, ByteArrayInputStream, ByteArrayOutputStream } -import java.util -import java.util.zip.DataFormatException -import akka.NotUsed - -import scala.annotation.tailrec -import scala.concurrent.duration._ -import scala.concurrent.Await -import scala.concurrent.ExecutionContext.Implicits.global -import java.util.concurrent.ThreadLocalRandom -import scala.util.control.NoStackTrace -import org.scalatest.{ Inspectors, WordSpec } -import akka.util.ByteString -import akka.stream.scaladsl.{ Sink, Source } -import akka.http.scaladsl.model.{ HttpEntity, HttpRequest } -import akka.http.scaladsl.model.HttpMethods._ -import akka.http.impl.util._ - -abstract class CoderSpec extends WordSpec with CodecSpecSupport with Inspectors { - protected def Coder: Coder with StreamDecoder - protected def newDecodedInputStream(underlying: InputStream): InputStream - protected def newEncodedOutputStream(underlying: OutputStream): OutputStream - - case object AllDataAllowed extends Exception with NoStackTrace - protected def corruptInputCheck: Boolean = true - - def extraTests(): Unit = {} - - s"The ${Coder.encoding.value} codec" should { - "produce valid data on immediate finish" in { - streamDecode(Coder.newCompressor.finish()) should readAs(emptyText) - } - "properly encode an empty string" in { - streamDecode(ourEncode(emptyTextBytes)) should readAs(emptyText) - } - "properly decode an empty string" in { - ourDecode(streamEncode(emptyTextBytes)) should readAs(emptyText) - } - "properly round-trip encode/decode an empty string" in { - ourDecode(ourEncode(emptyTextBytes)) should readAs(emptyText) - } - "properly encode a small string" in { - streamDecode(ourEncode(smallTextBytes)) should readAs(smallText) - } - "properly decode a small string" in { - ourDecode(streamEncode(smallTextBytes)) should readAs(smallText) - } - "properly round-trip encode/decode a small string" in { - ourDecode(ourEncode(smallTextBytes)) should readAs(smallText) - } - "properly encode a large string" in { - streamDecode(ourEncode(largeTextBytes)) should readAs(largeText) - } - "properly decode a large string" in { - ourDecode(streamEncode(largeTextBytes)) should readAs(largeText) - } - "properly round-trip encode/decode a large string" in { - ourDecode(ourEncode(largeTextBytes)) should readAs(largeText) - } - "properly round-trip encode/decode an HttpRequest" in { - val request = HttpRequest(POST, entity = HttpEntity(largeText)) - Coder.decode(Coder.encode(request)).toStrict(3.seconds).awaitResult(3.seconds) should equal(request) - } - - if (corruptInputCheck) { - "throw an error on corrupt input" in { - (the[RuntimeException] thrownBy { - ourDecode(corruptContent) - }).getCause should be(a[DataFormatException]) - } - } - - "not throw an error if a subsequent block is corrupt" in { - pending // FIXME: should we read as long as possible and only then report an error, that seems somewhat arbitrary - ourDecode(Seq(encode("Hello,"), encode(" dear "), corruptContent).join) should readAs("Hello, dear ") - } - "decompress in very small chunks" in { - val compressed = encode("Hello") - - decodeChunks(Source(Vector(compressed.take(10), compressed.drop(10)))) should readAs("Hello") - } - "support chunked round-trip encoding/decoding" in { - val chunks = largeTextBytes.grouped(512).toVector - val comp = Coder.newCompressor - val compressedChunks = chunks.map { chunk ⇒ comp.compressAndFlush(chunk) } :+ comp.finish() - val uncompressed = decodeFromIterator(() ⇒ compressedChunks.iterator) - - uncompressed should readAs(largeText) - } - "works for any split in prefix + suffix" in { - val compressed = streamEncode(smallTextBytes) - def tryWithPrefixOfSize(prefixSize: Int): Unit = { - val prefix = compressed.take(prefixSize) - val suffix = compressed.drop(prefixSize) - - decodeChunks(Source(prefix :: suffix :: Nil)) should readAs(smallText) - } - (0 to compressed.size).foreach(tryWithPrefixOfSize) - } - "works for chunked compressed data of sizes just above 1024" in { - val comp = Coder.newCompressor - val inputBytes = ByteString("""{"baseServiceURL":"http://www.acme.com","endpoints":{"assetSearchURL":"/search","showsURL":"/shows","mediaContainerDetailURL":"/container","featuredTapeURL":"/tape","assetDetailURL":"/asset","moviesURL":"/movies","recentlyAddedURL":"/recent","topicsURL":"/topics","scheduleURL":"/schedule"},"urls":{"aboutAweURL":"www.foobar.com"},"channelName":"Cool Stuff","networkId":"netId","slotProfile":"slot_1","brag":{"launchesUntilPrompt":10,"daysUntilPrompt":5,"launchesUntilReminder":5,"daysUntilReminder":2},"feedbackEmailAddress":"feedback@acme.com","feedbackEmailSubject":"Commends from User","splashSponsor":[],"adProvider":{"adProviderProfile":"","adProviderProfileAndroid":"","adProviderNetworkID":0,"adProviderSiteSectionNetworkID":0,"adProviderVideoAssetNetworkID":0,"adProviderSiteSectionCustomID":{},"adProviderServerURL":"","adProviderLiveVideoAssetID":""},"update":[{"forPlatform":"ios","store":{"iTunes":"www.something.com"},"minVer":"1.2.3","notificationVer":"1.2.5"},{"forPlatform":"android","store":{"amazon":"www.something.com","play":"www.something.com"},"minVer":"1.2.3","notificationVer":"1.2.5"}],"tvRatingPolicies":[{"type":"sometype","imageKey":"tv_rating_small","durationMS":15000,"precedence":1},{"type":"someothertype","imageKey":"tv_rating_big","durationMS":15000,"precedence":2}],"exts":{"adConfig":{"globals":{"#{adNetworkID}":"2620","#{ssid}":"usa_tveapp"},"iPad":{"showlist":{"adMobAdUnitID":"/2620/usa_tveapp_ipad/shows","adSize":[{"#{height}":90,"#{width}":728}]},"launch":{"doubleClickCallbackURL":"http://pubads.g.doubleclick.net/gampad/ad?iu=/2620/usa_tveapp_ipad&sz=1x1&t=&c=#{doubleclickrandom}"},"watchwithshowtile":{"adMobAdUnitID":"/2620/usa_tveapp_ipad/watchwithshowtile","adSize":[{"#{height}":120,"#{width}":240}]},"showpage":{"doubleClickCallbackURL":"http://pubads.g.doubleclick.net/gampad/ad?iu=/2620/usa_tveapp_ipad/shows/#{SHOW_NAME}&sz=1x1&t=&c=#{doubleclickrandom}"}},"iPadRetina":{"showlist":{"adMobAdUnitID":"/2620/usa_tveapp_ipad/shows","adSize":[{"#{height}":90,"#{width}":728}]},"launch":{"doubleClickCallbackURL":"http://pubads.g.doubleclick.net/gampad/ad?iu=/2620/usa_tveapp_ipad&sz=1x1&t=&c=#{doubleclickrandom}"},"watchwithshowtile":{"adMobAdUnitID":"/2620/usa_tveapp_ipad/watchwithshowtile","adSize":[{"#{height}":120,"#{width}":240}]},"showpage":{"doubleClickCallbackURL":"http://pubads.g.doubleclick.net/gampad/ad?iu=/2620/usa_tveapp_ipad/shows/#{SHOW_NAME}&sz=1x1&t=&c=#{doubleclickrandom}"}},"iPhone":{"home":{"adMobAdUnitID":"/2620/usa_tveapp_iphone/home","adSize":[{"#{height}":50,"#{width}":300},{"#{height}":50,"#{width}":320}]},"showlist":{"adMobAdUnitID":"/2620/usa_tveapp_iphone/shows","adSize":[{"#{height}":50,"#{width}":300},{"#{height}":50,"#{width}":320}]},"episodepage":{"adMobAdUnitID":"/2620/usa_tveapp_iphone/shows/#{SHOW_NAME}","adSize":[{"#{height}":50,"#{width}":300},{"#{height}":50,"#{width}":320}]},"launch":{"doubleClickCallbackURL":"http://pubads.g.doubleclick.net/gampad/ad?iu=/2620/usa_tveapp_iphone&sz=1x1&t=&c=#{doubleclickrandom}"},"showpage":{"doubleClickCallbackURL":"http://pubads.g.doubleclick.net/gampad/ad?iu=/2620/usa_tveapp_iphone/shows/#{SHOW_NAME}&sz=1x1&t=&c=#{doubleclickrandom}"}},"iPhoneRetina":{"home":{"adMobAdUnitID":"/2620/usa_tveapp_iphone/home","adSize":[{"#{height}":50,"#{width}":300},{"#{height}":50,"#{width}":320}]},"showlist":{"adMobAdUnitID":"/2620/usa_tveapp_iphone/shows","adSize":[{"#{height}":50,"#{width}":300},{"#{height}":50,"#{width}":320}]},"episodepage":{"adMobAdUnitID":"/2620/usa_tveapp_iphone/shows/#{SHOW_NAME}","adSize":[{"#{height}":50,"#{width}":300},{"#{height}":50,"#{width}":320}]},"launch":{"doubleClickCallbackURL":"http://pubads.g.doubleclick.net/gampad/ad?iu=/2620/usa_tveapp_iphone&sz=1x1&t=&c=#{doubleclickrandom}"},"showpage":{"doubleClickCallbackURL":"http://pubads.g.doubleclick.net/gampad/ad?iu=/2620/usa_tveapp_iphone/shows/#{SHOW_NAME}&sz=1x1&t=&c=#{doubleclickrandom}"}},"Tablet":{"home":{"adMobAdUnitID":"/2620/usa_tveapp_androidtab/home","adSize":[{"#{height}":90,"#{width}":728},{"#{height}":50,"#{width}":320},{"#{height}":50,"#{width}":300}]},"showlist":{"adMobAdUnitID":"/2620/usa_tveapp_androidtab/shows","adSize":[{"#{height}":90,"#{width}":728},{"#{height}":50,"#{width}":320},{"#{height}":50,"#{width}":300}]},"episodepage":{"adMobAdUnitID":"/2620/usa_tveapp_androidtab/shows/#{SHOW_NAME}","adSize":[{"#{height}":90,"#{width}":728},{"#{height}":50,"#{width}":320},{"#{height}":50,"#{width}":300}]},"launch":{"doubleClickCallbackURL":"http://pubads.g.doubleclick.net/gampad/ad?iu=/2620/usa_tveapp_androidtab&sz=1x1&t=&c=#{doubleclickrandom}"},"showpage":{"doubleClickCallbackURL":"http://pubads.g.doubleclick.net/gampad/ad?iu=/2620/usa_tveapp_androidtab/shows/#{SHOW_NAME}&sz=1x1&t=&c=#{doubleclickrandom}"}},"TabletHD":{"home":{"adMobAdUnitID":"/2620/usa_tveapp_androidtab/home","adSize":[{"#{height}":90,"#{width}":728},{"#{height}":50,"#{width}":320},{"#{height}":50,"#{width}":300}]},"showlist":{"adMobAdUnitID":"/2620/usa_tveapp_androidtab/shows","adSize":[{"#{height}":90,"#{width}":728},{"#{height}":50,"#{width}":320},{"#{height}":50,"#{width}":300}]},"episodepage":{"adMobAdUnitID":"/2620/usa_tveapp_androidtab/shows/#{SHOW_NAME}","adSize":[{"#{height}":90,"#{width}":728},{"#{height}":50,"#{width}":320},{"#{height}":50,"#{width}":300}]},"launch":{"doubleClickCallbackURL":"http://pubads.g.doubleclick.net/gampad/ad?iu=/2620/usa_tveapp_androidtab&sz=1x1&t=&c=#{doubleclickrandom}"},"showpage":{"doubleClickCallbackURL":"http://pubads.g.doubleclick.net/gampad/ad?iu=/2620/usa_tveapp_androidtab/shows/#{SHOW_NAME}&sz=1x1&t=&c=#{doubleclickrandom}"}},"Phone":{"home":{"adMobAdUnitID":"/2620/usa_tveapp_android/home","adSize":[{"#{height}":50,"#{width}":300},{"#{height}":50,"#{width}":320}]},"showlist":{"adMobAdUnitID":"/2620/usa_tveapp_android/shows","adSize":[{"#{height}":50,"#{width}":300},{"#{height}":50,"#{width}":320}]},"episodepage":{"adMobAdUnitID":"/2620/usa_tveapp_android/shows/#{SHOW_NAME}","adSize":[{"#{height}":50,"#{width}":300},{"#{height}":50,"#{width}":320}]},"launch":{"doubleClickCallbackURL":"http://pubads.g.doubleclick.net/gampad/ad?iu=/2620/usa_tveapp_android&sz=1x1&t=&c=#{doubleclickrandom}"},"showpage":{"doubleClickCallbackURL":"http://pubads.g.doubleclick.net/gampad/ad?iu=/2620/usa_tveapp_android/shows/#{SHOW_NAME}&sz=1x1&t=&c=#{doubleclickrandom}"}},"PhoneHD":{"home":{"adMobAdUnitID":"/2620/usa_tveapp_android/home","adSize":[{"#{height}":50,"#{width}":300},{"#{height}":50,"#{width}":320}]},"showlist":{"adMobAdUnitID":"/2620/usa_tveapp_android/shows","adSize":[{"#{height}":50,"#{width}":300},{"#{height}":50,"#{width}":320}]},"episodepage":{"adMobAdUnitID":"/2620/usa_tveapp_android/shows/#{SHOW_NAME}","adSize":[{"#{height}":50,"#{width}":300},{"#{height}":50,"#{width}":320}]},"launch":{"doubleClickCallbackURL":"http://pubads.g.doubleclick.net/gampad/ad?iu=/2620/usa_tveapp_android&sz=1x1&t=&c=#{doubleclickrandom}"},"showpage":{"doubleClickCallbackURL":"http://pubads.g.doubleclick.net/gampad/ad?iu=/2620/usa_tveapp_android/shows/#{SHOW_NAME}&sz=1x1&t=&c=#{doubleclickrandom}"}}}}}""", "utf8") - val compressed = comp.compressAndFinish(inputBytes) - - ourDecode(compressed) should equal(inputBytes) - } - - "shouldn't produce huge ByteStrings for some input" in { - val array = new Array[Byte](10) // FIXME - util.Arrays.fill(array, 1.toByte) - val compressed = streamEncode(ByteString(array)) - val limit = 10000 - val resultBs = - Source.single(compressed) - .via(Coder.withMaxBytesPerChunk(limit).decoderFlow) - .limit(4200).runWith(Sink.seq) - .awaitResult(3.seconds) - - forAll(resultBs) { bs ⇒ - bs.length should be < limit - bs.forall(_ == 1) should equal(true) - } - } - - "be able to decode chunk-by-chunk (depending on input chunks)" in { - val minLength = 100 - val maxLength = 1000 - val numElements = 1000 - - val random = ThreadLocalRandom.current() - val sizes = Seq.fill(numElements)(random.nextInt(minLength, maxLength)) - def createByteString(size: Int): ByteString = - ByteString(Array.fill(size)(1.toByte)) - - val sizesAfterRoundtrip = - Source.fromIterator(() ⇒ sizes.toIterator.map(createByteString)) - .via(Coder.encoderFlow) - .via(Coder.decoderFlow) - .runFold(Seq.empty[Int])(_ :+ _.size) - - sizes shouldEqual sizesAfterRoundtrip.awaitResult(3.seconds) - } - - extraTests() - } - - def encode(s: String) = ourEncode(ByteString(s, "UTF8")) - def ourEncode(bytes: ByteString): ByteString = Coder.encode(bytes) - def ourDecode(bytes: ByteString): ByteString = Coder.decode(bytes).awaitResult(3.seconds) - - lazy val corruptContent = { - val content = encode(largeText).toArray - content(14) = 26.toByte - ByteString(content) - } - - def streamEncode(bytes: ByteString): ByteString = { - val output = new ByteArrayOutputStream() - val gos = newEncodedOutputStream(output); gos.write(bytes.toArray); gos.close() - ByteString(output.toByteArray) - } - - def streamDecode(bytes: ByteString): ByteString = { - val output = new ByteArrayOutputStream() - val input = newDecodedInputStream(new ByteArrayInputStream(bytes.toArray)) - - val buffer = new Array[Byte](500) - @tailrec def copy(from: InputStream, to: OutputStream): Unit = { - val read = from.read(buffer) - if (read >= 0) { - to.write(buffer, 0, read) - copy(from, to) - } - } - - copy(input, output) - ByteString(output.toByteArray) - } - - def decodeChunks(input: Source[ByteString, NotUsed]): ByteString = - input.via(Coder.decoderFlow).join.awaitResult(3.seconds) - - def decodeFromIterator(iterator: () ⇒ Iterator[ByteString]): ByteString = - Await.result(Source.fromIterator(iterator).via(Coder.decoderFlow).join, 3.seconds) -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/coding/DecoderSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/coding/DecoderSpec.scala deleted file mode 100644 index a4a052b15b..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/coding/DecoderSpec.scala +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.coding - -import akka.stream.{ Attributes, FlowShape } -import akka.stream.impl.fusing.GraphStages.SimpleLinearGraphStage - -import scala.concurrent.duration._ -import org.scalatest.WordSpec -import akka.util.ByteString -import akka.stream.stage._ -import akka.http.scaladsl.model._ -import akka.http.impl.util._ -import headers._ -import HttpMethods.POST - -class DecoderSpec extends WordSpec with CodecSpecSupport { - - "A Decoder" should { - "not transform the message if it doesn't contain a Content-Encoding header" in { - val request = HttpRequest(POST, entity = HttpEntity(smallText)) - DummyDecoder.decode(request) shouldEqual request - } - "correctly transform the message if it contains a Content-Encoding header" in { - val request = HttpRequest(POST, entity = HttpEntity(smallText), headers = List(`Content-Encoding`(DummyDecoder.encoding))) - val decoded = DummyDecoder.decode(request) - decoded.headers shouldEqual Nil - decoded.entity.toStrict(3.seconds).awaitResult(3.seconds) shouldEqual HttpEntity(dummyDecompress(smallText)) - } - } - - def dummyDecompress(s: String): String = dummyDecompress(ByteString(s, "UTF8")).decodeString("UTF8") - def dummyDecompress(bytes: ByteString): ByteString = DummyDecoder.decode(bytes).awaitResult(3.seconds) - - case object DummyDecoder extends StreamDecoder { - val encoding = HttpEncodings.compress - - override def newDecompressorStage(maxBytesPerChunk: Int): () ⇒ GraphStage[FlowShape[ByteString, ByteString]] = - () ⇒ new SimpleLinearGraphStage[ByteString] { - override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = new GraphStageLogic(shape) { - setHandler(in, new InHandler { - override def onPush(): Unit = push(out, grab(in) ++ ByteString("compressed")) - }) - setHandler(out, new OutHandler { - override def onPull(): Unit = pull(in) - }) - } - } - } - -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/coding/DeflateSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/coding/DeflateSpec.scala deleted file mode 100644 index 70161affad..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/coding/DeflateSpec.scala +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.coding - -import akka.util.ByteString - -import java.io.{ InputStream, OutputStream } -import java.util.zip._ - -class DeflateSpec extends CoderSpec { - protected def Coder: Coder with StreamDecoder = Deflate - - protected def newDecodedInputStream(underlying: InputStream): InputStream = - new InflaterInputStream(underlying) - - protected def newEncodedOutputStream(underlying: OutputStream): OutputStream = - new DeflaterOutputStream(underlying) - - override def extraTests(): Unit = { - "throw early if header is corrupt" in { - (the[RuntimeException] thrownBy { - ourDecode(ByteString(0, 1, 2, 3, 4)) - }).getCause should be(a[DataFormatException]) - } - } -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/coding/EncoderSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/coding/EncoderSpec.scala deleted file mode 100644 index 08dd1a8e6a..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/coding/EncoderSpec.scala +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.coding - -import akka.util.ByteString -import org.scalatest.WordSpec -import akka.http.scaladsl.model._ -import headers._ -import HttpMethods.POST -import scala.concurrent.duration._ -import akka.http.impl.util._ - -class EncoderSpec extends WordSpec with CodecSpecSupport { - - "An Encoder" should { - "not transform the message if messageFilter returns false" in { - val request = HttpRequest(POST, entity = HttpEntity(smallText.getBytes("UTF8"))) - DummyEncoder.encode(request) shouldEqual request - } - "correctly transform the HttpMessage if messageFilter returns true" in { - val request = HttpRequest(POST, entity = HttpEntity(smallText)) - val encoded = DummyEncoder.encode(request) - encoded.headers shouldEqual List(`Content-Encoding`(DummyEncoder.encoding)) - encoded.entity.toStrict(3.seconds).awaitResult(3.seconds) shouldEqual HttpEntity(dummyCompress(smallText)) - } - } - - def dummyCompress(s: String): String = dummyCompress(ByteString(s, "UTF8")).utf8String - def dummyCompress(bytes: ByteString): ByteString = DummyCompressor.compressAndFinish(bytes) - - case object DummyEncoder extends Encoder { - val messageFilter = Encoder.DefaultFilter - val encoding = HttpEncodings.compress - def newCompressor = DummyCompressor - } - - case object DummyCompressor extends Compressor { - def compress(input: ByteString) = input ++ ByteString("compressed") - def flush() = ByteString.empty - def finish() = ByteString.empty - - def compressAndFlush(input: ByteString): ByteString = compress(input) - def compressAndFinish(input: ByteString): ByteString = compress(input) - } -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/coding/GzipSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/coding/GzipSpec.scala deleted file mode 100644 index 61f28c4dfc..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/coding/GzipSpec.scala +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.coding - -import akka.http.impl.util._ - -import java.io.{ InputStream, OutputStream } -import java.util.zip.{ ZipException, GZIPInputStream, GZIPOutputStream } - -import akka.util.ByteString - -class GzipSpec extends CoderSpec { - protected def Coder: Coder with StreamDecoder = Gzip - - protected def newDecodedInputStream(underlying: InputStream): InputStream = - new GZIPInputStream(underlying) - - protected def newEncodedOutputStream(underlying: OutputStream): OutputStream = - new GZIPOutputStream(underlying) - - override def extraTests(): Unit = { - "decode concatenated compressions" in { - ourDecode(Seq(encode("Hello, "), encode("dear "), encode("User!")).join) should readAs("Hello, dear User!") - } - "provide a better compression ratio than the standard Gzip/Gunzip streams" in { - ourEncode(largeTextBytes).length should be < streamEncode(largeTextBytes).length - } - "throw an error on truncated input" in { - val ex = the[RuntimeException] thrownBy ourDecode(streamEncode(smallTextBytes).dropRight(5)) - ex.getCause.getMessage should equal("Truncated GZIP stream") - } - "throw an error if compressed data is just missing the trailer at the end" in { - def brokenCompress(payload: String) = Gzip.newCompressor.compress(ByteString(payload, "UTF-8")) - val ex = the[RuntimeException] thrownBy ourDecode(brokenCompress("abcdefghijkl")) - ex.getCause.getMessage should equal("Truncated GZIP stream") - } - "throw early if header is corrupt" in { - val cause = (the[RuntimeException] thrownBy ourDecode(ByteString(0, 1, 2, 3, 4))).getCause - cause should (be(a[ZipException]) and have message "Not in GZIP format") - } - } -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/coding/NoCodingSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/coding/NoCodingSpec.scala deleted file mode 100644 index 1a3e2c80d1..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/coding/NoCodingSpec.scala +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.coding - -import java.io.{ OutputStream, InputStream } - -class NoCodingSpec extends CoderSpec { - protected def Coder: Coder with StreamDecoder = NoCoding - - override protected def corruptInputCheck = false - - protected def newEncodedOutputStream(underlying: OutputStream): OutputStream = underlying - protected def newDecodedInputStream(underlying: InputStream): InputStream = underlying -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/marshallers/JsonSupportSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/marshallers/JsonSupportSpec.scala deleted file mode 100644 index 5c513fb8a0..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/marshallers/JsonSupportSpec.scala +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.marshallers - -import akka.http.scaladsl.marshalling.ToEntityMarshaller -import akka.http.scaladsl.model.{ HttpCharsets, HttpEntity, MediaTypes } -import akka.http.scaladsl.testkit.ScalatestRouteTest -import akka.http.scaladsl.unmarshalling.FromEntityUnmarshaller -import akka.http.impl.util._ -import org.scalatest.{ Matchers, WordSpec } - -case class Employee(fname: String, name: String, age: Int, id: Long, boardMember: Boolean) { - require(!boardMember || age > 40, "Board members must be older than 40") -} - -object Employee { - val simple = Employee("Frank", "Smith", 42, 12345, false) - val json = """{"fname":"Frank","name":"Smith","age":42,"id":12345,"boardMember":false}""" - - val utf8 = Employee("Fränk", "Çmi√", 42, 12345, false) - val utf8json = - """{ - | "fname": "Fränk", - | "name": "Çmi√", - | "age": 42, - | "id": 12345, - | "boardMember": false - |}""".stripMargin.getBytes(HttpCharsets.`UTF-8`.nioCharset) - - val illegalEmployeeJson = """{"fname":"Little Boy","name":"Smith","age":7,"id":12345,"boardMember":true}""" -} - -/** Common infrastructure needed for several json support subprojects */ -abstract class JsonSupportSpec extends WordSpec with Matchers with ScalatestRouteTest { - require(getClass.getSimpleName.endsWith("Spec")) - // assuming that the classname ends with "Spec" - def name: String = getClass.getSimpleName.dropRight(4) - implicit def marshaller: ToEntityMarshaller[Employee] - implicit def unmarshaller: FromEntityUnmarshaller[Employee] - - "The " + name should { - "provide unmarshalling support for a case class" in { - HttpEntity(MediaTypes.`application/json`, Employee.json) should unmarshalToValue(Employee.simple) - } - "provide marshalling support for a case class" in { - val marshalled = marshal(Employee.simple) - - marshalled.data.utf8String shouldEqual - """{ - | "age": 42, - | "boardMember": false, - | "fname": "Frank", - | "id": 12345, - | "name": "Smith" - |}""".stripMarginWithNewline("\n") - } - "use UTF-8 as the default charset for JSON source decoding" in { - HttpEntity(MediaTypes.`application/json`, Employee.utf8json) should unmarshalToValue(Employee.utf8) - } - "provide proper error messages for requirement errors" in { - val result = unmarshal(HttpEntity(MediaTypes.`application/json`, Employee.illegalEmployeeJson)) - - result.isFailure shouldEqual true - val ex = result.failed.get - ex.getMessage shouldEqual "requirement failed: Board members must be older than 40" - } - } -} \ No newline at end of file diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/marshallers/sprayjson/SprayJsonSupportSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/marshallers/sprayjson/SprayJsonSupportSpec.scala deleted file mode 100644 index cb29175692..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/marshallers/sprayjson/SprayJsonSupportSpec.scala +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.marshallers.sprayjson - -import java.lang.StringBuilder - -import akka.http.scaladsl.marshallers.{ JsonSupportSpec, Employee } -import akka.http.scaladsl.marshalling.ToEntityMarshaller -import akka.http.scaladsl.unmarshalling.FromEntityUnmarshaller -import spray.json.{ JsValue, PrettyPrinter, JsonPrinter, DefaultJsonProtocol } - -import scala.collection.immutable.ListMap - -class SprayJsonSupportSpec extends JsonSupportSpec { - object EmployeeJsonProtocol extends DefaultJsonProtocol { - implicit val employeeFormat = jsonFormat5(Employee.apply) - } - import EmployeeJsonProtocol._ - - implicit val orderedFieldPrint: JsonPrinter = new PrettyPrinter { - override protected def printObject(members: Map[String, JsValue], sb: StringBuilder, indent: Int): Unit = - super.printObject(ListMap(members.toSeq.sortBy(_._1): _*), sb, indent) - } - - implicit def marshaller: ToEntityMarshaller[Employee] = SprayJsonSupport.sprayJsonMarshaller[Employee] - implicit def unmarshaller: FromEntityUnmarshaller[Employee] = SprayJsonSupport.sprayJsonUnmarshaller[Employee] -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/marshallers/xml/ScalaXmlSupportSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/marshallers/xml/ScalaXmlSupportSpec.scala deleted file mode 100644 index b802f84ef9..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/marshallers/xml/ScalaXmlSupportSpec.scala +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.marshallers.xml - -import java.io.File - -import akka.http.scaladsl.TestUtils -import org.xml.sax.SAXParseException -import scala.xml.NodeSeq -import scala.concurrent.{ Future, Await } -import scala.concurrent.duration._ -import org.scalatest.{ Inside, FreeSpec, Matchers } -import akka.util.ByteString -import akka.http.scaladsl.testkit.ScalatestRouteTest -import akka.http.scaladsl.unmarshalling.{ Unmarshaller, Unmarshal } -import akka.http.scaladsl.model._ -import MediaTypes._ - -class ScalaXmlSupportSpec extends FreeSpec with Matchers with ScalatestRouteTest with Inside { - import ScalaXmlSupport._ - - "NodeSeqMarshaller should" - { - "marshal xml snippets to `text/xml` content in UTF-8" in { - marshal(Ha“llo) shouldEqual - HttpEntity(ContentTypes.`text/xml(UTF-8)`, "Ha“llo") - } - "unmarshal `text/xml` content in UTF-8 to NodeSeqs" in { - Unmarshal(HttpEntity(ContentTypes.`text/xml(UTF-8)`, "Hällö")).to[NodeSeq].map(_.text) should evaluateTo("Hällö") - } - "reject `application/octet-stream`" in { - Unmarshal(HttpEntity(`application/octet-stream`, ByteString("Hällö"))).to[NodeSeq].map(_.text) should - haveFailedWith(Unmarshaller.UnsupportedContentTypeException(nodeSeqContentTypeRanges: _*)) - } - - "don't be vulnerable to XXE attacks" - { - "parse XML bodies without loading in a related schema" in { - withTempFile("I shouldn't be there!") { f ⇒ - val xml = s""" - | - | ]>hello&xxe;""".stripMargin - - shouldHaveFailedWithSAXParseException(Unmarshal(HttpEntity(ContentTypes.`text/xml(UTF-8)`, xml)).to[NodeSeq]) - } - } - "parse XML bodies without loading in a related schema from a parameter" in { - withTempFile("I shouldnt be there!") { generalEntityFile ⇒ - withTempFile { - s""" - |">""".stripMargin - } { parameterEntityFile ⇒ - val xml = s""" - | - | %xpe; - | %pe; - | ]>hello&xxe;""".stripMargin - shouldHaveFailedWithSAXParseException(Unmarshal(HttpEntity(ContentTypes.`text/xml(UTF-8)`, xml)).to[NodeSeq]) - } - } - } - "gracefully fail when there are too many nested entities" in { - val nested = for (x ← 1 to 30) yield "" - val xml = - s""" - | - | - | ${nested.mkString("\n")} - | ]> - | &laugh30;""".stripMargin - - shouldHaveFailedWithSAXParseException(Unmarshal(HttpEntity(ContentTypes.`text/xml(UTF-8)`, xml)).to[NodeSeq]) - } - "gracefully fail when an entity expands to be very large" in { - val as = "a" * 50000 - val entities = "&a;" * 50000 - val xml = s""" - | - | ]> - | $entities""".stripMargin - shouldHaveFailedWithSAXParseException(Unmarshal(HttpEntity(ContentTypes.`text/xml(UTF-8)`, xml)).to[NodeSeq]) - } - } - } - - def shouldHaveFailedWithSAXParseException(result: Future[NodeSeq]) = - inside(Await.result(result.failed, 1.second)) { - case _: SAXParseException ⇒ - } - - def withTempFile[T](content: String)(f: File ⇒ T): T = { - val file = File.createTempFile("xxe", ".txt") - try { - TestUtils.writeAllText(content, file) - f(file) - } finally { - file.delete() - } - } -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/marshalling/ContentNegotiationGivenResponseCodeSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/marshalling/ContentNegotiationGivenResponseCodeSpec.scala deleted file mode 100644 index 22e0c35a12..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/marshalling/ContentNegotiationGivenResponseCodeSpec.scala +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2009-2016 Typesafe Inc. - */ - -package akka.http.scaladsl.marshalling - -import akka.http.scaladsl.model.StatusCodes._ -import akka.http.scaladsl.model.headers.Accept -import akka.http.scaladsl.model.{ ContentTypes, MediaRanges } -import akka.http.scaladsl.server.{ Route, RoutingSpec } - -class ContentNegotiationGivenResponseCodeSpec extends RoutingSpec { - - val routes = { - pathPrefix(Segment) { mode ⇒ - complete { - mode match { - case "200-text" ⇒ OK → "ok" - case "201-text" ⇒ Created → "created" - case "400-text" ⇒ BadRequest → "bad-request" - } - } - } - } - - "Return NotAcceptable for" should { - "200 OK response, when entity not available in Accept-ed MediaRange" in { - val request = Post("/200-text").addHeader(Accept(MediaRanges.`application/*`)) - - request ~> Route.seal(routes) ~> check { - status should ===(NotAcceptable) - entityAs[String] should include("text/plain") - } - } - - "201 Created response, when entity not available in Accept-ed MediaRange" in { - val request = Post("/201-text").addHeader(Accept(MediaRanges.`application/*`)) - request ~> Route.seal(routes) ~> check { - status should ===(NotAcceptable) - entityAs[String] should include("text/plain") - } - } - } - - "Allow not explicitly Accept-ed content type to be returned if response code is non-2xx" should { - "400 BadRequest response, when entity not available in Accept-ed MediaRange" in { - val request = Post("/400-text").addHeader(Accept(MediaRanges.`application/*`)) - request ~> Route.seal(routes) ~> check { - status should ===(BadRequest) - contentType should ===(ContentTypes.`text/plain(UTF-8)`) - entityAs[String] should include("bad-request") - } - } - } - -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/marshalling/ContentNegotiationSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/marshalling/ContentNegotiationSpec.scala deleted file mode 100644 index 95110ec915..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/marshalling/ContentNegotiationSpec.scala +++ /dev/null @@ -1,181 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.marshalling - -import scala.concurrent.Await -import scala.concurrent.duration._ -import akka.http.scaladsl.server.ContentNegotiator.Alternative -import akka.util.ByteString -import org.scalatest.{ Matchers, FreeSpec } -import akka.http.scaladsl.util.FastFuture._ -import akka.http.scaladsl.model._ -import akka.http.impl.util._ -import MediaTypes._ -import HttpCharsets._ - -class ContentNegotiationSpec extends FreeSpec with Matchers { - "Content Negotiation should work properly for requests with header(s)" - { - - "(without headers)" test { accept ⇒ - accept(`text/plain` withCharset `UTF-16`) should select(`text/plain` withCharset `UTF-16`) - accept(`text/plain`, `text/html`) should select(`text/plain` withCharset `UTF-8`) - accept(`text/html`, `text/plain`) should select(`text/html` withCharset `UTF-8`) - } - - "Accept: */*" test { accept ⇒ - accept(`text/plain`) should select(`text/plain` withCharset `UTF-8`) - accept(`text/plain` withCharset `UTF-16`) should select(`text/plain` withCharset `UTF-16`) - } - - "Accept: */*;q=.8" test { accept ⇒ - accept(`text/plain`) should select(`text/plain` withCharset `UTF-8`) - accept(`text/plain` withCharset `UTF-16`) should select(`text/plain` withCharset `UTF-16`) - } - - "Accept: text/*" test { accept ⇒ - accept(`text/plain`) should select(`text/plain` withCharset `UTF-8`) - accept(`text/xml` withCharset `UTF-16`) should select(`text/xml` withCharset `UTF-16`) - accept(`audio/ogg`) should reject - } - - "Accept: text/*;q=.8" test { accept ⇒ - accept(`text/plain`) should select(`text/plain` withCharset `UTF-8`) - accept(`text/xml` withCharset `UTF-16`) should select(`text/xml` withCharset `UTF-16`) - accept(`audio/ogg`) should reject - } - - "Accept: text/*;q=0" test { accept ⇒ - accept(`text/plain`) should reject - accept(`text/xml` withCharset `UTF-16`) should reject - accept(`audio/ogg`) should reject - } - - "Accept: text/*, application/json;q=0.8, text/plain;q=0.5" test { accept ⇒ - accept(`text/plain`, `application/json`) should select(`application/json`) - } - - "Accept-Charset: UTF-16" test { accept ⇒ - accept(`text/plain`) should select(`text/plain` withCharset `UTF-16`) - accept(`text/plain` withCharset `UTF-8`) should reject - } - - "manually created Accept-Charset: UTF-16" in testHeaders(headers.`Accept-Charset`(Vector(HttpCharsets.`UTF-16`.toRange))) { accept ⇒ - accept(`text/plain`) should select(`text/plain` withCharset `UTF-16`) - accept(`text/plain` withCharset `UTF-8`) should reject - } - - "Accept-Charset: UTF-16, UTF-8" test { accept ⇒ - accept(`text/plain`) should select(`text/plain` withCharset `UTF-16`) - accept(`text/plain` withCharset `UTF-8`) should select(`text/plain` withCharset `UTF-8`) - } - - "Accept-Charset: UTF-8;q=.2, UTF-16" test { accept ⇒ - accept(`text/plain`) should select(`text/plain` withCharset `UTF-16`) - accept(`text/plain` withCharset `UTF-8`) should select(`text/plain` withCharset `UTF-8`) - } - - "Accept-Charset: ISO-8859-1;q=.2" test { accept ⇒ - accept(`text/plain`) should select(`text/plain` withCharset `ISO-8859-1`) - accept(`text/plain` withCharset `UTF-8`) should reject - } - - "Accept-Charset: latin1;q=.1, UTF-8;q=.2" test { accept ⇒ - accept(`text/plain`) should select(`text/plain` withCharset `UTF-8`) - accept(`text/plain` withCharset `UTF-8`) should select(`text/plain` withCharset `UTF-8`) - } - - "Accept-Charset: *" test { accept ⇒ - accept(`text/plain`) should select(`text/plain` withCharset `UTF-8`) - accept(`text/plain` withCharset `UTF-16`) should select(`text/plain` withCharset `UTF-16`) - } - - "Accept-Charset: *;q=0" test { accept ⇒ - accept(`text/plain`) should reject - accept(`text/plain` withCharset `UTF-16`) should reject - } - - "Accept-Charset: *;q=0.1" test { accept ⇒ - accept(`text/plain`) should select(`text/plain` withCharset `UTF-8`) - accept(`image/gif`) should select(`image/gif`) - } - - "Accept-Charset: us;q=0.1,*;q=0" test { accept ⇒ - accept(`text/plain`) should select(`text/plain` withCharset `US-ASCII`) - accept(`text/plain` withCharset `UTF-8`) should reject - } - - "Accept-Charset: UTF-8, *;q=0.8, us;q=0.1" test { accept ⇒ - accept( - `text/plain` withCharset `US-ASCII`, - `text/plain` withCharset `ISO-8859-1`) should select(`text/plain` withCharset `ISO-8859-1`) - } - - "Accept: text/xml, text/html;q=.5" test { accept ⇒ - accept(`text/plain`) should reject - accept(`text/xml`) should select(`text/xml` withCharset `UTF-8`) - accept(`text/html`) should select(`text/html` withCharset `UTF-8`) - accept(`text/html`, `text/xml`) should select(`text/xml` withCharset `UTF-8`) - accept(`text/xml`, `text/html`) should select(`text/xml` withCharset `UTF-8`) - accept(`text/plain`, `text/xml`) should select(`text/xml` withCharset `UTF-8`) - accept(`text/plain`, `text/html`) should select(`text/html` withCharset `UTF-8`) - } - - """Accept: text/html, text/plain;q=0.8, application/*;q=.5, *;q= .2 - |Accept-Charset: UTF-16""" test { accept ⇒ - accept(`text/plain`, `text/html`, `audio/ogg`) should select(`text/html` withCharset `UTF-16`) - accept(`text/plain`, `text/html` withCharset `UTF-8`, `audio/ogg`) should select(`text/plain` withCharset `UTF-16`) - accept(`audio/ogg`, `application/javascript`, `text/plain` withCharset `UTF-8`) should select(`application/javascript` withCharset `UTF-16`) - accept(`image/gif`, `application/javascript`) should select(`application/javascript` withCharset `UTF-16`) - accept(`image/gif`, `audio/ogg`) should select(`image/gif`) - } - - "Accept: text/xml, text/plain" test { accept ⇒ - accept(`text/plain` withCharset `UTF-16`) should select(`text/plain` withCharset `UTF-16`) - accept(`text/plain`, `text/xml`) should select(`text/plain` withCharset `UTF-8`) - accept(`text/xml`, `text/plain`) should select(`text/xml` withCharset `UTF-8`) - } - } - - def testHeaders[U](headers: HttpHeader*)(body: ((Alternative*) ⇒ Option[ContentType]) ⇒ U): U = { - val request = HttpRequest(headers = headers.toVector) - body { alternatives ⇒ - import scala.concurrent.ExecutionContext.Implicits.global - - // creates a pseudo marshaller for X, that applies for all the given content types - trait X - object X extends X - implicit val marshallers: ToEntityMarshaller[X] = - Marshaller.oneOf(alternatives map { - case Alternative.ContentType(ct) ⇒ Marshaller.withFixedContentType(ct)((s: X) ⇒ HttpEntity(ct, ByteString("The X"))) - case Alternative.MediaType(mt) ⇒ Marshaller.withOpenCharset(mt)((s: X, cs) ⇒ HttpEntity(mt withCharset cs, "The X")) - }: _*) - - Await.result(Marshal(X).toResponseFor(request) - .fast.map(response ⇒ Some(response.entity.contentType)) - .fast.recover { case _: Marshal.UnacceptableResponseContentTypeException ⇒ None }, 1.second) - } - } - - def reject = equal(None) - def select(contentType: ContentType) = equal(Some(contentType)) - - implicit class AddStringToIn(example: String) { - def test(body: ((Alternative*) ⇒ Option[ContentType]) ⇒ Unit): Unit = example in { - val headers = - if (example != "(without headers)") { - example.stripMarginWithNewline("\n").split('\n').toList map { rawHeader ⇒ - val Array(name, value) = rawHeader.split(':') - HttpHeader.parse(name.trim, value) match { - case HttpHeader.ParsingResult.Ok(header, Nil) ⇒ header - case result ⇒ fail(result.errors.head.formatPretty) - } - } - } else Nil - - testHeaders(headers: _*)(accept ⇒ body(accept)) - } - } -} - diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/marshalling/MarshallingSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/marshalling/MarshallingSpec.scala deleted file mode 100644 index fb8426b77f..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/marshalling/MarshallingSpec.scala +++ /dev/null @@ -1,171 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.marshalling - -import scala.collection.immutable.ListMap -import org.scalatest.{ BeforeAndAfterAll, FreeSpec, Matchers } -import akka.util.ByteString -import akka.actor.ActorSystem -import akka.stream.ActorMaterializer -import akka.stream.scaladsl.Source -import akka.http.scaladsl.testkit.MarshallingTestUtils -import akka.http.scaladsl.marshallers.xml.ScalaXmlSupport._ -import akka.http.impl.util._ -import akka.http.scaladsl.model._ -import headers._ -import HttpCharsets._ -import MediaTypes._ - -class MarshallingSpec extends FreeSpec with Matchers with BeforeAndAfterAll with MultipartMarshallers with MarshallingTestUtils { - implicit val system = ActorSystem(getClass.getSimpleName) - implicit val materializer = ActorMaterializer() - import system.dispatcher - - "The PredefinedToEntityMarshallers" - { - "StringMarshaller should marshal strings to `text/plain` content in UTF-8" in { - marshal("Ha“llo") shouldEqual HttpEntity("Ha“llo") - } - "DoneMarshaller should enable marshalling of akka.Done" in { - marshal(akka.Done) shouldEqual HttpEntity("") - } - "CharArrayMarshaller should marshal char arrays to `text/plain` content in UTF-8" in { - marshal("Ha“llo".toCharArray) shouldEqual HttpEntity("Ha“llo") - } - "FormDataMarshaller should marshal FormData instances to application/x-www-form-urlencoded content" in { - marshal(FormData(Map("name" → "Bob", "pass" → "hällo", "admin" → ""))) shouldEqual - HttpEntity(`application/x-www-form-urlencoded` withCharset `UTF-8`, "name=Bob&pass=h%C3%A4llo&admin=") - } - } - - "The PredefinedToResponseMarshallers" - { - "fromStatusCode should properly marshal entities that are not supposed to have a body" in { - marshalToResponse(StatusCodes.NoContent) shouldEqual HttpResponse(StatusCodes.NoContent, entity = HttpEntity.Empty) - } - "fromStatusCode should properly marshal entities that contain pre-defined content" in { - marshalToResponse(StatusCodes.EnhanceYourCalm) shouldEqual - HttpResponse(StatusCodes.EnhanceYourCalm, entity = HttpEntity(StatusCodes.EnhanceYourCalm.defaultMessage)) - } - "fromStatusCodeAndHeadersAndValue should properly marshal entities that are not supposed to have a body" in { - marshalToResponse((StatusCodes.NoContent, "This Content was intentionally left blank.")) shouldEqual - HttpResponse(StatusCodes.NoContent, entity = HttpEntity.Empty) - } - "fromStatusCodeAndHeadersAndValue should properly marshal entities that contain pre-defined content" in { - marshalToResponse((StatusCodes.EnhanceYourCalm, "Patience, young padawan!")) shouldEqual - HttpResponse(StatusCodes.EnhanceYourCalm, entity = HttpEntity("Patience, young padawan!")) - } - } - - "The GenericMarshallers" - { - "optionMarshaller should enable marshalling of Option[T]" in { - - marshal(Some("Ha“llo")) shouldEqual HttpEntity("Ha“llo") - marshal(None: Option[String]) shouldEqual HttpEntity.Empty - } - "eitherMarshaller should enable marshalling of Either[A, B]" in { - marshal[Either[Array[Char], String]](Right("right")) shouldEqual HttpEntity("right") - marshal[Either[Array[Char], String]](Left("left".toCharArray)) shouldEqual HttpEntity("left") - } - } - - "The MultipartMarshallers" - { - "multipartMarshaller should correctly marshal multipart content with" - { - "one empty part" in { - marshal(Multipart.General(`multipart/mixed`, Multipart.General.BodyPart.Strict(""))) shouldEqual HttpEntity( - contentType = `multipart/mixed` withBoundary randomBoundary withCharset `UTF-8`, - string = s"""--$randomBoundary - |Content-Type: text/plain; charset=UTF-8 - | - | - |--$randomBoundary--""".stripMarginWithNewline("\r\n")) - } - "one non-empty part" in { - marshal(Multipart.General(`multipart/alternative`, Multipart.General.BodyPart.Strict( - entity = HttpEntity(ContentTypes.`text/plain(UTF-8)`, "test@there.com"), - headers = `Content-Disposition`(ContentDispositionTypes.`form-data`, Map("name" → "email")) :: Nil))) shouldEqual - HttpEntity( - contentType = `multipart/alternative` withBoundary randomBoundary withCharset `UTF-8`, - string = s"""--$randomBoundary - |Content-Type: text/plain; charset=UTF-8 - |Content-Disposition: form-data; name=email - | - |test@there.com - |--$randomBoundary--""".stripMarginWithNewline("\r\n")) - } - "two different parts" in { - marshal(Multipart.General( - `multipart/related`, - Multipart.General.BodyPart.Strict(HttpEntity(`text/plain` withCharset `US-ASCII`, "first part, with a trailing linebreak\r\n")), - Multipart.General.BodyPart.Strict( - HttpEntity(`application/octet-stream`, ByteString("filecontent")), - RawHeader("Content-Transfer-Encoding", "binary") :: Nil))) shouldEqual - HttpEntity( - contentType = `multipart/related` withBoundary randomBoundary withCharset `UTF-8`, - string = s"""--$randomBoundary - |Content-Type: text/plain; charset=US-ASCII - | - |first part, with a trailing linebreak - | - |--$randomBoundary - |Content-Type: application/octet-stream - |Content-Transfer-Encoding: binary - | - |filecontent - |--$randomBoundary--""".stripMarginWithNewline("\r\n")) - } - } - - "multipartFormDataMarshaller should correctly marshal 'multipart/form-data' content with" - { - "two fields" in { - marshal(Multipart.FormData(ListMap( - "surname" → HttpEntity("Mike"), - "age" → marshal(42)))) shouldEqual - HttpEntity( - contentType = `multipart/form-data` withBoundary randomBoundary withCharset `UTF-8`, - string = s"""--$randomBoundary - |Content-Type: text/plain; charset=UTF-8 - |Content-Disposition: form-data; name=surname - | - |Mike - |--$randomBoundary - |Content-Type: text/xml; charset=UTF-8 - |Content-Disposition: form-data; name=age - | - |42 - |--$randomBoundary--""".stripMarginWithNewline("\r\n")) - } - - "two fields having a custom `Content-Disposition`" in { - marshal(Multipart.FormData(Source(List( - Multipart.FormData.BodyPart("attachment[0]", HttpEntity(`text/csv` withCharset `UTF-8`, "name,age\r\n\"John Doe\",20\r\n"), - Map("filename" → "attachment.csv")), - Multipart.FormData.BodyPart("attachment[1]", HttpEntity("naice!".getBytes), - Map("filename" → "attachment2.csv"), List(RawHeader("Content-Transfer-Encoding", "binary"))))))) shouldEqual - HttpEntity( - contentType = `multipart/form-data` withBoundary randomBoundary withCharset `UTF-8`, - string = s"""--$randomBoundary - |Content-Type: text/csv; charset=UTF-8 - |Content-Disposition: form-data; filename=attachment.csv; name="attachment[0]" - | - |name,age - |"John Doe",20 - | - |--$randomBoundary - |Content-Type: application/octet-stream - |Content-Disposition: form-data; filename=attachment2.csv; name="attachment[1]" - |Content-Transfer-Encoding: binary - | - |naice! - |--$randomBoundary--""".stripMarginWithNewline("\r\n")) - } - } - } - - override def afterAll() = system.terminate() - - protected class FixedRandom extends java.util.Random { - override def nextBytes(array: Array[Byte]): Unit = "my-stable-boundary".getBytes("UTF-8").copyToArray(array) - } - override protected val multipartBoundaryRandom = new FixedRandom // fix for stable value -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/BasicRouteSpecs.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/BasicRouteSpecs.scala deleted file mode 100644 index 3e24e1dab9..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/BasicRouteSpecs.scala +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server - -import akka.http.scaladsl.model -import model.HttpMethods._ -import model.StatusCodes -import akka.testkit.EventFilter - -class BasicRouteSpecs extends RoutingSpec { - - "routes created by the concatenation operator '~'" should { - "yield the first sub route if it succeeded" in { - Get() ~> { - get { complete("first") } ~ get { complete("second") } - } ~> check { responseAs[String] shouldEqual "first" } - } - "yield the second sub route if the first did not succeed" in { - Get() ~> { - post { complete("first") } ~ get { complete("second") } - } ~> check { responseAs[String] shouldEqual "second" } - } - "collect rejections from both sub routes" in { - Delete() ~> { - get { completeOk } ~ put { completeOk } - } ~> check { rejections shouldEqual Seq(MethodRejection(GET), MethodRejection(PUT)) } - } - "clear rejections that have already been 'overcome' by previous directives" in { - Put() ~> { - put { parameter('yeah) { echoComplete } } ~ - get { completeOk } - } ~> check { rejection shouldEqual MissingQueryParamRejection("yeah") } - } - } - - "routes created by the 'sequence' directive" should { - "reject with zero arguments, since no routes were matched" in { - Get() ~> { - concat() - } ~> check { rejections shouldEqual Seq() } - } - "yield the first sub route if it succeeded" in { - Get() ~> { - concat( - get { complete("first") }, - get { complete("second") }) - } ~> check { responseAs[String] shouldEqual "first" } - } - "yield the second sub route if the first did not succeed" in { - Get() ~> { - concat( - post { complete("first") }, - get { complete("second") }) - } ~> check { responseAs[String] shouldEqual "second" } - } - "collect rejections from both sub routes" in { - Delete() ~> { - concat( - get { completeOk }, - put { completeOk }) - } ~> check { rejections shouldEqual Seq(MethodRejection(GET), MethodRejection(PUT)) } - } - "clear rejections that have already been 'overcome' by previous directives" in { - Put() ~> { - concat( - put { parameter('yeah) { echoComplete } }, - get { completeOk }) - } ~> check { rejection shouldEqual MissingQueryParamRejection("yeah") } - } - } - - "Route conjunction" should { - val stringDirective = provide("The cat") - val intDirective = provide(42) - val doubleDirective = provide(23.0) - - val dirStringInt = stringDirective & intDirective - val dirStringIntDouble = dirStringInt & doubleDirective - val dirDoubleStringInt = doubleDirective & dirStringInt - val dirStringIntStringInt = dirStringInt & dirStringInt - - "work for two elements" in { - Get("/abc") ~> { - dirStringInt { (str, i) ⇒ - complete(s"$str ${i + 1}") - } - } ~> check { responseAs[String] shouldEqual "The cat 43" } - } - "work for 2 + 1" in { - Get("/abc") ~> { - dirStringIntDouble { (str, i, d) ⇒ - complete(s"$str ${i + 1} ${d + 0.1}") - } - } ~> check { responseAs[String] shouldEqual "The cat 43 23.1" } - } - "work for 1 + 2" in { - Get("/abc") ~> { - dirDoubleStringInt { (d, str, i) ⇒ - complete(s"$str ${i + 1} ${d + 0.1}") - } - } ~> check { responseAs[String] shouldEqual "The cat 43 23.1" } - } - "work for 2 + 2" in { - Get("/abc") ~> { - dirStringIntStringInt { (str, i, str2, i2) ⇒ - complete(s"$str ${i + i2} $str2") - } - } ~> check { responseAs[String] shouldEqual "The cat 84 The cat" } - } - } - "Route disjunction" should { - "work in the happy case" in { - val route = Route.seal((path("abc") | path("def")) { - completeOk - }) - - Get("/abc") ~> route ~> check { - status shouldEqual StatusCodes.OK - } - Get("/def") ~> route ~> check { - status shouldEqual StatusCodes.OK - } - Get("/ghi") ~> route ~> check { - status shouldEqual StatusCodes.NotFound - } - } - "don't apply alternative if inner route rejects" in { - object MyRejection extends Rejection - val route = (path("abc") | post) { - reject(MyRejection) - } - Get("/abc") ~> route ~> check { - rejection shouldEqual MyRejection - } - } - } - "Case class extraction with Directive.as" should { - "extract one argument" in { - case class MyNumber(i: Int) - - val abcPath = path("abc" / IntNumber).as(MyNumber)(echoComplete) - - Get("/abc/5") ~> abcPath ~> check { - responseAs[String] shouldEqual "MyNumber(5)" - } - } - "extract two arguments" in { - case class Person(name: String, age: Int) - - val personPath = path("person" / Segment / IntNumber).as(Person)(echoComplete) - - Get("/person/john/38") ~> personPath ~> check { - responseAs[String] shouldEqual "Person(john,38)" - } - } - "reject if case class requirements fail" in { - case class MyValidNumber(i: Int) { - require(i > 10) - } - - val abcPath = path("abc" / IntNumber).as(MyValidNumber)(echoComplete) - - Get("/abc/5") ~> abcPath ~> check { - rejection shouldBe a[ValidationRejection] - } - } - } - "Dynamic execution of inner routes of Directive0" should { - "re-execute inner routes every time" in { - var a = "" - val dynamicRoute = get { a += "x"; complete(a) } - def expect(route: Route, s: String) = Get() ~> route ~> check { responseAs[String] shouldEqual s } - - expect(dynamicRoute, "x") - expect(dynamicRoute, "xx") - expect(dynamicRoute, "xxx") - expect(dynamicRoute, "xxxx") - } - } - - case object MyException extends RuntimeException - "Route sealing" should { - "catch route execution exceptions" in EventFilter[MyException.type](occurrences = 1).intercept { - Get("/abc") ~> Route.seal { - get { ctx ⇒ - throw MyException - } - } ~> check { - status shouldEqual StatusCodes.InternalServerError - } - } - "catch route building exceptions" in EventFilter[MyException.type](occurrences = 1).intercept { - Get("/abc") ~> Route.seal { - get { - throw MyException - } - } ~> check { - status shouldEqual StatusCodes.InternalServerError - } - } - "convert all rejections to responses" in EventFilter[RuntimeException](occurrences = 1).intercept { - object MyRejection extends Rejection - Get("/abc") ~> Route.seal { - get { - reject(MyRejection) - } - } ~> check { - status shouldEqual StatusCodes.InternalServerError - } - } - "always prioritize MethodRejections over AuthorizationFailedRejections" in { - Get("/abc") ~> Route.seal { - post { completeOk } ~ - authorize(false) { completeOk } - } ~> check { - status shouldEqual StatusCodes.MethodNotAllowed - responseAs[String] shouldEqual "HTTP method not allowed, supported methods: POST" - } - - Get("/abc") ~> Route.seal { - authorize(false) { completeOk } ~ - post { completeOk } - } ~> check { status shouldEqual StatusCodes.MethodNotAllowed } - } - } -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/ConnectionTestApp.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/ConnectionTestApp.scala deleted file mode 100644 index 413b7bbe30..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/ConnectionTestApp.scala +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server - -import akka.actor._ -import akka.dispatch.MessageDispatcher -import akka.http.scaladsl.Http -import akka.http.scaladsl.model.{ HttpRequest, HttpResponse, Uri } -import akka.stream.scaladsl.{ Flow, Sink, Source } -import akka.stream.{ ActorMaterializer, OverflowStrategy } -import akka.util.Index -import com.typesafe.config.{ Config, ConfigFactory } - -import scala.concurrent.Future -import scala.util.{ Failure, Success, Try } - -object ConnectionTestApp { - val testConf: Config = ConfigFactory.parseString(""" - akka.loglevel = debug - akka.log-dead-letters = off - - akka.http { - client { - idle-timeout = 10s - } - } - """) - - implicit val system = ActorSystem("ConnectionTest", testConf) - import system.dispatcher - implicit val materializer = ActorMaterializer() - - val clientFlow = Http().superPool[Int]() - - val sourceActor = { - // Our superPool expects (HttpRequest, Int) as input - val source = Source.actorRef[(HttpRequest, Int)](10000, OverflowStrategy.dropNew).buffer(20000, OverflowStrategy.fail) - val sink = Sink.foreach[(Try[HttpResponse], Int)] { - case (resp, id) ⇒ handleResponse(resp, id) - } - - source.via(clientFlow).to(sink).run() - } - - def sendPoolFlow(uri: Uri, id: Int): Unit = { - sourceActor ! ((buildRequest(uri), id)) - } - - def sendPoolFuture(uri: Uri, id: Int): Unit = { - val responseFuture: Future[HttpResponse] = - Http().singleRequest(buildRequest(uri)) - - responseFuture.onComplete(r ⇒ handleResponse(r, id)) - } - - def sendSingle(uri: Uri, id: Int): Unit = { - val connectionFlow: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] = - Http().outgoingConnection(uri.authority.host.address, uri.effectivePort) - val responseFuture: Future[HttpResponse] = - Source.single(buildRequest(uri)) - .via(connectionFlow) - .runWith(Sink.head) - - responseFuture.onComplete(r ⇒ handleResponse(r, id)) - } - - private def buildRequest(uri: Uri): HttpRequest = - HttpRequest(uri = uri) - - private def handleResponse(httpResp: Try[HttpResponse], id: Int): Unit = { - httpResp match { - case Success(httpRes) ⇒ - println(s"$id: OK (${httpRes.status.intValue})") - httpRes.entity.dataBytes.runWith(Sink.ignore) - - case Failure(ex) ⇒ - println(s"$id: $ex") - } - } - - def main(args: Array[String]): Unit = { - for (i ← 1 to 1000) { - val u = s"http://127.0.0.1:6666/test/$i" - println("u =>" + u) - sendPoolFlow(Uri(u), i) - //sendPoolFuture(uri, i) - //sendSingle(uri, i) - } - - readLine() - println("===================== \n\n" + system.asInstanceOf[ActorSystemImpl].printTree + "\n\n========================") - readLine() - system.terminate() - } - -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/DontLeakActorsOnFailingConnectionSpecs.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/DontLeakActorsOnFailingConnectionSpecs.scala deleted file mode 100644 index ceb19eca38..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/DontLeakActorsOnFailingConnectionSpecs.scala +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server - -import java.util.concurrent.{ TimeUnit, CountDownLatch } - -import akka.actor.ActorSystem -import akka.event.Logging -import akka.http.scaladsl.{ TestUtils, Http } -import akka.http.scaladsl.model.{ HttpResponse, Uri, HttpRequest } -import akka.stream.ActorMaterializer -import akka.stream.scaladsl.{ Sink, Source } -import akka.stream.testkit.Utils.assertAllStagesStopped -import com.typesafe.config.ConfigFactory -import org.scalatest.{ Matchers, BeforeAndAfterAll, WordSpecLike } - -import scala.concurrent.Await -import scala.concurrent.duration._ -import scala.util.{ Failure, Success, Try } - -class DontLeakActorsOnFailingConnectionSpecs extends WordSpecLike with Matchers with BeforeAndAfterAll { - - val config = ConfigFactory.parseString(""" - akka { - # disable logs (very noisy tests - 100 exepected errors) - loglevel = OFF - stdout-loglevel = OFF - }""").withFallback(ConfigFactory.load()) - implicit val system = ActorSystem("DontLeakActorsOnFailingConnectionSpecs", config) - import system.dispatcher - implicit val materializer = ActorMaterializer() - - val log = Logging(system, getClass) - - "Http.superPool" should { - - "not leak connection Actors when hitting non-existing endpoint" in { - assertAllStagesStopped { - val reqsCount = 100 - val clientFlow = Http().superPool[Int]() - val (_, _, port) = TestUtils.temporaryServerHostnameAndPort() - val source = Source(1 to reqsCount).map(i ⇒ HttpRequest(uri = Uri(s"http://127.0.0.1:$port/test/$i")) → i) - - val countDown = new CountDownLatch(reqsCount) - val sink = Sink.foreach[(Try[HttpResponse], Int)] { - case (resp, id) ⇒ handleResponse(resp, id) - } - - val resps = source.via(clientFlow).runWith(sink) - resps.onComplete({ case _ ⇒ countDown.countDown() }) - - countDown.await(10, TimeUnit.SECONDS) - Thread.sleep(5000) - } - } - } - - private def handleResponse(httpResp: Try[HttpResponse], id: Int): Unit = { - httpResp match { - case Success(httpRes) ⇒ - println(s"$id: OK: (${httpRes.status.intValue}") - httpRes.entity.dataBytes.runWith(Sink.ignore) - - case Failure(ex) ⇒ - println(s"$id: FAIL: $ex") - } - } - - override def afterAll = { - Await.result(system.terminate(), 3.seconds) - } - -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/IntegrationRoutingSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/IntegrationRoutingSpec.scala deleted file mode 100644 index 223b6b2d9b..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/IntegrationRoutingSpec.scala +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.scaladsl.server - -import akka.actor.ActorSystem -import akka.http.scaladsl.{ Http, TestUtils } -import akka.http.scaladsl.client.RequestBuilding -import akka.http.scaladsl.model.{ HttpResponse, HttpRequest } -import akka.stream.ActorMaterializer -import akka.testkit.AkkaSpec -import org.scalatest.concurrent.{ IntegrationPatience, ScalaFutures } -import org.scalatest.{ BeforeAndAfterAll, Matchers, WordSpecLike } -import scala.concurrent.duration._ -import scala.concurrent.Await - -/** INTERNAL API - not (yet?) ready for public consuption */ -private[akka] trait IntegrationRoutingSpec extends WordSpecLike with Matchers with BeforeAndAfterAll - with Directives with RequestBuilding - with ScalaFutures with IntegrationPatience { - import IntegrationRoutingSpec._ - - implicit val system = ActorSystem(AkkaSpec.getCallerName(getClass)) - implicit val mat = ActorMaterializer() - import system.dispatcher - - override protected def afterAll(): Unit = { - Await.ready(system.terminate(), 3.seconds) - } - - implicit class DSL(request: HttpRequest) { - def ~!>(route: Route) = new Prepped(request, route) - } - - implicit class Checking(p: Prepped) { - def ~!>(checking: HttpResponse ⇒ Unit) = { - val (_, host, port) = TestUtils.temporaryServerHostnameAndPort() - val binding = Http().bindAndHandle(p.route, host, port) - - try { - val targetUri = p.request.uri.withHost(host).withPort(port).withScheme("http") - val response = Http().singleRequest(p.request.withUri(targetUri)).futureValue - checking(response) - } finally binding.flatMap(_.unbind()).futureValue - } - } - -} - -object IntegrationRoutingSpec { - final case class Prepped(request: HttpRequest, route: Route) -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/ModeledCustomHeaderSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/ModeledCustomHeaderSpec.scala deleted file mode 100644 index eedeb9d305..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/ModeledCustomHeaderSpec.scala +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server - -import akka.http.scaladsl.model.StatusCodes -import akka.http.scaladsl.model.headers._ - -import scala.util.{ Success, Failure, Try } - -object ModeledCustomHeaderSpec { - - //#modeled-api-key-custom-header - final class ApiTokenHeader(token: String) extends ModeledCustomHeader[ApiTokenHeader] { - override def renderInRequests = false - override def renderInResponses = false - override val companion = ApiTokenHeader - override def value: String = token - } - object ApiTokenHeader extends ModeledCustomHeaderCompanion[ApiTokenHeader] { - override val name = "apiKey" - override def parse(value: String) = Try(new ApiTokenHeader(value)) - } - //#modeled-api-key-custom-header - - final class DifferentHeader(token: String) extends ModeledCustomHeader[DifferentHeader] { - override def renderInRequests = false - override def renderInResponses = false - override val companion = DifferentHeader - override def value = token - } - object DifferentHeader extends ModeledCustomHeaderCompanion[DifferentHeader] { - override val name = "different" - override def parse(value: String) = - if (value contains " ") Failure(new Exception("Contains illegal whitespace!")) - else Success(new DifferentHeader(value)) - } - -} - -class ModeledCustomHeaderSpec extends RoutingSpec { - import ModeledCustomHeaderSpec._ - - "CustomHeader" should { - - "be able to be extracted using expected syntax" in { - //#matching-examples - val ApiTokenHeader(t1) = ApiTokenHeader("token") - t1 should ===("token") - - val RawHeader(k2, v2) = ApiTokenHeader("token") - k2 should ===("apiKey") - v2 should ===("token") - - // will match, header keys are case insensitive - val ApiTokenHeader(v3) = RawHeader("APIKEY", "token") - v3 should ===("token") - - intercept[MatchError] { - // won't match, different header name - val ApiTokenHeader(v4) = DifferentHeader("token") - } - - intercept[MatchError] { - // won't match, different header name - val RawHeader("something", v5) = DifferentHeader("token") - } - - intercept[MatchError] { - // won't match, different header name - val ApiTokenHeader(v6) = RawHeader("different", "token") - } - //#matching-examples - } - - "be able to match from RawHeader" in { - - //#matching-in-routes - def extractFromCustomHeader = headerValuePF { - case t @ ApiTokenHeader(token) ⇒ s"extracted> $t" - case raw: RawHeader ⇒ s"raw> $raw" - } - - val routes = extractFromCustomHeader { s ⇒ - complete(s) - } - - Get().withHeaders(RawHeader("apiKey", "TheKey")) ~> routes ~> check { - status should ===(StatusCodes.OK) - responseAs[String] should ===("extracted> apiKey: TheKey") - } - - Get().withHeaders(RawHeader("somethingElse", "TheKey")) ~> routes ~> check { - status should ===(StatusCodes.OK) - responseAs[String] should ===("raw> somethingElse: TheKey") - } - - Get().withHeaders(ApiTokenHeader("TheKey")) ~> routes ~> check { - status should ===(StatusCodes.OK) - responseAs[String] should ===("extracted> apiKey: TheKey") - } - //#matching-in-routes - } - - "be able to extract in routing DSL via headerValueByType" in { - val routes = headerValueByType[ApiTokenHeader]() { token ⇒ - complete(s"extracted> $token") - } - - Get().withHeaders(RawHeader("apiKey", "TheKey")) ~> routes ~> check { - status should ===(StatusCodes.OK) - responseAs[String] should ===("extracted> apiKey: TheKey") - } - - Get().withHeaders(RawHeader("somethingElse", "TheKey")) ~> routes ~> check { - rejection should ===(MissingHeaderRejection("ApiTokenHeader")) - } - - Get().withHeaders(ApiTokenHeader("TheKey")) ~> routes ~> check { - status should ===(StatusCodes.OK) - responseAs[String] should ===("extracted> apiKey: TheKey") - } - } - - "fail with useful message when unable to parse" in { - val ex = intercept[Exception] { - DifferentHeader("Hello world") // illegal " " - } - - ex.getMessage should ===("Unable to construct custom header by parsing: 'Hello world'") - ex.getCause.getMessage should include("whitespace") - } - } - -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/RejectionSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/RejectionSpec.scala deleted file mode 100644 index ff64157d81..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/RejectionSpec.scala +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (C) 2016 Lightbend Inc. - */ -package akka.http.scaladsl.server - -import org.scalatest.{ BeforeAndAfterAll, Matchers, WordSpecLike } -import scala.collection.JavaConverters._ - -class RejectionSpec extends WordSpecLike with Matchers with BeforeAndAfterAll { - - "The Transformation Rejection" should { - - "map to and from Java" in { - import akka.http.javadsl.{ server ⇒ jserver } - val rejections = List(RequestEntityExpectedRejection) - val jrejections: java.lang.Iterable[jserver.Rejection] = - rejections.map(_.asInstanceOf[jserver.Rejection]).asJava - val jresult = TransformationRejection(identity).getTransform.apply(jrejections) - - val result = jresult.asScala.map(r ⇒ r.asInstanceOf[Rejection]) - result should ===(rejections) - } - } -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/RoutingSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/RoutingSpec.scala deleted file mode 100644 index fb71b93956..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/RoutingSpec.scala +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server - -import org.scalatest.{ WordSpec, Suite, Matchers } -import akka.http.scaladsl.model.HttpResponse -import akka.http.scaladsl.testkit.ScalatestRouteTest - -trait GenericRoutingSpec extends Matchers with Directives with ScalatestRouteTest { this: Suite ⇒ - val Ok = HttpResponse() - val completeOk = complete(Ok) - - def echoComplete[T]: T ⇒ Route = { x ⇒ complete(x.toString) } - def echoComplete2[T, U]: (T, U) ⇒ Route = { (x, y) ⇒ complete(s"$x $y") } -} - -abstract class RoutingSpec extends WordSpec with GenericRoutingSpec diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/StreamingResponseSpecs.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/StreamingResponseSpecs.scala deleted file mode 100644 index a4995f9446..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/StreamingResponseSpecs.scala +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server - -import akka.http.scaladsl.model.{ ContentTypes, HttpEntity, HttpResponse, StatusCodes } -import akka.stream.scaladsl.Source -import akka.util.ByteString - -class StreamingResponseSpecs extends RoutingSpec { - - "streaming ByteString responses" should { - "should render empty string if stream was empty" in { - - val src = Source.empty[ByteString] - val entity = HttpEntity.Chunked.fromData(ContentTypes.`application/json`, src) - val response = HttpResponse(status = StatusCodes.OK, entity = entity) - val route = complete(response) - - Get() ~> route ~> check { - status should ===(StatusCodes.OK) - responseAs[String] should ===("") - } - } - - } -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/TcpLeakApp.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/TcpLeakApp.scala deleted file mode 100644 index 9f42e191c5..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/TcpLeakApp.scala +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server - -import java.net.InetSocketAddress - -import akka.actor.{ ActorSystem, ActorSystemImpl } -import akka.event.Logging -import akka.stream.scaladsl._ -import akka.stream.{ ActorAttributes, ActorMaterializer } -import akka.util.ByteString -import com.typesafe.config.{ Config, ConfigFactory } - -object TcpLeakApp extends App { - val testConf: Config = ConfigFactory.parseString( - """ - akka.loglevel = DEBUG - akka.log-dead-letters = on - akka.io.tcp.trace-logging = on""") - implicit val system = ActorSystem("ServerTest", testConf) - implicit val fm = ActorMaterializer() - - import system.dispatcher - - val tcpFlow = Tcp().outgoingConnection(new InetSocketAddress("127.0.0.1", 1234)).named("TCP-outgoingConnection") - List - .fill(100)( - Source - .single(ByteString("FOO")) - .log("outerFlow-beforeTcpFlow").withAttributes(ActorAttributes.logLevels(Logging.DebugLevel, Logging.ErrorLevel, Logging.ErrorLevel)) - .via(tcpFlow) - .log("outerFlow-afterTcpFlow").withAttributes(ActorAttributes.logLevels(Logging.DebugLevel, Logging.ErrorLevel, Logging.ErrorLevel)) - .toMat(Sink.head)(Keep.right).run()) - .last - .onComplete { - case error ⇒ - println(s"Error: $error") - Thread.sleep(10000) - println("===================== \n\n" + system.asInstanceOf[ActorSystemImpl].printTree + "\n\n========================") - } - - readLine() - system.terminate() -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/TestServer.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/TestServer.scala deleted file mode 100644 index 25fec791f3..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/TestServer.scala +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server - -import akka.NotUsed -import akka.http.scaladsl.marshallers.xml.ScalaXmlSupport -import akka.http.scaladsl.model.{ HttpResponse, StatusCodes } -import akka.http.scaladsl.server.directives.Credentials -import com.typesafe.config.{ Config, ConfigFactory } -import akka.actor.ActorSystem -import akka.stream._ -import akka.stream.scaladsl._ -import akka.http.scaladsl.Http -import akka.http.scaladsl.common.EntityStreamingSupport - -import scala.concurrent.duration._ -import scala.io.StdIn - -object TestServer extends App { - val testConf: Config = ConfigFactory.parseString(""" - akka.loglevel = INFO - akka.log-dead-letters = off - akka.stream.materializer.debug.fuzzing-mode = off - """) - - implicit val system = ActorSystem("ServerTest", testConf) - import system.dispatcher - implicit val materializer = ActorMaterializer() - - import spray.json.DefaultJsonProtocol._ - import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._ - final case class Tweet(message: String) - implicit val tweetFormat = jsonFormat1(Tweet) - - implicit val jsonStreaming = EntityStreamingSupport.json() - - import ScalaXmlSupport._ - import Directives._ - - def auth: AuthenticatorPF[String] = { - case p @ Credentials.Provided(name) if p.verify(name + "-password") ⇒ name - } - - // format: OFF - val routes = { - get { - path("") { - withRequestTimeout(1.milli, _ ⇒ HttpResponse( - StatusCodes.EnhanceYourCalm, - entity = "Unable to serve response within time limit, please enchance your calm.")) { - Thread.sleep(1000) - complete(index) - } - } ~ - path("secure") { - authenticateBasicPF("My very secure site", auth) { user ⇒ - complete( Hello {user}. Access has been granted! ) - } - } ~ - path("ping") { - complete("PONG!") - } ~ - path("crash") { - complete(sys.error("BOOM!")) - } ~ - path("tweet") { - complete(Tweet("Hello, world!")) - } ~ - (path("tweets") & parameter('n.as[Int])) { n => - get { - val tweets = Source.repeat(Tweet("Hello, world!")).take(n) - complete(tweets) - } ~ - post { - entity(asSourceOf[Tweet]) { tweets ⇒ - onComplete(tweets.runFold(0)({ case (acc, t) => acc + 1 })) { count => - complete(s"Total tweets received: " + count) - } - } - } ~ - put { - // checking the alternative syntax also works: - entity(as[Source[Tweet, NotUsed]]) { tweets ⇒ - onComplete(tweets.runFold(0)({ case (acc, t) => acc + 1 })) { count => - complete(s"Total tweets received: " + count) - } - } - } - } - } ~ - pathPrefix("inner")(getFromResourceDirectory("someDir")) - } - // format: ON - - val bindingFuture = Http().bindAndHandle(routes, interface = "0.0.0.0", port = 8080) - - println(s"Server online at http://0.0.0.0:8080/\nPress RETURN to stop...") - StdIn.readLine() - - bindingFuture.flatMap(_.unbind()).onComplete(_ ⇒ system.terminate()) - - lazy val index = - - -

Say hello to akka-http-core!

-

Defined resources:

- - - -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/WithoutSizeLimitSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/WithoutSizeLimitSpec.scala deleted file mode 100644 index 5cea8ab9a5..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/WithoutSizeLimitSpec.scala +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server - -import akka.actor.ActorSystem -import akka.http.scaladsl.client.RequestBuilding -import akka.http.scaladsl.model._ -import akka.http.scaladsl.server.Directives._ -import akka.http.scaladsl.{ Http, TestUtils } -import akka.stream.ActorMaterializer -import com.typesafe.config.{ Config, ConfigFactory } -import org.scalatest.{ BeforeAndAfterAll, Matchers, WordSpec } - -import scala.concurrent.Await -import scala.concurrent.duration._ - -class WithoutSizeLimitSpec extends WordSpec with Matchers with RequestBuilding with BeforeAndAfterAll { - val testConf: Config = ConfigFactory.parseString(""" - akka.loggers = ["akka.testkit.TestEventListener"] - akka.loglevel = ERROR - akka.stdout-loglevel = ERROR - akka.http.parsing.max-content-length = 800""") - implicit val system = ActorSystem(getClass.getSimpleName, testConf) - import system.dispatcher - implicit val materializer = ActorMaterializer() - - "the withoutSizeLimit directive" should { - "accept entities bigger than configured with akka.http.parsing.max-content-length" in { - val route = - path("noDirective") { - post { - entity(as[String]) { _ ⇒ - complete(HttpEntity(ContentTypes.`text/html(UTF-8)`, "

Say hello to akka-http

")) - } - } - } ~ - path("withoutSizeLimit") { - post { - withoutSizeLimit { - entity(as[String]) { _ ⇒ - complete(HttpEntity(ContentTypes.`text/html(UTF-8)`, "

Say hello to akka-http

")) - } - } - } - } - - val (_, hostName, port) = TestUtils.temporaryServerHostnameAndPort() - - val future = for { - _ ← Http().bindAndHandle(route, hostName, port) - - requestToNoDirective = Post(s"http://$hostName:$port/noDirective", entityOfSize(801)) - responseWithoutDirective ← Http().singleRequest(requestToNoDirective) - _ = responseWithoutDirective.status shouldEqual StatusCodes.BadRequest - - requestToDirective = Post(s"http://$hostName:$port/withoutSizeLimit", entityOfSize(801)) - responseWithDirective ← Http().singleRequest(requestToDirective) - } yield responseWithDirective - - val response = Await.result(future, 5 seconds) - response.status shouldEqual StatusCodes.OK - } - } - - override def afterAll() = { - system.terminate - } - - private def entityOfSize(size: Int) = HttpEntity(ContentTypes.`text/plain(UTF-8)`, "0" * size) -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/BasicDirectivesSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/BasicDirectivesSpec.scala deleted file mode 100644 index c1e619bb63..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/BasicDirectivesSpec.scala +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import akka.http.scaladsl.model._ -import akka.stream.scaladsl.Source -import akka.util.ByteString - -class BasicDirectivesSpec extends RoutingSpec { - - "The `mapUnmatchedPath` directive" should { - "map the unmatched path" in { - Get("/abc") ~> { - mapUnmatchedPath(_ / "def") { - path("abc" / "def") { completeOk } - } - } ~> check { response shouldEqual Ok } - } - } - - "The `extract` directive" should { - "extract from the RequestContext" in { - Get("/abc") ~> { - extract(_.request.method.value) { - echoComplete - } - } ~> check { responseAs[String] shouldEqual "GET" } - } - } - - "The `extractDataBytes` directive" should { - "extract stream of ByteString from the RequestContext" in { - val dataBytes = Source.fromIterator(() ⇒ Iterator.range(1, 10).map(x ⇒ ByteString(x.toString))) - Post("/abc", HttpEntity(ContentTypes.`text/plain(UTF-8)`, data = dataBytes)) ~> { - extractDataBytes { data ⇒ - val sum = data.runFold(0) { (acc, i) ⇒ acc + i.utf8String.toInt } - onSuccess(sum) { s ⇒ - complete(HttpResponse(entity = HttpEntity(s.toString))) - } - } - } ~> check { responseAs[String] shouldEqual "45" } - } - } - - "The `extractRequestEntity` directive" should { - "extract entity from the RequestContext" in { - val httpEntity = HttpEntity(ContentTypes.`text/plain(UTF-8)`, "req") - Post("/abc", httpEntity) ~> { - extractRequestEntity { complete(_) } - } ~> check { responseEntity shouldEqual httpEntity } - } - } -} \ No newline at end of file diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/CacheConditionDirectivesSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/CacheConditionDirectivesSpec.scala deleted file mode 100644 index 4493e9793c..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/CacheConditionDirectivesSpec.scala +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import akka.http.scaladsl.model._ -import StatusCodes._ -import headers._ - -class CacheConditionDirectivesSpec extends RoutingSpec { - - "the `conditional` directive" should { - val timestamp = DateTime.now - 2000 - val ifUnmodifiedSince = `If-Unmodified-Since`(timestamp) - val ifModifiedSince = `If-Modified-Since`(timestamp) - val tag = EntityTag("fresh") - val responseHeaders = List(ETag(tag), `Last-Modified`(timestamp)) - - def taggedAndTimestamped = conditional(tag, timestamp) { completeOk } - def weak = conditional(tag.copy(weak = true), timestamp) { completeOk } - - "return OK for new resources" in { - Get() ~> taggedAndTimestamped ~> check { - status shouldEqual OK - headers should contain theSameElementsAs (responseHeaders) - } - } - - "return OK for non-matching resources" in { - Get() ~> `If-None-Match`(EntityTag("old")) ~> taggedAndTimestamped ~> check { - status shouldEqual OK - headers should contain theSameElementsAs (responseHeaders) - } - Get() ~> `If-Modified-Since`(timestamp - 1000) ~> taggedAndTimestamped ~> check { - status shouldEqual OK - headers should contain theSameElementsAs (responseHeaders) - } - Get() ~> `If-None-Match`(EntityTag("old")) ~> `If-Modified-Since`(timestamp - 1000) ~> taggedAndTimestamped ~> check { - status shouldEqual OK - headers should contain theSameElementsAs (responseHeaders) - } - } - - "ignore If-Modified-Since if If-None-Match is defined" in { - Get() ~> `If-None-Match`(tag) ~> `If-Modified-Since`(timestamp - 1000) ~> taggedAndTimestamped ~> check { - status shouldEqual NotModified - } - Get() ~> `If-None-Match`(EntityTag("old")) ~> ifModifiedSince ~> taggedAndTimestamped ~> check { - status shouldEqual OK - } - } - - "return PreconditionFailed for matched but unsafe resources" in { - Put() ~> `If-None-Match`(tag) ~> ifModifiedSince ~> taggedAndTimestamped ~> check { - status shouldEqual PreconditionFailed - headers shouldEqual Nil - } - } - - "return NotModified for matching resources" in { - Get() ~> `If-None-Match`.`*` ~> ifModifiedSince ~> taggedAndTimestamped ~> check { - status shouldEqual NotModified - headers should contain theSameElementsAs (responseHeaders) - } - Get() ~> `If-None-Match`(tag) ~> ifModifiedSince ~> taggedAndTimestamped ~> check { - status shouldEqual NotModified - headers should contain theSameElementsAs (responseHeaders) - } - Get() ~> `If-None-Match`(tag) ~> `If-Modified-Since`(timestamp + 1000) ~> taggedAndTimestamped ~> check { - status shouldEqual NotModified - headers should contain theSameElementsAs (responseHeaders) - } - Get() ~> `If-None-Match`(tag.copy(weak = true)) ~> ifModifiedSince ~> taggedAndTimestamped ~> check { - status shouldEqual NotModified - headers should contain theSameElementsAs (responseHeaders) - } - Get() ~> `If-None-Match`(tag, EntityTag("some"), EntityTag("other")) ~> ifModifiedSince ~> taggedAndTimestamped ~> check { - status shouldEqual NotModified - headers should contain theSameElementsAs (responseHeaders) - } - } - - "return NotModified when only one matching header is set" in { - Get() ~> `If-None-Match`.`*` ~> taggedAndTimestamped ~> check { - status shouldEqual NotModified - headers should contain theSameElementsAs (responseHeaders) - } - Get() ~> `If-None-Match`(tag) ~> taggedAndTimestamped ~> check { - status shouldEqual NotModified - headers should contain theSameElementsAs (responseHeaders) - } - Get() ~> ifModifiedSince ~> taggedAndTimestamped ~> check { - status shouldEqual NotModified - headers should contain theSameElementsAs (responseHeaders) - } - } - - "return NotModified for matching weak resources" in { - val weakTag = tag.copy(weak = true) - Get() ~> `If-None-Match`(tag) ~> weak ~> check { - status shouldEqual NotModified - headers should contain theSameElementsAs (List(ETag(weakTag), `Last-Modified`(timestamp))) - } - Get() ~> `If-None-Match`(weakTag) ~> weak ~> check { - status shouldEqual NotModified - headers should contain theSameElementsAs (List(ETag(weakTag), `Last-Modified`(timestamp))) - } - } - - "return normally for matching If-Match/If-Unmodified" in { - Put() ~> `If-Match`.`*` ~> taggedAndTimestamped ~> check { - status shouldEqual OK - headers should contain theSameElementsAs (responseHeaders) - } - Put() ~> `If-Match`(tag) ~> taggedAndTimestamped ~> check { - status shouldEqual OK - headers should contain theSameElementsAs (responseHeaders) - } - Put() ~> ifUnmodifiedSince ~> taggedAndTimestamped ~> check { - status shouldEqual OK - headers should contain theSameElementsAs (responseHeaders) - } - } - - "return PreconditionFailed for non-matching If-Match/If-Unmodified" in { - Put() ~> `If-Match`(EntityTag("old")) ~> taggedAndTimestamped ~> check { - status shouldEqual PreconditionFailed - headers shouldEqual Nil - } - Put() ~> `If-Unmodified-Since`(timestamp - 1000) ~> taggedAndTimestamped ~> check { - status shouldEqual PreconditionFailed - headers shouldEqual Nil - } - } - - "ignore If-Unmodified-Since if If-Match is defined" in { - Put() ~> `If-Match`(tag) ~> `If-Unmodified-Since`(timestamp - 1000) ~> taggedAndTimestamped ~> check { - status shouldEqual OK - } - Put() ~> `If-Match`(EntityTag("old")) ~> ifModifiedSince ~> taggedAndTimestamped ~> check { - status shouldEqual PreconditionFailed - } - } - - "not filter out a `Range` header if `If-Range` does match the timestamp" in { - Get() ~> `If-Range`(timestamp) ~> Range(ByteRange(0, 10)) ~> { - (conditional(tag, timestamp) & optionalHeaderValueByType[Range]()) { echoComplete } - } ~> check { - status shouldEqual OK - responseAs[String] should startWith("Some") - } - } - - "filter out a `Range` header if `If-Range` doesn't match the timestamp" in { - Get() ~> `If-Range`(timestamp - 1000) ~> Range(ByteRange(0, 10)) ~> { - (conditional(tag, timestamp) & optionalHeaderValueByType[Range]()) { echoComplete } - } ~> check { - status shouldEqual OK - responseAs[String] shouldEqual "None" - } - } - - "not filter out a `Range` header if `If-Range` does match the ETag" in { - Get() ~> `If-Range`(tag) ~> Range(ByteRange(0, 10)) ~> { - (conditional(tag, timestamp) & optionalHeaderValueByType[Range]()) { echoComplete } - } ~> check { - status shouldEqual OK - responseAs[String] should startWith("Some") - } - } - - "filter out a `Range` header if `If-Range` doesn't match the ETag" in { - Get() ~> `If-Range`(EntityTag("other")) ~> Range(ByteRange(0, 10)) ~> { - (conditional(tag, timestamp) & optionalHeaderValueByType[Range]()) { echoComplete } - } ~> check { - status shouldEqual OK - responseAs[String] shouldEqual "None" - } - } - } - -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/CodingDirectivesSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/CodingDirectivesSpec.scala deleted file mode 100644 index 12024906ae..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/CodingDirectivesSpec.scala +++ /dev/null @@ -1,540 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import org.scalatest.Inside -import org.scalatest.matchers.Matcher -import akka.util.ByteString -import akka.stream.scaladsl.{ Sink, Source } -import akka.http.impl.util._ -import akka.http.scaladsl.model._ -import akka.http.scaladsl.coding._ -import headers._ -import HttpEntity.{ ChunkStreamPart, Chunk } -import HttpCharsets._ -import HttpEncodings._ -import MediaTypes._ -import StatusCodes._ -import ContentTypes.`application/octet-stream` - -import scala.concurrent.duration._ - -class CodingDirectivesSpec extends RoutingSpec with Inside { - - val echoRequestContent: Route = { ctx ⇒ ctx.complete(ctx.request.entity.dataBytes.utf8String) } - - val yeah = complete("Yeah!") - lazy val yeahGzipped = compress("Yeah!", Gzip) - lazy val yeahDeflated = compress("Yeah!", Deflate) - - lazy val helloGzipped = compress("Hello", Gzip) - lazy val helloDeflated = compress("Hello", Deflate) - - val nope = complete((404, "Nope!")) - lazy val nopeGzipped = compress("Nope!", Gzip) - lazy val nopeDeflated = compress("Nope!", Deflate) - - "the NoEncoding decoder" should { - "decode the request content if it has encoding 'identity'" in { - Post("/", "yes") ~> `Content-Encoding`(identity) ~> { - decodeRequestWith(NoCoding) { echoRequestContent } - } ~> check { responseAs[String] shouldEqual "yes" } - } - "reject requests with content encoded with 'deflate'" in { - Post("/", "yes") ~> `Content-Encoding`(deflate) ~> { - decodeRequestWith(NoCoding) { echoRequestContent } - } ~> check { rejection shouldEqual UnsupportedRequestEncodingRejection(identity) } - } - "decode the request content if no Content-Encoding header is present" in { - Post("/", "yes") ~> decodeRequestWith(NoCoding) { echoRequestContent } ~> check { responseAs[String] shouldEqual "yes" } - } - "leave request without content unchanged" in { - Post() ~> decodeRequestWith(NoCoding) { completeOk } ~> check { response shouldEqual Ok } - } - - val echoDecodedEntity = - decodeRequestWith(NoCoding) { - extractRequest { request ⇒ - complete(HttpResponse(200, entity = request.entity)) - } - } - "leave Strict request entity unchanged" in { - val data = ByteString(Array.fill[Byte](10000)(42.toByte)) - val strictEntity = HttpEntity.Strict(`application/octet-stream`, data) - - Post("/", strictEntity) ~> echoDecodedEntity ~> check { - responseEntity shouldEqual strictEntity - } - } - "leave Default request entity unchanged" in { - val chunks = Vector(ByteString("abc"), ByteString("def"), ByteString("ghi")) - val data = Source(chunks) - - val defaultEntity = HttpEntity.Default(`application/octet-stream`, 9, data) - - Post("/", defaultEntity) ~> echoDecodedEntity ~> check { - inside(responseEntity) { - case HttpEntity.Default(`application/octet-stream`, 9, dataChunks) ⇒ - dataChunks.grouped(1000).runWith(Sink.head).awaitResult(1.second).toVector shouldEqual chunks - } - } - } - // CloseDelimited not support for requests - "leave Chunked request entity unchanged" in { - val chunks = - Vector(ByteString("abc"), ByteString("def"), ByteString("ghi")) - .map(ChunkStreamPart(_)) - val data = Source(chunks) - - val defaultEntity = HttpEntity.Chunked(`application/octet-stream`, data) - - Post("/", defaultEntity) ~> echoDecodedEntity ~> check { - inside(responseEntity) { - case HttpEntity.Chunked(`application/octet-stream`, dataChunks) ⇒ - dataChunks.grouped(1000).runWith(Sink.head).awaitResult(1.second).toVector shouldEqual chunks - } - } - } - } - - "the Gzip decoder" should { - "decode the request content if it has encoding 'gzip'" in { - Post("/", helloGzipped) ~> `Content-Encoding`(gzip) ~> { - decodeRequestWith(Gzip) { echoRequestContent } - } ~> check { responseAs[String] shouldEqual "Hello" } - } - "reject the request content if it has encoding 'gzip' but is corrupt" in { - Post("/", fromHexDump("000102")) ~> `Content-Encoding`(gzip) ~> { - decodeRequestWith(Gzip) { echoRequestContent } - } ~> check { - status shouldEqual BadRequest - responseAs[String] shouldEqual "The request's encoding is corrupt" - } - } - "reject truncated gzip request content" in { - Post("/", helloGzipped.dropRight(2)) ~> `Content-Encoding`(gzip) ~> { - decodeRequestWith(Gzip) { echoRequestContent } - } ~> check { - status shouldEqual BadRequest - responseAs[String] shouldEqual "The request's encoding is corrupt" - } - } - "reject requests with content encoded with 'deflate'" in { - Post("/", "Hello") ~> `Content-Encoding`(deflate) ~> { - decodeRequestWith(Gzip) { completeOk } - } ~> check { rejection shouldEqual UnsupportedRequestEncodingRejection(gzip) } - } - "reject requests without Content-Encoding header" in { - Post("/", "Hello") ~> { - decodeRequestWith(Gzip) { completeOk } - } ~> check { rejection shouldEqual UnsupportedRequestEncodingRejection(gzip) } - } - "leave request without content unchanged" in { - Post() ~> { - decodeRequestWith(Gzip) { completeOk } - } ~> check { response shouldEqual Ok } - } - } - - "a (decodeRequestWith(Gzip) | decodeRequestWith(NoEncoding)) compound directive" should { - lazy val decodeWithGzipOrNoEncoding = decodeRequestWith(Gzip) | decodeRequestWith(NoCoding) - "decode the request content if it has encoding 'gzip'" in { - Post("/", helloGzipped) ~> `Content-Encoding`(gzip) ~> { - decodeWithGzipOrNoEncoding { echoRequestContent } - } ~> check { responseAs[String] shouldEqual "Hello" } - } - "decode the request content if it has encoding 'identity'" in { - Post("/", "yes") ~> `Content-Encoding`(identity) ~> { - decodeWithGzipOrNoEncoding { echoRequestContent } - } ~> check { responseAs[String] shouldEqual "yes" } - } - "decode the request content if no Content-Encoding header is present" in { - Post("/", "yes") ~> decodeWithGzipOrNoEncoding { echoRequestContent } ~> check { responseAs[String] shouldEqual "yes" } - } - "reject requests with content encoded with 'deflate'" in { - Post("/", "yes") ~> `Content-Encoding`(deflate) ~> { - decodeWithGzipOrNoEncoding { echoRequestContent } - } ~> check { - rejections shouldEqual Seq( - UnsupportedRequestEncodingRejection(gzip), - UnsupportedRequestEncodingRejection(identity)) - } - } - } - - "the encoder" should { - "encode the response content with GZIP if the response is not a success and request has no Accept-Encoding header" in { - Post() ~> { - encodeResponseWith(Gzip) { nope } - } ~> check { strictify(responseEntity) shouldEqual HttpEntity(ContentType(`text/plain`, `UTF-8`), nopeGzipped) } - } - "encode the response content with Deflate if the response is not a success and request has no Accept-Encoding header" in { - Post() ~> { - encodeResponseWith(Deflate) { nope } - } ~> check { strictify(responseEntity) shouldEqual HttpEntity(ContentType(`text/plain`, `UTF-8`), nopeDeflated) } - } - "not encode the response content with GZIP if the response is of status not allowing entity" in { - Post() ~> { - encodeResponseWith(Gzip) { complete { StatusCodes.NoContent } } - } ~> check { - response should haveNoContentEncoding - response shouldEqual HttpResponse(StatusCodes.NoContent, entity = HttpEntity.Empty) - } - } - "not encode the response content with Deflate if the response is of status not allowing entity" in { - Post() ~> { - encodeResponseWith(Deflate) { complete((100, "Let's continue!")) } - } ~> check { - response should haveNoContentEncoding - response shouldEqual HttpResponse(StatusCodes.Continue, entity = HttpEntity.Empty) - } - } - "encode the response content with GZIP if the response is of status allowing entity" in { - Post() ~> { - encodeResponseWith(Gzip) { nope } - } ~> check { - response should haveContentEncoding(gzip) - } - } - } - - "the Gzip encoder" should { - "encode the response content with GZIP if the client accepts it with a dedicated Accept-Encoding header" in { - Post() ~> `Accept-Encoding`(gzip) ~> { - encodeResponseWith(Gzip) { yeah } - } ~> check { - response should haveContentEncoding(gzip) - strictify(responseEntity) shouldEqual HttpEntity(ContentType(`text/plain`, `UTF-8`), yeahGzipped) - } - } - "encode the response content with GZIP if the request has no Accept-Encoding header" in { - Post() ~> { - encodeResponseWith(Gzip) { yeah } - } ~> check { strictify(responseEntity) shouldEqual HttpEntity(ContentType(`text/plain`, `UTF-8`), yeahGzipped) } - } - "reject the request if the client does not accept GZIP encoding" in { - Post() ~> `Accept-Encoding`(identity) ~> { - encodeResponseWith(Gzip) { completeOk } - } ~> check { rejection shouldEqual UnacceptedResponseEncodingRejection(gzip) } - } - "leave responses without content unchanged" in { - Post() ~> `Accept-Encoding`(gzip) ~> { - encodeResponseWith(Gzip) { completeOk } - } ~> check { - response shouldEqual Ok - response should haveNoContentEncoding - } - } - "leave responses with an already set Content-Encoding header unchanged" in { - Post() ~> `Accept-Encoding`(gzip) ~> { - encodeResponseWith(Gzip) { - RespondWithDirectives.respondWithHeader(`Content-Encoding`(identity)) { completeOk } - } - } ~> check { response shouldEqual Ok.withHeaders(`Content-Encoding`(identity)) } - } - "correctly encode the chunk stream produced by a chunked response" in { - val text = "This is a somewhat lengthy text that is being chunked by the autochunk directive!" - val textChunks = - () ⇒ text.grouped(8).map { chars ⇒ - Chunk(chars.mkString): ChunkStreamPart - } - val chunkedTextEntity = HttpEntity.Chunked(ContentTypes.`text/plain(UTF-8)`, Source.fromIterator(textChunks)) - - Post() ~> `Accept-Encoding`(gzip) ~> { - encodeResponseWith(Gzip) { - complete(chunkedTextEntity) - } - } ~> check { - response should haveContentEncoding(gzip) - chunks.size shouldEqual (11 + 1) // 11 regular + the last one - val bytes = chunks.foldLeft(ByteString.empty)(_ ++ _.data) - Gzip.decode(bytes).awaitResult(1.second) should readAs(text) - } - } - "correctly encode the chunk stream produced by an empty chunked response" in { - val emptyChunkedEntity = HttpEntity.Chunked(ContentTypes.`text/plain(UTF-8)`, Source.empty) - - Post() ~> `Accept-Encoding`(gzip) ~> { - encodeResponseWith(Gzip) { - complete(emptyChunkedEntity) - } - } ~> check { - response should haveContentEncoding(gzip) - val bytes = chunks.foldLeft(ByteString.empty)(_ ++ _.data) - Gzip.decode(bytes).awaitResult(1.second) should readAs("") - } - } - } - - "the encodeResponseWith(NoEncoding) directive" should { - "produce a response if no Accept-Encoding is present in the request" in { - Post() ~> encodeResponseWith(NoCoding) { completeOk } ~> check { - response shouldEqual Ok - response should haveNoContentEncoding - } - } - "produce a not encoded response if the client only accepts non matching encodings" in { - Post() ~> `Accept-Encoding`(gzip, identity) ~> { - encodeResponseWith(NoCoding) { completeOk } - } ~> check { - response shouldEqual Ok - response should haveNoContentEncoding - } - - Post() ~> `Accept-Encoding`(gzip) ~> { - encodeResponseWith(Deflate, NoCoding) { completeOk } - } ~> check { - response shouldEqual Ok - response should haveNoContentEncoding - } - } - "reject the request if the request has an 'Accept-Encoding: identity; q=0' header" in { - Post() ~> `Accept-Encoding`(identity.withQValue(0f)) ~> { - encodeResponseWith(NoCoding) { completeOk } - } ~> check { rejection shouldEqual UnacceptedResponseEncodingRejection(identity) } - } - } - - "a (encodeResponse(Gzip) | encodeResponse(NoEncoding)) compound directive" should { - lazy val encodeGzipOrIdentity = encodeResponseWith(Gzip) | encodeResponseWith(NoCoding) - "produce a not encoded response if the request has no Accept-Encoding header" in { - Post() ~> { - encodeGzipOrIdentity { completeOk } - } ~> check { - response shouldEqual Ok - response should haveNoContentEncoding - } - } - "produce a GZIP encoded response if the request has an `Accept-Encoding: deflate;q=0.5, gzip` header" in { - Post() ~> `Accept-Encoding`(deflate.withQValue(.5f), gzip) ~> { - encodeGzipOrIdentity { yeah } - } ~> check { - response should haveContentEncoding(gzip) - strictify(responseEntity) shouldEqual HttpEntity(ContentType(`text/plain`, `UTF-8`), yeahGzipped) - } - } - "produce a non-encoded response if the request has an `Accept-Encoding: identity` header" in { - Post() ~> `Accept-Encoding`(identity) ~> { - encodeGzipOrIdentity { completeOk } - } ~> check { - response shouldEqual Ok - response should haveNoContentEncoding - } - } - "produce a non-encoded response if the request has an `Accept-Encoding: deflate` header" in { - Post() ~> `Accept-Encoding`(deflate) ~> { - encodeGzipOrIdentity { completeOk } - } ~> check { - response shouldEqual Ok - response should haveNoContentEncoding - } - } - } - - "the encodeResponse directive" should { - "produce a non-encoded response if the request has no Accept-Encoding header" in { - Get("/") ~> { - encodeResponse { completeOk } - } ~> check { - response shouldEqual Ok - response should haveNoContentEncoding - } - } - "produce a GZIP encoded response if the request has an `Accept-Encoding: gzip, deflate` header" in { - Get("/") ~> `Accept-Encoding`(gzip, deflate) ~> { - encodeResponse { yeah } - } ~> check { - response should haveContentEncoding(gzip) - strictify(responseEntity) shouldEqual HttpEntity(ContentType(`text/plain`, `UTF-8`), yeahGzipped) - } - } - "produce a Deflate encoded response if the request has an `Accept-Encoding: deflate` header" in { - Get("/") ~> `Accept-Encoding`(deflate) ~> { - encodeResponse { yeah } - } ~> check { - response should haveContentEncoding(deflate) - strictify(responseEntity) shouldEqual HttpEntity(ContentType(`text/plain`, `UTF-8`), yeahDeflated) - } - } - } - - "the encodeResponseWith directive" should { - "produce a response encoded with the specified Encoder if the request has a matching Accept-Encoding header" in { - Get("/") ~> `Accept-Encoding`(gzip) ~> { - encodeResponseWith(Gzip) { yeah } - } ~> check { - response should haveContentEncoding(gzip) - strictify(responseEntity) shouldEqual HttpEntity(ContentType(`text/plain`, `UTF-8`), yeahGzipped) - } - } - "produce a response encoded with one of the specified Encoders if the request has a matching Accept-Encoding header" in { - Get("/") ~> `Accept-Encoding`(deflate) ~> { - encodeResponseWith(Gzip, Deflate) { yeah } - } ~> check { - response should haveContentEncoding(deflate) - strictify(responseEntity) shouldEqual HttpEntity(ContentType(`text/plain`, `UTF-8`), yeahDeflated) - } - } - "produce a response encoded with the first of the specified Encoders if the request has no Accept-Encoding header" in { - Get("/") ~> { - encodeResponseWith(Gzip, Deflate) { yeah } - } ~> check { - response should haveContentEncoding(gzip) - strictify(responseEntity) shouldEqual HttpEntity(ContentType(`text/plain`, `UTF-8`), yeahGzipped) - } - } - "produce a response with no encoding if the request has an empty Accept-Encoding header" in { - Get("/") ~> `Accept-Encoding`() ~> { - encodeResponseWith(Gzip, Deflate, NoCoding) { completeOk } - } ~> check { - response shouldEqual Ok - response should haveNoContentEncoding - } - } - "negotiate the correct content encoding" in { - Get("/") ~> `Accept-Encoding`(identity.withQValue(.5f), deflate.withQValue(0f), gzip) ~> { - encodeResponseWith(NoCoding, Deflate, Gzip) { yeah } - } ~> check { - response should haveContentEncoding(gzip) - strictify(responseEntity) shouldEqual HttpEntity(ContentType(`text/plain`, `UTF-8`), yeahGzipped) - } - - Get("/") ~> `Accept-Encoding`(HttpEncodingRange.`*`, deflate withQValue 0.2) ~> { - encodeResponseWith(Deflate, Gzip) { yeah } - } ~> check { - response should haveContentEncoding(gzip) - strictify(responseEntity) shouldEqual HttpEntity(ContentType(`text/plain`, `UTF-8`), yeahGzipped) - } - } - "reject the request if it has an Accept-Encoding header with an encoding that doesn't match" in { - Get("/") ~> `Accept-Encoding`(deflate) ~> { - encodeResponseWith(Gzip) { yeah } - } ~> check { - rejection shouldEqual UnacceptedResponseEncodingRejection(gzip) - } - } - "reject the request if it has an Accept-Encoding header with an encoding that matches but is blacklisted" in { - Get("/") ~> `Accept-Encoding`(gzip.withQValue(0f)) ~> { - encodeResponseWith(Gzip) { yeah } - } ~> check { - rejection shouldEqual UnacceptedResponseEncodingRejection(gzip) - } - } - } - - "the decodeRequest directive" should { - "decode the request content if it has a `Content-Encoding: gzip` header and the content is gzip encoded" in { - Post("/", helloGzipped) ~> `Content-Encoding`(gzip) ~> { - decodeRequest { echoRequestContent } - } ~> check { responseAs[String] shouldEqual "Hello" } - } - "decode the request content if it has a `Content-Encoding: deflate` header and the content is deflate encoded" in { - Post("/", helloDeflated) ~> `Content-Encoding`(deflate) ~> { - decodeRequest { echoRequestContent } - } ~> check { responseAs[String] shouldEqual "Hello" } - } - "decode the request content if it has a `Content-Encoding: identity` header and the content is not encoded" in { - Post("/", "yes") ~> `Content-Encoding`(identity) ~> { - decodeRequest { echoRequestContent } - } ~> check { responseAs[String] shouldEqual "yes" } - } - "decode the request content using NoEncoding if no Content-Encoding header is present" in { - Post("/", "yes") ~> decodeRequest { echoRequestContent } ~> check { responseAs[String] shouldEqual "yes" } - } - "reject the request if it has a `Content-Encoding: deflate` header but the request is encoded with Gzip" in { - Post("/", helloGzipped) ~> `Content-Encoding`(deflate) ~> - decodeRequest { echoRequestContent } ~> check { - status shouldEqual BadRequest - responseAs[String] shouldEqual "The request's encoding is corrupt" - } - } - } - - "the decodeRequestWith directive" should { - "decode the request content if its `Content-Encoding` header matches the specified encoder" in { - Post("/", helloGzipped) ~> `Content-Encoding`(gzip) ~> { - decodeRequestWith(Gzip) { echoRequestContent } - } ~> check { responseAs[String] shouldEqual "Hello" } - } - "reject the request if its `Content-Encoding` header doesn't match the specified encoder" in { - Post("/", helloGzipped) ~> `Content-Encoding`(deflate) ~> { - decodeRequestWith(Gzip) { echoRequestContent } - } ~> check { - rejection shouldEqual UnsupportedRequestEncodingRejection(gzip) - } - } - "reject the request when decodeing with GZIP and no Content-Encoding header is present" in { - Post("/", "yes") ~> decodeRequestWith(Gzip) { echoRequestContent } ~> check { - rejection shouldEqual UnsupportedRequestEncodingRejection(gzip) - } - } - } - - "the (decodeRequest & encodeResponse) compound directive" should { - lazy val decodeEncode = decodeRequest & encodeResponse - "decode a GZIP encoded request and produce a none encoded response if the request has no Accept-Encoding header" in { - Post("/", helloGzipped) ~> `Content-Encoding`(gzip) ~> { - decodeEncode { echoRequestContent } - } ~> check { - response should haveNoContentEncoding - strictify(responseEntity) shouldEqual HttpEntity(ContentType(`text/plain`, `UTF-8`), "Hello") - } - } - "decode a GZIP encoded request and produce a Deflate encoded response if the request has an `Accept-Encoding: deflate` header" in { - Post("/", helloGzipped) ~> `Content-Encoding`(gzip) ~> `Accept-Encoding`(deflate) ~> { - decodeEncode { echoRequestContent } - } ~> check { - response should haveContentEncoding(deflate) - strictify(responseEntity) shouldEqual HttpEntity(ContentType(`text/plain`, `UTF-8`), helloDeflated) - } - } - "decode an unencoded request and produce a GZIP encoded response if the request has an `Accept-Encoding: gzip` header" in { - Post("/", "Hello") ~> `Accept-Encoding`(gzip) ~> { - decodeEncode { echoRequestContent } - } ~> check { - response should haveContentEncoding(gzip) - strictify(responseEntity) shouldEqual HttpEntity(ContentType(`text/plain`, `UTF-8`), helloGzipped) - } - } - } - - "the default marshaller" should { - "allow compressed responses with no body for informational messages" in { - Get() ~> `Accept-Encoding`(HttpEncodings.compress) ~> { - encodeResponse { - complete { StatusCodes.Continue } - } - } ~> check { - status shouldBe StatusCodes.Continue - } - } - "allow gzipped responses with no body for 204 messages" in { - Get() ~> `Accept-Encoding`(HttpEncodings.gzip) ~> { - encodeResponse { - complete { StatusCodes.NoContent } - } - } ~> check { - status shouldBe StatusCodes.NoContent - } - } - } - - def compress(input: String, encoder: Encoder): ByteString = { - val compressor = encoder.newCompressor - compressor.compressAndFlush(ByteString(input)) ++ compressor.finish() - } - - def hexDump(bytes: Array[Byte]) = bytes.map("%02x" format _).mkString - def fromHexDump(dump: String) = dump.grouped(2).toArray.map(chars ⇒ Integer.parseInt(new String(chars), 16).toByte) - - def haveNoContentEncoding: Matcher[HttpResponse] = be(None) compose { (_: HttpResponse).header[`Content-Encoding`] } - def haveContentEncoding(encoding: HttpEncoding): Matcher[HttpResponse] = - be(Some(`Content-Encoding`(encoding))) compose { (_: HttpResponse).header[`Content-Encoding`] } - - def readAs(string: String, charset: String = "UTF8") = be(string) compose { (_: ByteString).decodeString(charset) } - - def strictify(entity: HttpEntity) = entity.toStrict(1.second).awaitResult(1.second) -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/CookieDirectivesSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/CookieDirectivesSpec.scala deleted file mode 100644 index 3e9c1f8a27..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/CookieDirectivesSpec.scala +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import akka.http.scaladsl.model._ -import StatusCodes.OK -import headers._ - -class CookieDirectivesSpec extends RoutingSpec { - - val deletedTimeStamp = DateTime.fromIsoDateTimeString("1800-01-01T00:00:00") - - "The 'cookie' directive" should { - "extract the respectively named cookie" in { - Get() ~> addHeader(Cookie("fancy" → "pants")) ~> { - cookie("fancy") { echoComplete } - } ~> check { responseAs[String] shouldEqual "fancy=pants" } - } - "reject the request if the cookie is not present" in { - Get() ~> { - cookie("fancy") { echoComplete } - } ~> check { rejection shouldEqual MissingCookieRejection("fancy") } - } - "properly pass through inner rejections" in { - Get() ~> addHeader(Cookie("fancy" → "pants")) ~> { - cookie("fancy") { c ⇒ reject(ValidationRejection("Dont like " + c.value)) } - } ~> check { rejection shouldEqual ValidationRejection("Dont like pants") } - } - } - - "The 'deleteCookie' directive" should { - "add a respective Set-Cookie headers to successful responses" in { - Get() ~> { - deleteCookie("myCookie", "test.com") { completeOk } - } ~> check { - status shouldEqual OK - header[`Set-Cookie`] shouldEqual Some(`Set-Cookie`(HttpCookie("myCookie", "deleted", expires = deletedTimeStamp, - domain = Some("test.com")))) - } - } - - "support deleting multiple cookies at a time" in { - Get() ~> { - deleteCookie(HttpCookie("myCookie", "test.com"), HttpCookie("myCookie2", "foobar.com")) { completeOk } - } ~> check { - status shouldEqual OK - headers.collect { case `Set-Cookie`(x) ⇒ x } shouldEqual List( - HttpCookie("myCookie", "deleted", expires = deletedTimeStamp), - HttpCookie("myCookie2", "deleted", expires = deletedTimeStamp)) - } - } - } - - "The 'optionalCookie' directive" should { - "produce a `Some(cookie)` extraction if the cookie is present" in { - Get() ~> Cookie("abc" → "123") ~> { - optionalCookie("abc") { echoComplete } - } ~> check { responseAs[String] shouldEqual "Some(abc=123)" } - } - "produce a `None` extraction if the cookie is not present" in { - Get() ~> optionalCookie("abc") { echoComplete } ~> check { responseAs[String] shouldEqual "None" } - } - "let rejections from its inner route pass through" in { - Get() ~> { - optionalCookie("test-cookie") { _ ⇒ - validate(false, "ouch") { completeOk } - } - } ~> check { rejection shouldEqual ValidationRejection("ouch") } - } - } - - "The 'setCookie' directive" should { - "add a respective Set-Cookie headers to successful responses" in { - Get() ~> { - setCookie(HttpCookie("myCookie", "test.com")) { completeOk } - } ~> check { - status shouldEqual OK - header[`Set-Cookie`] shouldEqual Some(`Set-Cookie`(HttpCookie("myCookie", "test.com"))) - } - } - - "support setting multiple cookies at a time" in { - Get() ~> { - setCookie(HttpCookie("myCookie", "test.com"), HttpCookie("myCookie2", "foobar.com")) { completeOk } - } ~> check { - status shouldEqual OK - headers.collect { case `Set-Cookie`(x) ⇒ x } shouldEqual List( - HttpCookie("myCookie", "test.com"), HttpCookie("myCookie2", "foobar.com")) - } - } - } -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/DebuggingDirectivesSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/DebuggingDirectivesSpec.scala deleted file mode 100644 index b7fbb32585..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/DebuggingDirectivesSpec.scala +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import akka.event.LoggingAdapter -import akka.http.impl.util._ - -class DebuggingDirectivesSpec extends RoutingSpec { - var debugMsg = "" - - def resetDebugMsg(): Unit = { debugMsg = "" } - - val log = new LoggingAdapter { - def isErrorEnabled = true - def isWarningEnabled = true - def isInfoEnabled = true - def isDebugEnabled = true - - def notifyError(message: String): Unit = {} - def notifyError(cause: Throwable, message: String): Unit = {} - def notifyWarning(message: String): Unit = {} - def notifyInfo(message: String): Unit = {} - def notifyDebug(message: String): Unit = { debugMsg += message + '\n' } - } - - "The 'logRequest' directive" should { - "produce a proper log message for incoming requests" in { - val route = - withLog(log)( - logRequest("1")( - completeOk)) - - resetDebugMsg() - Get("/hello") ~> route ~> check { - response shouldEqual Ok - debugMsg shouldEqual "1: HttpRequest(HttpMethod(GET),http://example.com/hello,List(),HttpEntity.Strict(none/none,ByteString()),HttpProtocol(HTTP/1.1))\n" - } - } - } - - "The 'logResponse' directive" should { - "produce a proper log message for outgoing responses" in { - val route = - withLog(log)( - logResult("2")( - completeOk)) - - resetDebugMsg() - Get("/hello") ~> route ~> check { - response shouldEqual Ok - debugMsg shouldEqual "2: Complete(HttpResponse(200 OK,List(),HttpEntity.Strict(none/none,ByteString()),HttpProtocol(HTTP/1.1)))\n" - } - } - } - - "The 'logRequestResponse' directive" should { - "produce proper log messages for outgoing responses, thereby showing the corresponding request" in { - val route = - withLog(log)( - logRequestResult("3")( - completeOk)) - - resetDebugMsg() - Get("/hello") ~> route ~> check { - response shouldEqual Ok - debugMsg shouldEqual """|3: Response for - | Request : HttpRequest(HttpMethod(GET),http://example.com/hello,List(),HttpEntity.Strict(none/none,ByteString()),HttpProtocol(HTTP/1.1)) - | Response: Complete(HttpResponse(200 OK,List(),HttpEntity.Strict(none/none,ByteString()),HttpProtocol(HTTP/1.1))) - |""".stripMarginWithNewline("\n") - } - } - } - -} \ No newline at end of file diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/ExecutionDirectivesSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/ExecutionDirectivesSpec.scala deleted file mode 100644 index bafe421f2a..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/ExecutionDirectivesSpec.scala +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import akka.http.scaladsl.coding.Gzip -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers.HttpEncodings._ -import akka.http.scaladsl.model.headers._ -import scala.concurrent.Future -import akka.testkit.EventFilter -import org.scalatest.matchers.Matcher - -class ExecutionDirectivesSpec extends RoutingSpec { - object MyException extends RuntimeException - val handler = - ExceptionHandler { - case MyException ⇒ complete((500, "Pling! Plong! Something went wrong!!!")) - } - - "The `handleExceptions` directive" should { - "handle an exception strictly thrown in the inner route with the supplied exception handler" in { - exceptionShouldBeHandled { - handleExceptions(handler) { ctx ⇒ - throw MyException - } - } - } - "handle an Future.failed RouteResult with the supplied exception handler" in { - exceptionShouldBeHandled { - handleExceptions(handler) { ctx ⇒ - Future.failed(MyException) - } - } - } - "handle an eventually failed Future[RouteResult] with the supplied exception handler" in { - exceptionShouldBeHandled { - handleExceptions(handler) { ctx ⇒ - Future { - Thread.sleep(100) - throw MyException - } - } - } - } - "handle an exception happening during route building" in { - exceptionShouldBeHandled { - get { - handleExceptions(handler) { - throw MyException - } - } - } - } - "not interfere with alternative routes" in EventFilter[MyException.type](occurrences = 1).intercept { - Get("/abc") ~> - get { - handleExceptions(handler)(reject) ~ { ctx ⇒ - throw MyException - } - } ~> check { - status shouldEqual StatusCodes.InternalServerError - responseAs[String] shouldEqual "There was an internal server error." - } - } - "not handle other exceptions" in EventFilter[RuntimeException](occurrences = 1, message = "buh").intercept { - Get("/abc") ~> - get { - handleExceptions(handler) { - throw new RuntimeException("buh") - } - } ~> check { - status shouldEqual StatusCodes.InternalServerError - responseAs[String] shouldEqual "There was an internal server error." - } - } - "always fall back to a default content type" in EventFilter[RuntimeException](occurrences = 2, message = "buh2").intercept { - Get("/abc") ~> Accept(MediaTypes.`application/json`) ~> - get { - handleExceptions(handler) { - throw new RuntimeException("buh2") - } - } ~> check { - status shouldEqual StatusCodes.InternalServerError - responseAs[String] shouldEqual "There was an internal server error." - } - - Get("/abc") ~> Accept(MediaTypes.`text/xml`, MediaRanges.`*/*`.withQValue(0f)) ~> - get { - handleExceptions(handler) { - throw new RuntimeException("buh2") - } - } ~> check { - status shouldEqual StatusCodes.InternalServerError - responseAs[String] shouldEqual "There was an internal server error." - } - } - } - - "The `handleRejections` directive" should { - "handle encodeResponse inside RejectionHandler for non-success responses" in { - val rejectionHandler: RejectionHandler = RejectionHandler.newBuilder() - .handleNotFound { - encodeResponseWith(Gzip) { - complete((404, "Not here!")) - } - }.result() - - Get("/hell0") ~> - get { - handleRejections(rejectionHandler) { - encodeResponseWith(Gzip) { - path("hello") { - get { - complete(HttpEntity(ContentTypes.`text/plain(UTF-8)`, "world")) - } - } - } - } - } ~> check { - response should haveContentEncoding(gzip) - status shouldEqual StatusCodes.NotFound - } - } - } - - def exceptionShouldBeHandled(route: Route) = - Get("/abc") ~> route ~> check { - status shouldEqual StatusCodes.InternalServerError - responseAs[String] shouldEqual "Pling! Plong! Something went wrong!!!" - } - - def haveContentEncoding(encoding: HttpEncoding): Matcher[HttpResponse] = - be(Some(`Content-Encoding`(encoding))) compose { (_: HttpResponse).header[`Content-Encoding`] } -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/FileAndResourceDirectivesSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/FileAndResourceDirectivesSpec.scala deleted file mode 100644 index a59aab9c26..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/FileAndResourceDirectivesSpec.scala +++ /dev/null @@ -1,509 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import java.io.File - -import akka.http.scaladsl.settings.RoutingSettings -import akka.http.scaladsl.testkit.RouteTestTimeout - -import scala.concurrent.duration._ -import scala.concurrent.{ ExecutionContext, Future } -import scala.util.Properties -import org.scalatest.matchers.Matcher -import org.scalatest.{ Inside, Inspectors } -import akka.http.scaladsl.model.MediaTypes._ -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers._ -import akka.http.impl.util._ -import akka.http.scaladsl.TestUtils.writeAllText -import akka.http.scaladsl.model.Uri.Path - -class FileAndResourceDirectivesSpec extends RoutingSpec with Inspectors with Inside { - - // operations touch files, can be randomly hit by slowness - implicit val routeTestTimeout = RouteTestTimeout(3.seconds) - - // need to serve from the src directory, when sbt copies the resource directory over to the - // target directory it will resolve symlinks in the process - val testRoot = new File("akka-http-tests/src/test/resources") - require(testRoot.exists(), s"testRoot was not found at ${testRoot.getAbsolutePath}") - - override def testConfigSource = "akka.http.routing.range-coalescing-threshold = 1" - - "getFromFile" should { - "reject non-GET requests" in { - Put() ~> getFromFile("some") ~> check { handled shouldEqual false } - } - "reject requests to non-existing files" in { - Get() ~> getFromFile("nonExistentFile") ~> check { handled shouldEqual false } - } - "reject requests to directories" in { - Get() ~> getFromFile(Properties.javaHome) ~> check { handled shouldEqual false } - } - "return the file content with the MediaType matching the file extension" in { - val file = File.createTempFile("akka Http Test", ".PDF") - try { - writeAllText("This is PDF", file) - Get() ~> getFromFile(file.getPath) ~> check { - mediaType shouldEqual `application/pdf` - charsetOption shouldEqual None - responseAs[String] shouldEqual "This is PDF" - headers should contain(`Last-Modified`(DateTime(file.lastModified))) - } - } finally file.delete - } - "return the file content with MediaType 'application/octet-stream' on unknown file extensions" in { - val file = File.createTempFile("akkaHttpTest", null) - try { - writeAllText("Some content", file) - Get() ~> getFromFile(file) ~> check { - mediaType shouldEqual `application/octet-stream` - responseAs[String] shouldEqual "Some content" - } - } finally file.delete - } - - "return a single range from a file" in { - val file = File.createTempFile("akkaHttpTest", null) - try { - writeAllText("ABCDEFGHIJKLMNOPQRSTUVWXYZ", file) - Get() ~> addHeader(Range(ByteRange(0, 10))) ~> getFromFile(file) ~> check { - status shouldEqual StatusCodes.PartialContent - headers should contain(`Content-Range`(ContentRange(0, 10, 26))) - responseAs[String] shouldEqual "ABCDEFGHIJK" - } - } finally file.delete - } - - "return multiple ranges from a file at once" in { - val file = File.createTempFile("akkaHttpTest", null) - try { - writeAllText("ABCDEFGHIJKLMNOPQRSTUVWXYZ", file) - val rangeHeader = Range(ByteRange(1, 10), ByteRange.suffix(10)) - Get() ~> addHeader(rangeHeader) ~> getFromFile(file, ContentTypes.`text/plain(UTF-8)`) ~> check { - status shouldEqual StatusCodes.PartialContent - header[`Content-Range`] shouldEqual None - mediaType.withParams(Map.empty) shouldEqual `multipart/byteranges` - - val parts = responseAs[Multipart.ByteRanges].toStrict(1.second).awaitResult(3.seconds).strictParts - parts.map(_.entity.data.utf8String) should contain theSameElementsAs List("BCDEFGHIJK", "QRSTUVWXYZ") - } - } finally file.delete - } - - "properly handle zero-byte files" in { - val file = File.createTempFile("akkaHttpTest", null) - try { - Get() ~> getFromFile(file) ~> check { - mediaType shouldEqual NoMediaType - responseAs[String] shouldEqual "" - } - } finally file.delete - } - - "support precompressed files with registered MediaType" in { - val file = File.createTempFile("akkaHttpTest", ".svgz") - try { - writeAllText("123", file) - Get() ~> getFromFile(file) ~> check { - mediaType shouldEqual `image/svg+xml` - header[`Content-Encoding`] shouldEqual Some(`Content-Encoding`(HttpEncodings.gzip)) - responseAs[String] shouldEqual "123" - } - } finally file.delete - } - - "support files with registered MediaType and .gz suffix" in { - val file = File.createTempFile("akkaHttpTest", ".js.gz") - try { - writeAllText("456", file) - Get() ~> getFromFile(file) ~> check { - mediaType shouldEqual `application/javascript` - header[`Content-Encoding`] shouldEqual Some(`Content-Encoding`(HttpEncodings.gzip)) - responseAs[String] shouldEqual "456" - } - } finally file.delete - } - } - - "getFromDirectory" should { - def _getFromDirectory(directory: String) = getFromDirectory(new File(testRoot, directory).getCanonicalPath) - - "reject non-GET requests" in { - Put() ~> _getFromDirectory("someDir") ~> check { handled shouldEqual false } - } - "reject requests to non-existing files" in { - Get("nonExistentFile") ~> _getFromDirectory("subDirectory") ~> check { handled shouldEqual false } - } - "reject requests to directories" in { - Get("sub") ~> _getFromDirectory("someDir") ~> check { handled shouldEqual false } - } - "reject path traversal attempts" in { - def route(uri: String) = - mapRequestContext(_.withUnmatchedPath(Path("/" + uri))) { _getFromDirectory("someDir/sub") } - - Get() ~> route("file.html") ~> check { handled shouldEqual true } - - def shouldReject(prefix: String) = - Get() ~> route(prefix + "fileA.txt") ~> check { handled shouldEqual false } - shouldReject("../") // resolved - shouldReject("%5c../") - shouldReject("%2e%2e%2f") - shouldReject("%2e%2e/") // resolved - shouldReject("..%2f") - shouldReject("%2e%2e%5c") - shouldReject("%2e%2e\\") - shouldReject("..\\") - shouldReject("\\") - shouldReject("%5c") - shouldReject("..%5c") - shouldReject("..%255c") - shouldReject("..%c0%af") - shouldReject("..%c1%9c") - } - "return the file content with the MediaType matching the file extension" in { - Get("fileA.txt") ~> _getFromDirectory("someDir") ~> check { - mediaType shouldEqual `text/plain` - charsetOption shouldEqual Some(HttpCharsets.`UTF-8`) - responseAs[String] shouldEqual "123" - val lastModified = new File(testRoot, "someDir/fileA.txt").lastModified() - headers should contain(`Last-Modified`(DateTime(lastModified))) - } - } - "return the file content with the MediaType matching the file extension (unicode chars in filename)" in { - Get("sample%20sp%c3%a4ce.PDF") ~> _getFromDirectory("sübdir") ~> check { - mediaType shouldEqual `application/pdf` - charsetOption shouldEqual None - responseAs[String] shouldEqual "This is PDF" - val lastModified = new File(testRoot, "sübdir/sample späce.PDF").lastModified() - headers should contain(`Last-Modified`(DateTime(lastModified))) - } - } - "not follow symbolic links to find a file" in { - Get("linked-dir/empty.pdf") ~> _getFromDirectory("dirWithLink") ~> check { - handled shouldBe false - /* TODO: resurrect following links under an option - responseAs[String] shouldEqual "123" - mediaType shouldEqual `application/pdf`*/ - } - } - } - - "getFromResource" should { - "reject non-GET requests" in { - Put() ~> getFromResource("some") ~> check { handled shouldEqual false } - } - "reject requests to non-existing resources" in { - Get() ~> getFromResource("nonExistingResource") ~> check { handled shouldEqual false } - } - "reject requests to directory resources" in { - Get() ~> getFromResource("someDir") ~> check { handled shouldEqual false } - } - "reject requests to directory resources with trailing slash" in { - Get() ~> getFromResource("someDir/") ~> check { handled shouldEqual false } - } - "reject requests to directory resources from an archive " in { - Get() ~> getFromResource("com/typesafe/config") ~> check { handled shouldEqual false } - } - "reject requests to directory resources from an archive with trailing slash" in { - Get() ~> getFromResource("com/typesafe/config/") ~> check { handled shouldEqual false } - } - "return the resource from an archive with spaces and umlauts" in { - // contained within lib/jar with späces.jar - Get() ~> getFromResource("test-resource.txt") ~> check { - mediaType shouldEqual `text/plain` - responseAs[String] shouldEqual "I have spaces, too!" - } - } - "return the resource content with the MediaType matching the file extension" in { - val route = getFromResource("sample.html") - - def runCheck() = - Get() ~> route ~> check { - mediaType shouldEqual `text/html` - forAtLeast(1, headers) { h ⇒ - inside(h) { - case `Last-Modified`(dt) ⇒ - DateTime(2011, 7, 1) should be < dt - dt.clicks should be < System.currentTimeMillis() - } - } - responseAs[String] shouldEqual "

Lorem ipsum!

" - } - - runCheck() - runCheck() // additional test to check that no internal state is kept - } - "return the resource content from an archive" in { - Get() ~> getFromResource("com/typesafe/config/Config.class") ~> check { - mediaType shouldEqual `application/octet-stream` - responseEntity.toStrict(1.second).awaitResult(1.second).data.asByteBuffer.getInt shouldEqual 0xCAFEBABE - } - } - "return the file content with MediaType 'application/octet-stream' on unknown file extensions" in { - Get() ~> getFromResource("sample.xyz") ~> check { - mediaType shouldEqual `application/octet-stream` - responseAs[String] shouldEqual "XyZ" - } - } - "properly handle zero-byte files" in { - Get() ~> getFromResource("subDirectory/fileA.txt") ~> check { - mediaType shouldEqual NoMediaType - responseAs[String] shouldEqual "" - } - } - } - - "getFromResourceDirectory" should { - "reject requests to non-existing resources" in { - Get("not/found") ~> getFromResourceDirectory("subDirectory") ~> check { handled shouldEqual false } - } - val verify = check { - mediaType shouldEqual `application/pdf` - responseAs[String] shouldEqual "123" - } - "return the resource content with the MediaType matching the file extension - example 1" in { - Get("empty.pdf") ~> getFromResourceDirectory("subDirectory") ~> verify - } - "return the resource content with the MediaType matching the file extension - example 2" in { - Get("empty.pdf") ~> getFromResourceDirectory("subDirectory/") ~> verify - } - "return the resource content with the MediaType matching the file extension - example 3" in { - Get("subDirectory/empty.pdf") ~> getFromResourceDirectory("") ~> verify - } - "return the resource content from an archive" in { - Get("Config.class") ~> getFromResourceDirectory("com/typesafe/config") ~> check { - mediaType shouldEqual `application/octet-stream` - responseEntity.toStrict(1.second).awaitResult(1.second).data.asByteBuffer.getInt shouldEqual 0xCAFEBABE - } - } - "reject requests to directory resources" in { - Get() ~> getFromResourceDirectory("subDirectory") ~> check { handled shouldEqual false } - } - "reject requests to directory resources with trailing slash" in { - Get() ~> getFromResourceDirectory("subDirectory/") ~> check { handled shouldEqual false } - } - "reject requests to sub directory resources" in { - Get("sub") ~> getFromResourceDirectory("someDir") ~> check { handled shouldEqual false } - } - "reject requests to sub directory resources with trailing slash" in { - Get("sub/") ~> getFromResourceDirectory("someDir") ~> check { handled shouldEqual false } - } - "reject requests to directory resources from an archive" in { - Get() ~> getFromResourceDirectory("com/typesafe/config") ~> check { handled shouldEqual false } - } - "reject requests to directory resources from an archive with trailing slash" in { - Get() ~> getFromResourceDirectory("com/typesafe/config/") ~> check { handled shouldEqual false } - } - } - - "listDirectoryContents" should { - val base = new File(getClass.getClassLoader.getResource("").toURI).getPath - new File(base, "subDirectory/emptySub").mkdir() - def eraseDateTime(s: String) = s.replaceAll("""\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d""", "xxxx-xx-xx xx:xx:xx") - implicit val settings = RoutingSettings.default.withRenderVanityFooter(false) - - "properly render a simple directory" in { - Get() ~> listDirectoryContents(base + "/someDir") ~> check { - eraseDateTime(responseAs[String]) shouldEqual prep { - """ - |Index of / - | - |

Index of /

- |
- |
-            |sub/             xxxx-xx-xx xx:xx:xx
-            |fileA.txt        xxxx-xx-xx xx:xx:xx            3  B
-            |fileB.xml        xxxx-xx-xx xx:xx:xx            0  B
-            |
- |
- | - | - |""" - } - } - } - "properly render a sub directory" in { - Get("/sub/") ~> listDirectoryContents(base + "/someDir") ~> check { - eraseDateTime(responseAs[String]) shouldEqual prep { - """ - |Index of /sub/ - | - |

Index of /sub/

- |
- |
-            |../
-            |file.html        xxxx-xx-xx xx:xx:xx            0  B
-            |
- |
- | - | - |""" - } - } - } - "properly render the union of several directories" in { - Get() ~> listDirectoryContents(base + "/someDir", base + "/subDirectory") ~> check { - eraseDateTime(responseAs[String]) shouldEqual prep { - """ - |Index of / - | - |

Index of /

- |
- |
-            |emptySub/        xxxx-xx-xx xx:xx:xx
-            |sub/             xxxx-xx-xx xx:xx:xx
-            |empty.pdf        xxxx-xx-xx xx:xx:xx            3  B
-            |fileA.txt        xxxx-xx-xx xx:xx:xx            3  B
-            |fileB.xml        xxxx-xx-xx xx:xx:xx            0  B
-            |
- |
- | - | - |""" - } - } - } - "properly render an empty sub directory with vanity footer" in { - val settings = 0 // shadow implicit - Get("/emptySub/") ~> listDirectoryContents(base + "/subDirectory") ~> check { - eraseDateTime(responseAs[String]) shouldEqual prep { - """ - |Index of /emptySub/ - | - |

Index of /emptySub/

- |
- |
-            |../
-            |
- |
- |
- |rendered by Akka Http on xxxx-xx-xx xx:xx:xx - |
- | - | - |""" - } - } - } - "properly render an empty top-level directory" in { - Get() ~> listDirectoryContents(base + "/subDirectory/emptySub") ~> check { - eraseDateTime(responseAs[String]) shouldEqual prep { - """ - |Index of / - | - |

Index of /

- |
- |
-            |(no files)
-            |
- |
- | - | - |""" - } - } - } - "properly render a simple directory with a path prefix" in { - Get("/files/") ~> pathPrefix("files")(listDirectoryContents(base + "/someDir")) ~> check { - eraseDateTime(responseAs[String]) shouldEqual prep { - """ - |Index of /files/ - | - |

Index of /files/

- |
- |
-            |sub/             xxxx-xx-xx xx:xx:xx
-            |fileA.txt        xxxx-xx-xx xx:xx:xx            3  B
-            |fileB.xml        xxxx-xx-xx xx:xx:xx            0  B
-            |
- |
- | - | - |""" - } - } - } - "properly render a sub directory with a path prefix" in { - Get("/files/sub/") ~> pathPrefix("files")(listDirectoryContents(base + "/someDir")) ~> check { - eraseDateTime(responseAs[String]) shouldEqual prep { - """ - |Index of /files/sub/ - | - |

Index of /files/sub/

- |
- |
-            |../
-            |file.html        xxxx-xx-xx xx:xx:xx            0  B
-            |
- |
- | - | - |""" - } - } - } - "properly render an empty top-level directory with a path prefix" in { - Get("/files/") ~> pathPrefix("files")(listDirectoryContents(base + "/subDirectory/emptySub")) ~> check { - eraseDateTime(responseAs[String]) shouldEqual prep { - """ - |Index of /files/ - | - |

Index of /files/

- |
- |
-            |(no files)
-            |
- |
- | - | - |""" - } - } - } - "reject requests to file resources" in { - Get() ~> listDirectoryContents(base + "subDirectory/empty.pdf") ~> check { handled shouldEqual false } - } - "reject path traversal attempts" in { - def _listDirectoryContents(directory: String) = listDirectoryContents(new File(testRoot, directory).getCanonicalPath) - def route(uri: String) = - mapRequestContext(_.withUnmatchedPath(Path("/" + uri)).mapRequest(_.copy(uri = "/" + uri))) { - _listDirectoryContents("someDir/sub") - } - - Get() ~> route("") ~> check { - handled shouldEqual true - } - - def shouldReject(prefix: String) = - Get() ~> route(prefix) ~> check { - handled shouldEqual false - } - shouldReject("../") // resolved - shouldReject("%5c../") - shouldReject("%2e%2e%2f") - shouldReject("%2e%2e/") // resolved - shouldReject("..%2f") - shouldReject("%2e%2e%5c") - shouldReject("%2e%2e\\") - shouldReject("..\\") - shouldReject("\\") - shouldReject("%5c") - shouldReject("..%5c") - shouldReject("..%255c") - shouldReject("..%c0%af") - shouldReject("..%c1%9c") - } - } - - def prep(s: String) = s.stripMarginWithNewline("\n") - - def evaluateTo[T](t: T, atMost: Duration = 100.millis)(implicit ec: ExecutionContext): Matcher[Future[T]] = - be(t).compose[Future[T]] { fut ⇒ - fut.awaitResult(atMost) - } -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/FileUploadDirectivesSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/FileUploadDirectivesSpec.scala deleted file mode 100644 index d0c23a0789..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/FileUploadDirectivesSpec.scala +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server.directives - -import java.io.{ File, FileInputStream } - -import akka.http.scaladsl.model._ -import akka.http.scaladsl.server.{ MissingFormFieldRejection, RoutingSpec } -import akka.http.scaladsl.testkit.RouteTestTimeout -import akka.util.ByteString -import scala.concurrent.duration._ - -class FileUploadDirectivesSpec extends RoutingSpec { - - // tests touches filesystem, so reqs may take longer than the default of 1.second to complete - implicit val routeTimeout = RouteTestTimeout(3.seconds) - - "the uploadedFile directive" should { - - "write a posted file to a temporary file on disk" in { - - val xml = "42" - - val simpleMultipartUpload = - Multipart.FormData(Multipart.FormData.BodyPart.Strict( - "fieldName", - HttpEntity(ContentTypes.`text/xml(UTF-8)`, xml), - Map("filename" → "age.xml"))) - - @volatile var file: Option[File] = None - - try { - Post("/", simpleMultipartUpload) ~> { - uploadedFile("fieldName") { - case (info, tmpFile) ⇒ - file = Some(tmpFile) - complete(info.toString) - } - } ~> check { - file.isDefined === true - responseAs[String] === FileInfo("fieldName", "age.xml", ContentTypes.`text/xml(UTF-8)`).toString - read(file.get) === xml - } - } finally { - file.foreach(_.delete()) - } - } - } - - "the fileUpload directive" should { - - def echoAsAService = - extractRequestContext { ctx ⇒ - implicit val mat = ctx.materializer - - fileUpload("field1") { - case (info, bytes) ⇒ - // stream the bytes somewhere - val allBytesF = bytes.runFold(ByteString.empty) { (all, bytes) ⇒ all ++ bytes } - - // sum all individual file sizes - onSuccess(allBytesF) { allBytes ⇒ - complete(allBytes) - } - } - } - - "stream the file upload" in { - // byte count as a service ;) - val route = echoAsAService - - // tests: - val str1 = "some data" - val multipartForm = - Multipart.FormData(Multipart.FormData.BodyPart.Strict( - "field1", - HttpEntity(ContentTypes.`text/plain(UTF-8)`, str1), - Map("filename" → "data1.txt"))) - - Post("/", multipartForm) ~> route ~> check { - status shouldEqual StatusCodes.OK - responseAs[String] shouldEqual str1 - } - - } - - "stream the first file upload if multiple with the same name are posted" in { - // byte count as a service ;) - val route = echoAsAService - - // tests: - val str1 = "some data" - val str2 = "other data" - val multipartForm = - Multipart.FormData( - Multipart.FormData.BodyPart.Strict( - "field1", - HttpEntity(ContentTypes.`text/plain(UTF-8)`, str1), - Map("filename" → "data1.txt")), - Multipart.FormData.BodyPart.Strict( - "field1", - HttpEntity(ContentTypes.`text/plain(UTF-8)`, str2), - Map("filename" → "data2.txt"))) - - Post("/", multipartForm) ~> route ~> check { - status shouldEqual StatusCodes.OK - responseAs[String] shouldEqual str1 - } - - } - - "reject the file upload if the field name is missing" in { - // byte count as a service ;) - val route = - extractRequestContext { ctx ⇒ - implicit val mat = ctx.materializer - - fileUpload("missing") { - case (info, bytes) ⇒ - // stream the bytes somewhere - val allBytesF = bytes.runFold(ByteString.empty) { (all, bytes) ⇒ all ++ bytes } - - // sum all individual file sizes - onSuccess(allBytesF) { allBytes ⇒ - complete(allBytes) - } - } - } - - // tests: - val str1 = "some data" - val multipartForm = - Multipart.FormData(Multipart.FormData.BodyPart.Strict( - "field1", - HttpEntity(ContentTypes.`text/plain(UTF-8)`, str1), - Map("filename" → "data1.txt"))) - - Post("/", multipartForm) ~> route ~> check { - rejection === MissingFormFieldRejection("missing") - } - - } - - } - - private def read(file: File): String = { - val in = new FileInputStream(file) - try { - val buffer = new Array[Byte](1024) - in.read(buffer) - new String(buffer, "UTF-8") - } finally { - in.close() - } - } - -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/FormFieldDirectivesSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/FormFieldDirectivesSpec.scala deleted file mode 100644 index f253dabb30..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/FormFieldDirectivesSpec.scala +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import akka.http.scaladsl.common.StrictForm -import akka.http.scaladsl.marshallers.xml.ScalaXmlSupport -import akka.http.scaladsl.unmarshalling.Unmarshaller.HexInt -import akka.http.scaladsl.model._ -import MediaTypes._ - -class FormFieldDirectivesSpec extends RoutingSpec { - // FIXME: unfortunately, it has make a come back, this time it's reproducible ... - import akka.http.scaladsl.server.directives.FormFieldDirectives.FieldMagnet - - implicit val nodeSeqUnmarshaller = - ScalaXmlSupport.nodeSeqUnmarshaller(`text/xml`, `text/html`, `text/plain`) - - val nodeSeq: xml.NodeSeq = yes - val urlEncodedForm = FormData(Map("firstName" → "Mike", "age" → "42")) - val urlEncodedFormWithVip = FormData(Map("firstName" → "Mike", "age" → "42", "VIP" → "true", "super" → "no")) - val multipartForm = Multipart.FormData { - Map( - "firstName" → HttpEntity("Mike"), - "age" → HttpEntity(ContentTypes.`text/xml(UTF-8)`, "42"), - "VIPBoolean" → HttpEntity("true")) - } - val multipartFormWithTextHtml = Multipart.FormData { - Map( - "firstName" → HttpEntity("Mike"), - "age" → HttpEntity(ContentTypes.`text/xml(UTF-8)`, "42"), - "VIP" → HttpEntity(ContentTypes.`text/html(UTF-8)`, "yes"), - "super" → HttpEntity("no")) - } - val multipartFormWithFile = Multipart.FormData( - Multipart.FormData.BodyPart.Strict("file", HttpEntity(ContentTypes.`text/xml(UTF-8)`, "42"), - Map("filename" → "age.xml"))) - - "The 'formFields' extraction directive" should { - "properly extract the value of www-urlencoded form fields" in { - Post("/", urlEncodedForm) ~> { - formFields('firstName, "age".as[Int], 'sex.?, "VIP" ? false) { (firstName, age, sex, vip) ⇒ - complete(firstName + age + sex + vip) - } - } ~> check { responseAs[String] shouldEqual "Mike42Nonefalse" } - } - "properly extract the value of www-urlencoded form fields when an explicit unmarshaller is given" in { - Post("/", urlEncodedForm) ~> { - formFields('firstName, "age".as(HexInt), 'sex.?, "VIP" ? false) { (firstName, age, sex, vip) ⇒ - complete(firstName + age + sex + vip) - } - } ~> check { responseAs[String] shouldEqual "Mike66Nonefalse" } - } - "properly extract the value of multipart form fields" in { - Post("/", multipartForm) ~> { - formFields('firstName, "age", 'sex.?, "VIP" ? nodeSeq) { (firstName, age, sex, vip) ⇒ - complete(firstName + age + sex + vip) - } - } ~> check { responseAs[String] shouldEqual "Mike42Noneyes" } - } - "extract StrictForm.FileData from a multipart part" in { - Post("/", multipartFormWithFile) ~> { - formFields('file.as[StrictForm.FileData]) { - case StrictForm.FileData(name, HttpEntity.Strict(ct, data)) ⇒ - complete(s"type ${ct.mediaType} length ${data.length} filename ${name.get}") - } - } ~> check { responseAs[String] shouldEqual "type text/xml length 13 filename age.xml" } - } - "reject the request with a MissingFormFieldRejection if a required form field is missing" in { - Post("/", urlEncodedForm) ~> { - formFields('firstName, "age", 'sex, "VIP" ? false) { (firstName, age, sex, vip) ⇒ - complete(firstName + age + sex + vip) - } - } ~> check { rejection shouldEqual MissingFormFieldRejection("sex") } - } - "properly extract the value if only a urlencoded deserializer is available for a multipart field that comes without a" + - "Content-Type (or text/plain)" in { - Post("/", multipartForm) ~> { - formFields('firstName, "age", 'sex.?, "VIPBoolean" ? false) { (firstName, age, sex, vip) ⇒ - complete(firstName + age + sex + vip) - } - } ~> check { - responseAs[String] shouldEqual "Mike42Nonetrue" - } - } - "work even if only a FromStringUnmarshaller is available for a multipart field with custom Content-Type" in { - Post("/", multipartFormWithTextHtml) ~> { - formFields(('firstName, "age", 'super ? false)) { (firstName, age, vip) ⇒ - complete(firstName + age + vip) - } - } ~> check { - responseAs[String] shouldEqual "Mike42false" - } - } - "work even if only a FromEntityUnmarshaller is available for a www-urlencoded field" in { - Post("/", urlEncodedFormWithVip) ~> { - formFields('firstName, "age", 'sex.?, "super" ? nodeSeq) { (firstName, age, sex, vip) ⇒ - complete(firstName + age + sex + vip) - } - } ~> check { - responseAs[String] shouldEqual "Mike42Noneno" - } - } - } - "The 'formField' requirement directive" should { - "block requests that do not contain the required formField" in { - Post("/", urlEncodedForm) ~> { - formField('name ! "Mr. Mike") { completeOk } - } ~> check { handled shouldEqual false } - } - "block requests that contain the required parameter but with an unmatching value" in { - Post("/", urlEncodedForm) ~> { - formField('firstName ! "Pete") { completeOk } - } ~> check { handled shouldEqual false } - } - "let requests pass that contain the required parameter with its required value" in { - Post("/", urlEncodedForm) ~> { - formField('firstName ! "Mike") { completeOk } - } ~> check { response shouldEqual Ok } - } - } - - "The 'formField' requirement with explicit unmarshaller directive" should { - "block requests that do not contain the required formField" in { - Post("/", urlEncodedForm) ~> { - formField('oldAge.as(HexInt) ! 78) { completeOk } - } ~> check { handled shouldEqual false } - } - "block requests that contain the required parameter but with an unmatching value" in { - Post("/", urlEncodedForm) ~> { - formField('age.as(HexInt) ! 78) { completeOk } - } ~> check { handled shouldEqual false } - } - "let requests pass that contain the required parameter with its required value" in { - Post("/", urlEncodedForm) ~> { - formField('age.as(HexInt) ! 66 /* hex! */ ) { completeOk } - } ~> check { response shouldEqual Ok } - } - } - - "The 'formField' repeated directive" should { - "extract an empty Iterable when the parameter is absent" in { - Post("/", FormData("age" → "42")) ~> { - formField('hobby.*) { echoComplete } - } ~> check { responseAs[String] === "List()" } - } - "extract all occurrences into an Iterable when parameter is present" in { - Post("/", FormData("age" → "42", "hobby" → "cooking", "hobby" → "reading")) ~> { - formField('hobby.*) { echoComplete } - } ~> check { responseAs[String] === "List(cooking, reading)" } - } - "extract as Iterable[Int]" in { - Post("/", FormData("age" → "42", "number" → "3", "number" → "5")) ~> { - formField('number.as[Int].*) { echoComplete } - } ~> check { responseAs[String] === "List(3, 5)" } - } - "extract as Iterable[Int] with an explicit deserializer" in { - Post("/", FormData("age" → "42", "number" → "3", "number" → "A")) ~> { - formField('number.as(HexInt).*) { echoComplete } - } ~> check { responseAs[String] === "List(3, 10)" } - } - } - - "The 'formFieldMap' directive" should { - "extract fields with different keys" in { - Post("/", FormData("age" → "42", "numberA" → "3", "numberB" → "5")) ~> { - formFieldMap { echoComplete } - } ~> check { responseAs[String] shouldEqual "Map(age -> 42, numberA -> 3, numberB -> 5)" } - } - } - - "The 'formFieldSeq' directive" should { - "extract all fields" in { - Post("/", FormData("age" → "42", "number" → "3", "number" → "5")) ~> { - formFieldSeq { echoComplete } - } ~> check { responseAs[String] shouldEqual "Vector((age,42), (number,3), (number,5))" } - } - "produce empty Seq when FormData is empty" in { - Post("/", FormData.Empty) ~> { - formFieldSeq { echoComplete } - } ~> check { responseAs[String] shouldEqual "Vector()" } - } - } - - "The 'formFieldMultiMap' directive" should { - "extract fields with different keys (with duplicates)" in { - Post("/", FormData("age" → "42", "number" → "3", "number" → "5")) ~> { - formFieldMultiMap { echoComplete } - } ~> check { responseAs[String] shouldEqual "Map(age -> List(42), number -> List(5, 3))" } - } - } -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/FutureDirectivesSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/FutureDirectivesSpec.scala deleted file mode 100644 index 6df986d115..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/FutureDirectivesSpec.scala +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import akka.http.scaladsl.model.StatusCodes -import akka.pattern.{ CircuitBreaker, CircuitBreakerOpenException } - -import scala.concurrent.Future -import akka.testkit.EventFilter -import org.scalatest.Inside - -import scala.concurrent.duration._ - -class FutureDirectivesSpec extends RoutingSpec with Inside { - - class TestException(msg: String) extends Exception(msg) - object TestException extends Exception("XXX") - def throwTestException[T](msgPrefix: String): T ⇒ Nothing = t ⇒ throw new TestException(msgPrefix + t) - - implicit val exceptionHandler = ExceptionHandler { - case e: TestException ⇒ complete((StatusCodes.InternalServerError, "Oops. " + e)) - } - - trait TestWithCircuitBreaker { - val breakerResetTimeout = 500.millis - val breaker = new CircuitBreaker(system.scheduler, maxFailures = 1, callTimeout = 10.seconds, breakerResetTimeout) - def openBreaker() = breaker.withCircuitBreaker(Future.failed(new Exception("boom"))) - } - - "The `onComplete` directive" should { - "unwrap a Future in the success case" in { - var i = 0 - def nextNumber() = { i += 1; i } - val route = onComplete(Future.successful(nextNumber())) { echoComplete } - Get() ~> route ~> check { - responseAs[String] shouldEqual "Success(1)" - } - Get() ~> route ~> check { - responseAs[String] shouldEqual "Success(2)" - } - } - "unwrap a Future in the failure case" in { - Get() ~> onComplete(Future.failed[String](new RuntimeException("no"))) { echoComplete } ~> check { - responseAs[String] shouldEqual "Failure(java.lang.RuntimeException: no)" - } - } - "catch an exception in the success case" in { - Get() ~> onComplete(Future.successful("ok")) { throwTestException("EX when ") } ~> check { - status shouldEqual StatusCodes.InternalServerError - responseAs[String] shouldEqual s"Oops. akka.http.scaladsl.server.directives.FutureDirectivesSpec$$TestException: EX when Success(ok)" - } - } - "catch an exception in the failure case" in { - Get() ~> onComplete(Future.failed[String](new RuntimeException("no"))) { throwTestException("EX when ") } ~> check { - status shouldEqual StatusCodes.InternalServerError - responseAs[String] shouldEqual s"Oops. akka.http.scaladsl.server.directives.FutureDirectivesSpec$$TestException: EX when Failure(java.lang.RuntimeException: no)" - } - } - } - - "The `onCompleteWithBreaker` directive" should { - "unwrap a Future in the success case" in new TestWithCircuitBreaker { - var i = 0 - def nextNumber() = { i += 1; i } - val route = onCompleteWithBreaker(breaker)(Future.successful(nextNumber())) { echoComplete } - Get() ~> route ~> check { - responseAs[String] shouldEqual "Success(1)" - } - Get() ~> route ~> check { - responseAs[String] shouldEqual "Success(2)" - } - } - "unwrap a Future in the failure case" in new TestWithCircuitBreaker { - Get() ~> onCompleteWithBreaker(breaker)(Future.failed[String](new RuntimeException("no"))) { echoComplete } ~> check { - responseAs[String] shouldEqual "Failure(java.lang.RuntimeException: no)" - } - } - "fail fast if the circuit breaker is open" in new TestWithCircuitBreaker { - openBreaker() - Get() ~> onCompleteWithBreaker(breaker)(Future.successful(1)) { echoComplete } ~> check { - inside(rejection) { - case CircuitBreakerOpenRejection(_) ⇒ - } - } - } - "stop failing fast when the circuit breaker closes" in new TestWithCircuitBreaker { - openBreaker() - Thread.sleep(breakerResetTimeout.toMillis + 200) - Get() ~> onCompleteWithBreaker(breaker)(Future.successful(1)) { echoComplete } ~> check { - responseAs[String] shouldEqual "Success(1)" - } - } - "catch an exception in the success case" in new TestWithCircuitBreaker { - Get() ~> onCompleteWithBreaker(breaker)(Future.successful("ok")) { throwTestException("EX when ") } ~> check { - status shouldEqual StatusCodes.InternalServerError - responseAs[String] shouldEqual s"Oops. akka.http.scaladsl.server.directives.FutureDirectivesSpec$$TestException: EX when Success(ok)" - } - } - "catch an exception in the failure case" in new TestWithCircuitBreaker { - Get() ~> onCompleteWithBreaker(breaker)(Future.failed[String](new RuntimeException("no"))) { throwTestException("EX when ") } ~> check { - status shouldEqual StatusCodes.InternalServerError - responseAs[String] shouldEqual s"Oops. akka.http.scaladsl.server.directives.FutureDirectivesSpec$$TestException: EX when Failure(java.lang.RuntimeException: no)" - } - } - } - - "The `onSuccess` directive" should { - "unwrap a Future in the success case" in { - Get() ~> onSuccess(Future.successful("yes")) { echoComplete } ~> check { - responseAs[String] shouldEqual "yes" - } - } - "propagate the exception in the failure case" in EventFilter[Exception](occurrences = 1, message = "XXX").intercept { - Get() ~> onSuccess(Future.failed(TestException)) { echoComplete } ~> check { - status shouldEqual StatusCodes.InternalServerError - } - } - "catch an exception in the success case" in { - Get() ~> onSuccess(Future.successful("ok")) { throwTestException("EX when ") } ~> check { - status shouldEqual StatusCodes.InternalServerError - responseAs[String] shouldEqual s"Oops. akka.http.scaladsl.server.directives.FutureDirectivesSpec$$TestException: EX when ok" - } - } - "catch an exception in the failure case" in EventFilter[Exception](occurrences = 1, message = "XXX").intercept { - Get() ~> onSuccess(Future.failed(TestException)) { throwTestException("EX when ") } ~> check { - status shouldEqual StatusCodes.InternalServerError - responseAs[String] shouldEqual "There was an internal server error." - } - } - } - - "The `completeOrRecoverWith` directive" should { - "complete the request with the Future's value if the future succeeds" in { - Get() ~> completeOrRecoverWith(Future.successful("yes")) { echoComplete } ~> check { - responseAs[String] shouldEqual "yes" - } - } - "don't call the inner route if the Future succeeds" in { - Get() ~> completeOrRecoverWith(Future.successful("ok")) { throwTestException("EX when ") } ~> check { - status shouldEqual StatusCodes.OK - responseAs[String] shouldEqual "ok" - } - } - "recover using the inner route if the Future fails" in { - val route = completeOrRecoverWith(Future.failed[String](TestException)) { - case e ⇒ complete(s"Exception occurred: ${e.getMessage}") - } - - Get() ~> route ~> check { - responseAs[String] shouldEqual "Exception occurred: XXX" - } - } - "catch an exception during recovery" in { - Get() ~> completeOrRecoverWith(Future.failed[String](TestException)) { throwTestException("EX when ") } ~> check { - status shouldEqual StatusCodes.InternalServerError - responseAs[String] shouldEqual s"Oops. akka.http.scaladsl.server.directives.FutureDirectivesSpec$$TestException: EX when akka.http.scaladsl.server.directives.FutureDirectivesSpec$$TestException$$: XXX" - } - } - } -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/HeaderDirectivesSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/HeaderDirectivesSpec.scala deleted file mode 100644 index 6b636192d8..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/HeaderDirectivesSpec.scala +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server.directives - -import akka.http.scaladsl.model._ -import headers._ -import akka.http.scaladsl.server._ -import org.scalatest.Inside - -class HeaderDirectivesSpec extends RoutingSpec with Inside { - - "The headerValuePF directive" should { - lazy val myHeaderValue = headerValuePF { case Connection(tokens) ⇒ tokens.head } - - "extract the respective header value if a matching request header is present" in { - Get("/abc") ~> addHeader(Connection("close")) ~> myHeaderValue { echoComplete } ~> check { - responseAs[String] shouldEqual "close" - } - } - - "reject with an empty rejection set if no matching request header is present" in { - Get("/abc") ~> myHeaderValue { echoComplete } ~> check { rejections shouldEqual Nil } - } - - "reject with a MalformedHeaderRejection if the extract function throws an exception" in { - Get("/abc") ~> addHeader(Connection("close")) ~> { - (headerValuePF { case _ ⇒ sys.error("Naah!") }) { echoComplete } - } ~> check { - inside(rejection) { case MalformedHeaderRejection("Connection", "Naah!", _) ⇒ } - } - } - } - - "The headerValueByType directive" should { - val route = - headerValueByType[Origin]() { origin ⇒ - complete(s"The first origin was ${origin.origins.head}") - } - "extract a header if the type is matching" in { - val originHeader = Origin(HttpOrigin("http://localhost:8080")) - Get("abc") ~> originHeader ~> route ~> check { - responseAs[String] shouldEqual "The first origin was http://localhost:8080" - } - } - "reject a request if no header of the given type is present" in { - Get("abc") ~> route ~> check { - inside(rejection) { - case MissingHeaderRejection("Origin") ⇒ - } - } - } - "reject a request for missing header, and format it properly when header included special characters (e.g. `-`)" in { - val route = headerValueByType[`User-Agent`]() { agent ⇒ - complete(s"Agent: ${agent}") - } - Get("abc") ~> route ~> check { - inside(rejection) { - case MissingHeaderRejection("User-Agent") ⇒ - } - } - } - } - - "The headerValueByName directive" should { - lazy val route = - headerValueByName("Referer") { referer ⇒ - complete(s"The referer was $referer") - } - - "extract a header if the name is matching" in { - Get("abc") ~> RawHeader("Referer", "http://example.com") ~> route ~> check { - responseAs[String] shouldEqual "The referer was http://example.com" - } - } - - "extract a header with Symbol name" in { - lazy val symbolRoute = - headerValueByName('Referer) { referer ⇒ - complete(s"The symbol referer was $referer") - } - - Get("abc") ~> RawHeader("Referer", "http://example.com/symbol") ~> symbolRoute ~> check { - responseAs[String] shouldEqual "The symbol referer was http://example.com/symbol" - } - } - - "reject a request if no header of the given type is present" in { - Get("abc") ~> route ~> check { - inside(rejection) { - case MissingHeaderRejection("Referer") ⇒ - } - } - } - } - - "The optionalHeaderValueByName directive" should { - lazy val route = - optionalHeaderValueByName("Referer") { referer ⇒ - complete(s"The referer was $referer") - } - - "extract a header if the name is matching" in { - Get("abc") ~> RawHeader("Referer", "http://example.com") ~> route ~> check { - responseAs[String] shouldEqual "The referer was Some(http://example.com)" - } - } - - "extract a header with Symbol name" in { - lazy val symbolRoute = - optionalHeaderValueByName('Referer) { referer ⇒ - complete(s"The symbol referer was $referer") - } - - Get("abc") ~> RawHeader("Referer", "http://example.com/symbol") ~> symbolRoute ~> check { - responseAs[String] shouldEqual "The symbol referer was Some(http://example.com/symbol)" - } - } - - "extract None if no header of the given name is present" in { - Get("abc") ~> route ~> check { - responseAs[String] shouldEqual "The referer was None" - } - } - } - - "The optionalHeaderValue directive" should { - lazy val myHeaderValue = optionalHeaderValue { - case Connection(tokens) ⇒ Some(tokens.head) - case _ ⇒ None - } - - "extract the respective header value if a matching request header is present" in { - Get("/abc") ~> addHeader(Connection("close")) ~> myHeaderValue { echoComplete } ~> check { - responseAs[String] shouldEqual "Some(close)" - } - } - - "extract None if no matching request header is present" in { - Get("/abc") ~> myHeaderValue { echoComplete } ~> check { responseAs[String] shouldEqual "None" } - } - - "reject with a MalformedHeaderRejection if the extract function throws an exception" in { - Get("/abc") ~> addHeader(Connection("close")) ~> { - val myHeaderValue = optionalHeaderValue { case _ ⇒ sys.error("Naaah!") } - myHeaderValue { echoComplete } - } ~> check { - inside(rejection) { case MalformedHeaderRejection("Connection", "Naaah!", _) ⇒ } - } - } - } - - "The optionalHeaderValueByType directive" should { - val route = - optionalHeaderValueByType[Origin]() { - case Some(origin) ⇒ complete(s"The first origin was ${origin.origins.head}") - case None ⇒ complete("No Origin header found.") - } - "extract Some(header) if the type is matching" in { - val originHeader = Origin(HttpOrigin("http://localhost:8080")) - Get("abc") ~> originHeader ~> route ~> check { - responseAs[String] shouldEqual "The first origin was http://localhost:8080" - } - } - "extract None if no header of the given type is present" in { - Get("abc") ~> route ~> check { - responseAs[String] shouldEqual "No Origin header found." - } - } - } - - "The checkSameOrigin directive" should { - val correctOrigin = HttpOrigin("http://localhost:8080") - val route = checkSameOrigin(HttpOriginRange(correctOrigin)) { - complete("Result") - } - "handle request with correct origin headers" in { - Get("abc") ~> Origin(correctOrigin) ~> route ~> check { - status shouldEqual StatusCodes.OK - responseAs[String] shouldEqual "Result" - } - } - "reject request with missed origin header" in { - Get("abc") ~> route ~> check { - inside(rejection) { - case MissingHeaderRejection(headerName) ⇒ headerName shouldEqual Origin.name - } - } - } - "reject requests with invalid origin header value" in { - val invalidHttpOrigin = HttpOrigin("http://invalid.com") - val invalidOriginHeader = Origin(invalidHttpOrigin) - Get("abc") ~> invalidOriginHeader ~> route ~> check { - inside(rejection) { - case InvalidOriginRejection(allowedOrigins) ⇒ allowedOrigins shouldEqual Seq(correctOrigin) - } - } - Get("abc") ~> invalidOriginHeader ~> Route.seal(route) ~> check { - status shouldEqual StatusCodes.Forbidden - responseAs[String] should include(s"${correctOrigin.value}") - } - } - } -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/HostDirectivesSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/HostDirectivesSpec.scala deleted file mode 100644 index 3e3b7c9feb..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/HostDirectivesSpec.scala +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import akka.http.scaladsl.model.headers.Host -import org.scalatest.FreeSpec - -class HostDirectivesSpec extends FreeSpec with GenericRoutingSpec { - "The 'host' directive" - { - "in its simple String form should" - { - "block requests to unmatched hosts" in { - Get() ~> Host("spray.io") ~> { - host("spray.com") { completeOk } - } ~> check { handled shouldEqual false } - } - - "let requests to matching hosts pass" in { - Get() ~> Host("spray.io") ~> { - host("spray.com", "spray.io") { completeOk } - } ~> check { response shouldEqual Ok } - } - } - - "in its simple RegEx form" - { - "block requests to unmatched hosts" in { - Get() ~> Host("spray.io") ~> { - host("hairspray.*".r) { echoComplete } - } ~> check { handled shouldEqual false } - } - - "let requests to matching hosts pass and extract the full host" in { - Get() ~> Host("spray.io") ~> { - host("spra.*".r) { echoComplete } - } ~> check { responseAs[String] shouldEqual "spray.io" } - } - } - - "in its group RegEx form" - { - "block requests to unmatched hosts" in { - Get() ~> Host("spray.io") ~> { - host("hairspray(.*)".r) { echoComplete } - } ~> check { handled shouldEqual false } - } - - "let requests to matching hosts pass and extract the full host" in { - Get() ~> Host("spray.io") ~> { - host("spra(.*)".r) { echoComplete } - } ~> check { responseAs[String] shouldEqual "y.io" } - } - } - } -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/MarshallingDirectivesSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/MarshallingDirectivesSpec.scala deleted file mode 100644 index fc04936353..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/MarshallingDirectivesSpec.scala +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import scala.xml.NodeSeq -import org.scalatest.Inside -import akka.http.scaladsl.marshallers.xml.ScalaXmlSupport -import akka.http.scaladsl.unmarshalling._ -import akka.http.scaladsl.marshalling._ -import akka.http.scaladsl.model._ -import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._ -import spray.json.DefaultJsonProtocol._ -import MediaTypes._ -import HttpCharsets._ -import headers._ -import org.xml.sax.SAXParseException - -import scala.util.{ Failure, Try } - -class MarshallingDirectivesSpec extends RoutingSpec with Inside { - import ScalaXmlSupport._ - - private val iso88592 = HttpCharsets.getForKey("iso-8859-2").get - implicit val IntUnmarshaller: FromEntityUnmarshaller[Int] = - nodeSeqUnmarshaller(ContentTypeRange(`text/xml`, iso88592), `text/html`) map { - case NodeSeq.Empty ⇒ throw Unmarshaller.NoContentException - case x ⇒ { val i = x.text.toInt; require(i >= 0); i } - } - implicit val TryIntUnmarshaller: FromEntityUnmarshaller[Try[Int]] = - IntUnmarshaller map { - Try(_).recoverWith { - case e: IllegalArgumentException ⇒ Failure(RejectionError(ValidationRejection(e.getMessage, Option(e)))) - } - } - - val `text/xxml` = MediaType.customWithFixedCharset("text", "xxml", `UTF-8`) - implicit val IntMarshaller: ToEntityMarshaller[Int] = - Marshaller.oneOf[MediaType.NonBinary, Int, MessageEntity](`application/xhtml+xml`, `text/xxml`) { mediaType ⇒ - nodeSeqMarshaller(mediaType).wrap(mediaType) { (i: Int) ⇒ { i } } - } - - "The 'entityAs' directive" should { - "extract an object from the requests entity using the in-scope Unmarshaller" in { - Put("/",

cool

) ~> { - entity(as[NodeSeq]) { echoComplete } - } ~> check { responseAs[String] shouldEqual "

cool

" } - } - "return a RequestEntityExpectedRejection rejection if the request has no entity" in { - Put() ~> { - entity(as[Int]) { echoComplete } - } ~> check { rejection shouldEqual RequestEntityExpectedRejection } - } - "return an UnsupportedRequestContentTypeRejection if no matching unmarshaller is in scope" in { - Put("/", HttpEntity(`text/css` withCharset `UTF-8`, "

cool

")) ~> { - entity(as[NodeSeq]) { echoComplete } - } ~> check { - rejection shouldEqual UnsupportedRequestContentTypeRejection(Set(`text/xml`, `application/xml`, `text/html`, `application/xhtml+xml`)) - } - Put("/", HttpEntity(ContentType(`text/xml`, `UTF-16`), "26")) ~> { - entity(as[Int]) { echoComplete } - } ~> check { - rejection shouldEqual UnsupportedRequestContentTypeRejection(Set(ContentTypeRange(`text/xml`, iso88592), `text/html`)) - } - } - "cancel UnsupportedRequestContentTypeRejections if a subsequent `entity` directive succeeds" in { - Put("/", HttpEntity(ContentTypes.`text/plain(UTF-8)`, "yeah")) ~> { - entity(as[NodeSeq]) { _ ⇒ completeOk } ~ - entity(as[String]) { _ ⇒ validate(false, "Problem") { completeOk } } - } ~> check { rejection shouldEqual ValidationRejection("Problem") } - } - "return a ValidationRejection if the request entity is semantically invalid (IllegalArgumentException)" in { - Put("/", HttpEntity(ContentType(`text/xml`, iso88592), "-3")) ~> { - entity(as[Int]) { _ ⇒ completeOk } - } ~> check { - inside(rejection) { - case ValidationRejection("requirement failed", Some(_: IllegalArgumentException)) ⇒ - } - } - } - "unwrap a RejectionError and return its exception" in { - Put("/", HttpEntity(ContentType(`text/xml`, iso88592), "-3")) ~> { - entity(as[Try[Int]]) { _ ⇒ completeOk } - } ~> check { - inside(rejection) { - case ValidationRejection("requirement failed", Some(_: IllegalArgumentException)) ⇒ - } - } - } - "return a MalformedRequestContentRejection if unmarshalling failed due to a not further classified error" in { - Put("/", HttpEntity(ContentTypes.`text/xml(UTF-8)`, " { - entity(as[NodeSeq]) { _ ⇒ completeOk } - } ~> check { - inside(rejection) { - case MalformedRequestContentRejection( - "XML document structures must start and end within the same entity.", _: SAXParseException) ⇒ - } - } - } - "extract an Option[T] from the requests entity using the in-scope Unmarshaller" in { - Put("/",

cool

) ~> { - entity(as[Option[NodeSeq]]) { echoComplete } - } ~> check { responseAs[String] shouldEqual "Some(

cool

)" } - } - "extract an Option[T] as None if the request has no entity" in { - Put() ~> { - entity(as[Option[Int]]) { echoComplete } - } ~> check { responseAs[String] shouldEqual "None" } - } - "return an UnsupportedRequestContentTypeRejection if no matching unmarshaller is in scope (for Option[T]s)" in { - Put("/", HttpEntity(`text/css` withCharset `UTF-8`, "

cool

")) ~> { - entity(as[Option[NodeSeq]]) { echoComplete } - } ~> check { - rejection shouldEqual UnsupportedRequestContentTypeRejection(Set(`text/xml`, `application/xml`, `text/html`, `application/xhtml+xml`)) - } - } - "properly extract with a super-unmarshaller" in { - case class Person(name: String) - val jsonUnmarshaller: FromEntityUnmarshaller[Person] = jsonFormat1(Person) - val xmlUnmarshaller: FromEntityUnmarshaller[Person] = - ScalaXmlSupport.nodeSeqUnmarshaller(`text/xml`).map(seq ⇒ Person(seq.text)) - - implicit val unmarshaller = Unmarshaller.firstOf(jsonUnmarshaller, xmlUnmarshaller) - - val route = entity(as[Person]) { echoComplete } - - Put("/", HttpEntity(ContentTypes.`text/xml(UTF-8)`, "Peter Xml")) ~> route ~> check { - responseAs[String] shouldEqual "Person(Peter Xml)" - } - Put("/", HttpEntity(`application/json`, """{ "name": "Paul Json" }""")) ~> route ~> check { - responseAs[String] shouldEqual "Person(Paul Json)" - } - Put("/", HttpEntity(ContentTypes.`text/plain(UTF-8)`, """name = Sir Text }""")) ~> route ~> check { - rejection shouldEqual UnsupportedRequestContentTypeRejection(Set(`application/json`, `text/xml`)) - } - } - } - - "The 'completeWith' directive" should { - "provide a completion function converting custom objects to an HttpEntity using the in-scope marshaller" in { - Get() ~> completeWith(instanceOf[Int]) { prod ⇒ prod(42) } ~> check { - responseEntity shouldEqual HttpEntity(ContentType(`application/xhtml+xml`, `UTF-8`), "42") - } - } - "return a UnacceptedResponseContentTypeRejection rejection if no acceptable marshaller is in scope" in { - Get() ~> Accept(`text/css`) ~> completeWith(instanceOf[Int]) { prod ⇒ prod(42) } ~> check { - rejection shouldEqual UnacceptedResponseContentTypeRejection(Set(`application/xhtml+xml`, `text/xxml`)) - } - } - "convert the response content to an accepted charset" in { - Get() ~> `Accept-Charset`(`UTF-8`) ~> completeWith(instanceOf[String]) { prod ⇒ prod("Hällö") } ~> check { - responseEntity shouldEqual HttpEntity(ContentType(`text/plain`, `UTF-8`), "Hällö") - } - } - } - - "The 'handleWith' directive" should { - def times2(x: Int) = x * 2 - - "support proper round-trip content unmarshalling/marshalling to and from a function" in ( - Put("/", HttpEntity(ContentTypes.`text/html(UTF-8)`, "42")) ~> Accept(`text/xxml`) ~> handleWith(times2) - ~> check { responseEntity shouldEqual HttpEntity(`text/xxml`, "84") }) - - "result in UnsupportedRequestContentTypeRejection rejection if there is no unmarshaller supporting the requests charset" in ( - Put("/", HttpEntity(ContentTypes.`text/xml(UTF-8)`, "42")) ~> Accept(`text/xml`) ~> handleWith(times2) - ~> check { - rejection shouldEqual UnsupportedRequestContentTypeRejection(Set(ContentTypeRange(`text/xml`, iso88592), `text/html`)) - }) - - "result in an UnacceptedResponseContentTypeRejection rejection if there is no marshaller supporting the requests Accept-Charset header" in ( - Put("/", HttpEntity(ContentTypes.`text/html(UTF-8)`, "42")) ~> addHeaders(Accept(`text/xxml`), `Accept-Charset`(`UTF-16`)) ~> - handleWith(times2) ~> check { - rejection shouldEqual UnacceptedResponseContentTypeRejection(Set(`application/xhtml+xml`, `text/xxml`)) - }) - } - - "The marshalling infrastructure for JSON" should { - import spray.json._ - case class Foo(name: String) - implicit val fooFormat = jsonFormat1(Foo) - val foo = Foo("Hällö") - - "render JSON with UTF-8 encoding if no `Accept-Charset` request header is present" in { - Get() ~> complete(foo) ~> check { - responseEntity shouldEqual HttpEntity(`application/json`, foo.toJson.compactPrint) - } - } - "reject JSON rendering if an `Accept-Charset` request header requests a non-UTF-8 encoding" in { - Get() ~> `Accept-Charset`(`ISO-8859-1`) ~> complete(foo) ~> check { - rejection shouldEqual UnacceptedResponseContentTypeRejection(Set(ContentType(`application/json`))) - } - } - } -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/MethodDirectivesSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/MethodDirectivesSpec.scala deleted file mode 100644 index 5fdb50bf99..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/MethodDirectivesSpec.scala +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server.directives - -import akka.http.scaladsl.model.{ ContentTypes, HttpEntity, StatusCodes, HttpMethods } -import akka.http.scaladsl.server._ -import akka.stream.scaladsl.Source - -import scala.concurrent.Await -import scala.concurrent.duration.Duration - -class MethodDirectivesSpec extends RoutingSpec { - - "get | put" should { - lazy val getOrPut = (get | put) { completeOk } - - "block POST requests" in { - Post() ~> getOrPut ~> check { handled shouldEqual false } - } - "let GET requests pass" in { - Get() ~> getOrPut ~> check { response shouldEqual Ok } - } - "let PUT requests pass" in { - Put() ~> getOrPut ~> check { response shouldEqual Ok } - } - } - - "head" should { - val headRoute = head { - complete(HttpEntity.Default( - ContentTypes.`application/octet-stream`, - 12345L, - Source.empty - )) - } - - "allow manual complete" in { - Head() ~> headRoute ~> check { - status shouldEqual StatusCodes.OK - - val lengthF = response._3.dataBytes.runFold(0)((c, _) ⇒ c + 1) - val length = Await.result(lengthF, Duration(100, "millis")) - length shouldEqual 0 - } - } - } - - "two failed `get` directives" should { - "only result in a single Rejection" in { - Put() ~> { - get { completeOk } ~ - get { completeOk } - } ~> check { - rejections shouldEqual List(MethodRejection(HttpMethods.GET)) - } - } - } - - "overrideMethodWithParameter" should { - "change the request method" in { - Get("/?_method=put") ~> overrideMethodWithParameter("_method") { - get { complete("GET") } ~ - put { complete("PUT") } - } ~> check { responseAs[String] shouldEqual "PUT" } - } - "not affect the request when not specified" in { - Get() ~> overrideMethodWithParameter("_method") { - get { complete("GET") } ~ - put { complete("PUT") } - } ~> check { responseAs[String] shouldEqual "GET" } - } - "complete with 501 Not Implemented when not a valid method" in { - Get("/?_method=hallo") ~> overrideMethodWithParameter("_method") { - get { complete("GET") } ~ - put { complete("PUT") } - } ~> check { status shouldEqual StatusCodes.NotImplemented } - } - } - - "MethodRejections under a successful match" should { - "be cancelled if the match happens after the rejection" in { - Put() ~> { - get { completeOk } ~ - put { reject(RequestEntityExpectedRejection) } - } ~> check { - rejections shouldEqual List(RequestEntityExpectedRejection) - } - } - "be cancelled if the match happens after the rejection (example 2)" in { - Put() ~> { - (get & complete(Ok)) ~ (put & reject(RequestEntityExpectedRejection)) - } ~> check { - rejections shouldEqual List(RequestEntityExpectedRejection) - } - } - "be cancelled if the match happens before the rejection" in { - Put() ~> { - put { reject(RequestEntityExpectedRejection) } ~ get { completeOk } - } ~> check { - rejections shouldEqual List(RequestEntityExpectedRejection) - } - } - "be cancelled if the match happens before the rejection (example 2)" in { - Put() ~> { - (put & reject(RequestEntityExpectedRejection)) ~ (get & complete(Ok)) - } ~> check { - rejections shouldEqual List(RequestEntityExpectedRejection) - } - } - } -} \ No newline at end of file diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/MiscDirectivesSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/MiscDirectivesSpec.scala deleted file mode 100644 index 9ea665975f..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/MiscDirectivesSpec.scala +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import scala.concurrent.{ Await, Promise } -import scala.concurrent.duration._ -import scala.util.Try -import akka.http.scaladsl.model._ -import headers._ -import java.net.InetAddress - -class MiscDirectivesSpec extends RoutingSpec { - - "the extractClientIP directive" should { - "extract from a X-Forwarded-For header" in { - Get() ~> addHeaders(`X-Forwarded-For`(remoteAddress("2.3.4.5")), RawHeader("x-real-ip", "1.2.3.4")) ~> { - extractClientIP { echoComplete } - } ~> check { responseAs[String] shouldEqual "2.3.4.5" } - } - "extract from a Remote-Address header" in { - Get() ~> addHeaders(`X-Real-Ip`(remoteAddress("1.2.3.4")), `Remote-Address`(remoteAddress("5.6.7.8"))) ~> { - extractClientIP { echoComplete } - } ~> check { responseAs[String] shouldEqual "5.6.7.8" } - } - "extract from a X-Real-IP header" in { - Get() ~> addHeader(`X-Real-Ip`(remoteAddress("1.2.3.4"))) ~> { - extractClientIP { echoComplete } - } ~> check { responseAs[String] shouldEqual "1.2.3.4" } - } - "extract unknown when no headers" in { - Get() ~> { - extractClientIP { echoComplete } - } ~> check { responseAs[String] shouldEqual "unknown" } - } - } - - "the selectPreferredLanguage directive" should { - "Accept-Language: de, en" test { selectFrom ⇒ - selectFrom("de", "en") shouldEqual "de" - selectFrom("en", "de") shouldEqual "en" - } - "Accept-Language: en, de;q=.5" test { selectFrom ⇒ - selectFrom("de", "en") shouldEqual "en" - selectFrom("en", "de") shouldEqual "en" - } - "Accept-Language: en;q=.5, de" test { selectFrom ⇒ - selectFrom("de", "en") shouldEqual "de" - selectFrom("en", "de") shouldEqual "de" - } - "Accept-Language: en-US, en;q=.7, *;q=.1, de;q=.5" test { selectFrom ⇒ - selectFrom("en", "en-US") shouldEqual "en-US" - selectFrom("de", "en") shouldEqual "en" - selectFrom("de", "hu") shouldEqual "de" - selectFrom("de-DE", "hu") shouldEqual "de-DE" - selectFrom("hu", "es") shouldEqual "hu" - selectFrom("es", "hu") shouldEqual "es" - } - "Accept-Language: en, *;q=.5, de;q=0" test { selectFrom ⇒ - selectFrom("es", "de") shouldEqual "es" - selectFrom("de", "es") shouldEqual "es" - selectFrom("es", "en") shouldEqual "en" - } - "Accept-Language: en, *;q=0" test { selectFrom ⇒ - selectFrom("es", "de") shouldEqual "es" - selectFrom("de", "es") shouldEqual "de" - selectFrom("es", "en") shouldEqual "en" - } - } - - "the withSizeLimit directive" should { - "not apply if entity is not consumed" in { - val route = withSizeLimit(500) { completeOk } - - Post("/abc", entityOfSize(500)) ~> route ~> check { - status shouldEqual StatusCodes.OK - } - - Post("/abc", entityOfSize(501)) ~> route ~> check { - status shouldEqual StatusCodes.OK - } - } - - "apply if entity is consumed" in { - val route = withSizeLimit(500) { - entity(as[String]) { _ ⇒ - completeOk - } - } - - Post("/abc", entityOfSize(500)) ~> route ~> check { - status shouldEqual StatusCodes.OK - } - - Post("/abc", entityOfSize(501)) ~> Route.seal(route) ~> check { - status shouldEqual StatusCodes.BadRequest - } - } - - "properly handle nested directives by applying innermost `withSizeLimit` directive" in { - val route = - withSizeLimit(500) { - withSizeLimit(800) { - entity(as[String]) { _ ⇒ - completeOk - } - } - } - - Post("/abc", entityOfSize(800)) ~> route ~> check { - status shouldEqual StatusCodes.OK - } - - Post("/abc", entityOfSize(801)) ~> Route.seal(route) ~> check { - status shouldEqual StatusCodes.BadRequest - } - - val route2 = - withSizeLimit(500) { - withSizeLimit(400) { - entity(as[String]) { _ ⇒ - completeOk - } - } - } - - Post("/abc", entityOfSize(400)) ~> route2 ~> check { - status shouldEqual StatusCodes.OK - } - - Post("/abc", entityOfSize(401)) ~> Route.seal(route2) ~> check { - status shouldEqual StatusCodes.BadRequest - } - } - } - - "the withoutSizeLimit directive" should { - "skip request entity size verification" in { - val route = - withSizeLimit(500) { - withoutSizeLimit { - entity(as[String]) { _ ⇒ - completeOk - } - } - } - - Post("/abc", entityOfSize(501)) ~> route ~> check { - status shouldEqual StatusCodes.OK - } - } - } - - implicit class AddStringToIn(acceptLanguageHeaderString: String) { - def test(body: ((String*) ⇒ String) ⇒ Unit): Unit = - s"properly handle `$acceptLanguageHeaderString`" in { - val Array(name, value) = acceptLanguageHeaderString.split(':') - val acceptLanguageHeader = HttpHeader.parse(name.trim, value) match { - case HttpHeader.ParsingResult.Ok(h: `Accept-Language`, Nil) ⇒ h - case result ⇒ fail(result.toString) - } - body { availableLangs ⇒ - val selected = Promise[String]() - val first = Language(availableLangs.head) - val more = availableLangs.tail.map(Language(_)) - Get() ~> addHeader(acceptLanguageHeader) ~> { - selectPreferredLanguage(first, more: _*) { lang ⇒ - complete(lang.toString) - } - } ~> check(selected.complete(Try(responseAs[String]))) - Await.result(selected.future, 1.second) - } - } - } - - def remoteAddress(ip: String) = RemoteAddress(InetAddress.getByName(ip)) - - private def entityOfSize(size: Int) = HttpEntity(ContentTypes.`text/plain(UTF-8)`, "0" * size) -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/ParameterDirectivesSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/ParameterDirectivesSpec.scala deleted file mode 100755 index e9ae1849dd..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/ParameterDirectivesSpec.scala +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import org.scalatest.{ FreeSpec, Inside } -import akka.http.scaladsl.unmarshalling.Unmarshaller, Unmarshaller._ - -class ParameterDirectivesSpec extends FreeSpec with GenericRoutingSpec with Inside { - "when used with 'as[Int]' the parameter directive should" - { - "extract a parameter value as Int" in { - Get("/?amount=123") ~> { - parameter('amount.as[Int]) { echoComplete } - } ~> check { responseAs[String] shouldEqual "123" } - } - "cause a MalformedQueryParamRejection on illegal Int values" in { - Get("/?amount=1x3") ~> { - parameter('amount.as[Int]) { echoComplete } - } ~> check { - inside(rejection) { - case MalformedQueryParamRejection("amount", "'1x3' is not a valid 32-bit signed integer value", Some(_)) ⇒ - } - } - } - "supply typed default values" in { - Get() ~> { - parameter('amount ? 45) { echoComplete } - } ~> check { responseAs[String] shouldEqual "45" } - } - "create typed optional parameters that" - { - "extract Some(value) when present" in { - Get("/?amount=12") ~> { - parameter("amount".as[Int].?) { echoComplete } - } ~> check { responseAs[String] shouldEqual "Some(12)" } - } - "extract None when not present" in { - Get() ~> { - parameter("amount".as[Int].?) { echoComplete } - } ~> check { responseAs[String] shouldEqual "None" } - } - "cause a MalformedQueryParamRejection on illegal Int values" in { - Get("/?amount=x") ~> { - parameter("amount".as[Int].?) { echoComplete } - } ~> check { - inside(rejection) { - case MalformedQueryParamRejection("amount", "'x' is not a valid 32-bit signed integer value", Some(_)) ⇒ - } - } - } - } - "supply chaining of unmarshallers" in { - case class UserId(id: Int) - case class AnotherUserId(id: Int) - val UserIdUnmarshaller = Unmarshaller.strict[Int, UserId](UserId) - implicit val AnotherUserIdUnmarshaller = Unmarshaller.strict[UserId, AnotherUserId](userId ⇒ AnotherUserId(userId.id)) - Get("/?id=45") ~> { - parameter('id.as[Int].as(UserIdUnmarshaller)) { echoComplete } - } ~> check { responseAs[String] shouldEqual "UserId(45)" } - Get("/?id=45") ~> { - parameter('id.as[Int].as(UserIdUnmarshaller).as[AnotherUserId]) { echoComplete } - } ~> check { responseAs[String] shouldEqual "AnotherUserId(45)" } - } - } - - "when used with 'as(CsvSeq[...])' the parameter directive should" - { - val route = - parameter("names".as(CsvSeq[String])) { names ⇒ - complete(s"The parameters are ${names.mkString(", ")}") - } - - "extract a single name" in { - Get("/?names=Caplin") ~> route ~> check { - responseAs[String] shouldEqual "The parameters are Caplin" - } - } - "extract a number of names" in { - Get("/?names=Caplin,John") ~> route ~> check { - responseAs[String] shouldEqual "The parameters are Caplin, John" - } - } - } - - "when used with 'as(HexInt)' the parameter directive should" - { - "extract parameter values as Int" in { - Get("/?amount=1f") ~> { - parameter('amount.as(HexInt)) { echoComplete } - } ~> check { responseAs[String] shouldEqual "31" } - } - "cause a MalformedQueryParamRejection on illegal Int values" in { - Get("/?amount=1x3") ~> { - parameter('amount.as(HexInt)) { echoComplete } - } ~> check { - inside(rejection) { - case MalformedQueryParamRejection("amount", "'1x3' is not a valid 32-bit hexadecimal integer value", Some(_)) ⇒ - } - } - } - "supply typed default values" in { - Get() ~> { - parameter('amount.as(HexInt) ? 45) { echoComplete } - } ~> check { responseAs[String] shouldEqual "45" } - } - "create typed optional parameters that" - { - "extract Some(value) when present" in { - Get("/?amount=A") ~> { - parameter("amount".as(HexInt).?) { echoComplete } - } ~> check { responseAs[String] shouldEqual "Some(10)" } - } - "extract None when not present" in { - Get() ~> { - parameter("amount".as(HexInt).?) { echoComplete } - } ~> check { responseAs[String] shouldEqual "None" } - } - "cause a MalformedQueryParamRejection on illegal Int values" in { - Get("/?amount=x") ~> { - parameter("amount".as(HexInt).?) { echoComplete } - } ~> check { - inside(rejection) { - case MalformedQueryParamRejection("amount", "'x' is not a valid 32-bit hexadecimal integer value", Some(_)) ⇒ - } - } - } - } - } - - "when used with 'as[Boolean]' the parameter directive should" - { - "extract parameter values as Boolean" in { - Get("/?really=true") ~> { - parameter('really.as[Boolean]) { echoComplete } - } ~> check { responseAs[String] shouldEqual "true" } - Get("/?really=no") ~> { - parameter('really.as[Boolean]) { echoComplete } - } ~> check { responseAs[String] shouldEqual "false" } - } - "extract numeric parameter value as Boolean" in { - Get("/?really=1") ~> { - parameter('really.as[Boolean]) { echoComplete } - } ~> check { responseAs[String] shouldEqual "true" } - Get("/?really=0") ~> { - parameter('really.as[Boolean]) { echoComplete } - } ~> check { responseAs[String] shouldEqual "false" } - } - "extract optional parameter values as Boolean" in { - Get() ~> { - parameter('really.as[Boolean] ? false) { echoComplete } - } ~> check { responseAs[String] shouldEqual "false" } - } - "cause a MalformedQueryParamRejection on illegal Boolean values" in { - Get("/?really=absolutely") ~> { - parameter('really.as[Boolean]) { echoComplete } - } ~> check { - inside(rejection) { - case MalformedQueryParamRejection("really", "'absolutely' is not a valid Boolean value", None) ⇒ - } - } - } - } - - "The 'parameters' extraction directive should" - { - "extract the value of given parameters" in { - Get("/?name=Parsons&FirstName=Ellen") ~> { - parameters("name", 'FirstName) { (name, firstName) ⇒ - complete(firstName + name) - } - } ~> check { responseAs[String] shouldEqual "EllenParsons" } - } - "correctly extract an optional parameter" in { - Get("/?foo=bar") ~> parameters('foo.?) { echoComplete } ~> check { responseAs[String] shouldEqual "Some(bar)" } - Get("/?foo=bar") ~> parameters('baz.?) { echoComplete } ~> check { responseAs[String] shouldEqual "None" } - } - "ignore additional parameters" in { - Get("/?name=Parsons&FirstName=Ellen&age=29") ~> { - parameters("name", 'FirstName) { (name, firstName) ⇒ - complete(firstName + name) - } - } ~> check { responseAs[String] shouldEqual "EllenParsons" } - } - "reject the request with a MissingQueryParamRejection if a required parameter is missing" in { - Get("/?name=Parsons&sex=female") ~> { - parameters('name, 'FirstName, 'age) { (name, firstName, age) ⇒ - completeOk - } - } ~> check { rejection shouldEqual MissingQueryParamRejection("FirstName") } - } - "supply the default value if an optional parameter is missing" in { - Get("/?name=Parsons&FirstName=Ellen") ~> { - parameters("name".?, 'FirstName, 'age ? "29", 'eyes.?) { (name, firstName, age, eyes) ⇒ - complete(firstName + name + age + eyes) - } - } ~> check { responseAs[String] shouldEqual "EllenSome(Parsons)29None" } - } - } - - "The 'parameter' requirement directive should" - { - "block requests that do not contain the required parameter" in { - Get("/person?age=19") ~> { - parameter('nose ! "large") { completeOk } - } ~> check { handled shouldEqual false } - } - "block requests that contain the required parameter but with an unmatching value" in { - Get("/person?age=19&nose=small") ~> { - parameter('nose ! "large") { completeOk } - } ~> check { handled shouldEqual false } - } - "let requests pass that contain the required parameter with its required value" in { - Get("/person?nose=large&eyes=blue") ~> { - parameter('nose ! "large") { completeOk } - } ~> check { response shouldEqual Ok } - } - "be useable for method tunneling" in { - val route = { - (post | parameter('method ! "post")) { complete("POST") } ~ - get { complete("GET") } - } - Get("/?method=post") ~> route ~> check { responseAs[String] shouldEqual "POST" } - Post() ~> route ~> check { responseAs[String] shouldEqual "POST" } - Get() ~> route ~> check { responseAs[String] shouldEqual "GET" } - } - } - - "The 'parameter' repeated directive should" - { - "extract an empty Iterable when the parameter is absent" in { - Get("/person?age=19") ~> { - parameter('hobby.*) { echoComplete } - } ~> check { responseAs[String] === "List()" } - } - "extract all occurrences into an Iterable when parameter is present" in { - Get("/person?age=19&hobby=cooking&hobby=reading") ~> { - parameter('hobby.*) { echoComplete } - } ~> check { responseAs[String] === "List(cooking, reading)" } - } - "extract as Iterable[Int]" in { - Get("/person?age=19&number=3&number=5") ~> { - parameter('number.as[Int].*) { echoComplete } - } ~> check { responseAs[String] === "List(3, 5)" } - } - "extract as Iterable[Int] with an explicit deserializer" in { - Get("/person?age=19&number=3&number=A") ~> { - parameter('number.as(HexInt).*) { echoComplete } - } ~> check { responseAs[String] === "List(3, 10)" } - } - } - - "The 'parameterSeq' directive should" - { - val completeAsList = - parameterSeq { params ⇒ - val sorted = params.sorted - complete(s"${sorted.size}: [${sorted.map(e ⇒ e._1 + " -> " + e._2).mkString(", ")}]") - } - - "extract parameters with different keys" in { - Get("/?a=b&e=f&c=d") ~> completeAsList ~> check { - responseAs[String] shouldEqual "3: [a -> b, c -> d, e -> f]" - } - } - "extract parameters with duplicate keys" in { - Get("/?a=b&e=f&c=d&a=z") ~> completeAsList ~> check { - responseAs[String] shouldEqual "4: [a -> b, a -> z, c -> d, e -> f]" - } - } - } -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/PathDirectivesSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/PathDirectivesSpec.scala deleted file mode 100644 index 4f173e18e7..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/PathDirectivesSpec.scala +++ /dev/null @@ -1,407 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server.directives - -import akka.http.scaladsl.model.StatusCodes -import akka.http.scaladsl.server._ -import org.scalactic.source.Position -import org.scalatest.Inside - -class PathDirectivesSpec extends RoutingSpec with Inside { - val echoUnmatchedPath = extractUnmatchedPath { echoComplete } - def echoCaptureAndUnmatchedPath[T]: T ⇒ Route = - capture ⇒ ctx ⇒ ctx.complete(capture.toString + ":" + ctx.unmatchedPath) - - """path("foo")""" should { - val test = testFor(path("foo") { echoUnmatchedPath }) - "reject [/bar]" inThe test() - "reject [/foobar]" inThe test() - "reject [/foo/bar]" inThe test() - "accept [/foo] and clear the unmatchedPath" inThe test("") - "reject [/foo/]" inThe test() - } - - """pathPrefix("")""" should { - val test = testFor(pathPrefix("") { echoUnmatchedPath }) - - // Should match everything because pathPrefix is used and "" is a neutral element. - "accept [/] and clear the unmatchedPath=" inThe test("") - "accept [/foo] and clear the unmatchedPath" inThe test("foo") - "accept [/foo/] and clear the unmatchedPath" inThe test("foo/") - "accept [/bar/] and clear the unmatchedPath" inThe test("bar/") - } - - """path("" | "foo")""" should { - val test = testFor(path("" | "foo") { echoUnmatchedPath }) - - // Should not match anything apart of "/", because path requires whole path being matched. - "accept [/] and clear the unmatchedPath=" inThe test("") - "reject [/foo]" inThe test() - "reject [/foo/]" inThe test() - "reject [/bar/]" inThe test() - } - - """path("") ~ path("foo")""" should { - val test = testFor(path("")(echoUnmatchedPath) ~ path("foo")(echoUnmatchedPath)) - - // Should match both because ~ operator is used for two exclusive routes. - "accept [/] and clear the unmatchedPath=" inThe test("") - "accept [/foo] and clear the unmatchedPath=" inThe test("") - } - - """path("foo" /)""" should { - val test = testFor(path("foo" /) { echoUnmatchedPath }) - "reject [/foo]" inThe test() - "accept [/foo/] and clear the unmatchedPath" inThe test("") - } - - """path("")""" should { - val test = testFor(path("") { echoUnmatchedPath }) - "reject [/foo]" inThe test() - "accept [/] and clear the unmatchedPath" in test("") - } - - """pathPrefix("foo")""" should { - val test = testFor(pathPrefix("foo") { echoUnmatchedPath }) - "reject [/bar]" inThe test() - "accept [/foobar]" inThe test("bar") - "accept [/foo/bar]" inThe test("/bar") - "accept [/foo] and clear the unmatchedPath" inThe test("") - "accept [/foo/] and clear the unmatchedPath" inThe test("/") - } - - """pathPrefix("foo" / "bar")""" should { - val test = testFor(pathPrefix("foo" / "bar") { echoUnmatchedPath }) - "reject [/bar]" inThe test() - "accept [/foo/bar]" inThe test("") - "accept [/foo/bar/baz]" inThe test("/baz") - } - - """pathPrefix("ab[cd]+".r)""" should { - val test = testFor(pathPrefix("ab[cd]+".r) { echoCaptureAndUnmatchedPath }) - "reject [/bar]" inThe test() - "reject [/ab/cd]" inThe test() - "accept [/abcdef]" inThe test("abcd:ef") - "accept [/abcdd/ef]" inThe test("abcdd:/ef") - } - - """pathPrefix("ab(cd)".r)""" should { - val test = testFor(pathPrefix("ab(cd)+".r) { echoCaptureAndUnmatchedPath }) - "reject [/bar]" inThe test() - "reject [/ab/cd]" inThe test() - "accept [/abcdef]" inThe test("cd:ef") - "accept [/abcde/fg]" inThe test("cd:e/fg") - } - - "pathPrefix(regex)" should { - "fail when the regex contains more than one group" in { - an[IllegalArgumentException] must be thrownBy path("a(b+)(c+)".r) { echoCaptureAndUnmatchedPath } - } - } - - "pathPrefix(IntNumber)" should { - val test = testFor(pathPrefix(IntNumber) { echoCaptureAndUnmatchedPath }) - "accept [/23]" inThe test("23:") - "accept [/12345yes]" inThe test("12345:yes") - "reject [/]" inThe test() - "reject [/abc]" inThe test() - "reject [/2147483648]" inThe test() // > Int.MaxValue - } - - "pathPrefix(CustomShortNumber)" should { - object CustomShortNumber extends NumberMatcher[Short](Short.MaxValue, 10) { - def fromChar(c: Char) = fromDecimalChar(c) - } - - val test = testFor(pathPrefix(CustomShortNumber) { echoCaptureAndUnmatchedPath }) - "accept [/23]" inThe test("23:") - "accept [/12345yes]" inThe test("12345:yes") - "reject [/]" inThe test() - "reject [/abc]" inThe test() - "reject [/33000]" inThe test() // > Short.MaxValue - } - - "pathPrefix(JavaUUID)" should { - val test = testFor(pathPrefix(JavaUUID) { echoCaptureAndUnmatchedPath }) - "accept [/bdea8652-f26c-40ca-8157-0b96a2a8389d]" inThe test("bdea8652-f26c-40ca-8157-0b96a2a8389d:") - "accept [/bdea8652-f26c-40ca-8157-0b96a2a8389dyes]" inThe test("bdea8652-f26c-40ca-8157-0b96a2a8389d:yes") - "reject [/]" inThe test() - "reject [/abc]" inThe test() - } - - "pathPrefix(Map(\"red\" -> 1, \"green\" -> 2, \"blue\" -> 3))" should { - val test = testFor(pathPrefix(Map("red" → 1, "green" → 2, "blue" → 3)) { echoCaptureAndUnmatchedPath }) - "accept [/green]" inThe test("2:") - "accept [/redsea]" inThe test("1:sea") - "reject [/black]" inThe test() - } - - "pathPrefix(Map.empty)" should { - val test = testFor(pathPrefix(Map[String, Int]()) { echoCaptureAndUnmatchedPath }) - "reject [/black]" inThe test() - } - - "pathPrefix(Segment)" should { - val test = testFor(pathPrefix(Segment) { echoCaptureAndUnmatchedPath }) - "accept [/abc]" inThe test("abc:") - "accept [/abc/]" inThe test("abc:/") - "accept [/abc/def]" inThe test("abc:/def") - "reject [/]" inThe test() - } - - "pathPrefix(Segments)" should { - val test = testFor(pathPrefix(Segments) { echoCaptureAndUnmatchedPath }) - "accept [/]" inThe test("List():") - "accept [/a/b/c]" inThe test("List(a, b, c):") - "accept [/a/b/c/]" inThe test("List(a, b, c):/") - } - - """pathPrefix(separateOnSlashes("a/b"))""" should { - val test = testFor(pathPrefix(separateOnSlashes("a/b")) { echoUnmatchedPath }) - "accept [/a/b]" inThe test("") - "accept [/a/b/]" inThe test("/") - "reject [/a/c]" inThe test() - } - """pathPrefix(separateOnSlashes("abc"))""" should { - val test = testFor(pathPrefix(separateOnSlashes("abc")) { echoUnmatchedPath }) - "accept [/abc]" inThe test("") - "accept [/abcdef]" inThe test("def") - "reject [/ab]" inThe test() - } - - """pathPrefixTest("a" / Segment ~ Slash)""" should { - val test = testFor(pathPrefixTest("a" / Segment ~ Slash) { echoCaptureAndUnmatchedPath }) - "accept [/a/bc/]" inThe test("bc:/a/bc/") - "reject [/a/bc]" inThe test() - "reject [/a/]" inThe test() - } - - """pathSuffix("edit" / Segment)""" should { - val test = testFor(pathSuffix("edit" / Segment) { echoCaptureAndUnmatchedPath }) - "accept [/orders/123/edit]" inThe test("123:/orders/") - "reject [/orders/123/ed]" inThe test() - "reject [/edit]" inThe test() - } - - """pathSuffix("foo" / "bar" ~ "baz")""" should { - val test = testFor(pathSuffix("foo" / "bar" ~ "baz") { echoUnmatchedPath }) - "accept [/orders/barbaz/foo]" inThe test("/orders/") - "reject [/orders/bazbar/foo]" inThe test() - } - - "pathSuffixTest(Slash)" should { - val test = testFor(pathSuffixTest(Slash) { echoUnmatchedPath }) - "accept [/]" inThe test("/") - "accept [/foo/]" inThe test("/foo/") - "reject [/foo]" inThe test() - } - - """pathPrefix("foo" | "bar")""" should { - val test = testFor(pathPrefix("foo" | "bar") { echoUnmatchedPath }) - "accept [/foo]" inThe test("") - "accept [/foops]" inThe test("ps") - "accept [/bar]" inThe test("") - "reject [/baz]" inThe test() - } - - """pathSuffix(!"foo")""" should { - val test = testFor(pathSuffix(!"foo") { echoUnmatchedPath }) - "accept [/bar]" inThe test("/bar") - "reject [/foo]" inThe test() - } - - "pathPrefix(IntNumber?)" should { - val test = testFor(pathPrefix(IntNumber?) { echoCaptureAndUnmatchedPath }) - "accept [/12]" inThe test("Some(12):") - "accept [/12a]" inThe test("Some(12):a") - "accept [/foo]" inThe test("None:foo") - } - - """pathPrefix("foo"?)""" should { - val test = testFor(pathPrefix("foo"?) { echoUnmatchedPath }) - "accept [/foo]" inThe test("") - "accept [/fool]" inThe test("l") - "accept [/bar]" inThe test("bar") - } - - """pathPrefix("foo") & pathEnd""" should { - val test = testFor((pathPrefix("foo") & pathEnd) { echoUnmatchedPath }) - "reject [/foobar]" inThe test() - "reject [/foo/bar]" inThe test() - "accept [/foo] and clear the unmatchedPath" inThe test("") - "reject [/foo/]" inThe test() - } - - """pathPrefix("foo") & pathEndOrSingleSlash""" should { - val test = testFor((pathPrefix("foo") & pathEndOrSingleSlash) { echoUnmatchedPath }) - "reject [/foobar]" inThe test() - "reject [/foo/bar]" inThe test() - "accept [/foo] and clear the unmatchedPath" inThe test("") - "accept [/foo/] and clear the unmatchedPath" inThe test("") - } - - """pathPrefix(IntNumber.repeat(separator = "."))""" should { - { - val test = testFor(pathPrefix(IntNumber.repeat(min = 2, max = 5, separator = ".")) { echoCaptureAndUnmatchedPath }) - "reject [/foo]" inThe test() - "reject [/1foo]" inThe test() - "reject [/1.foo]" inThe test() - "accept [/1.2foo]" inThe test("List(1, 2):foo") - "accept [/1.2.foo]" inThe test("List(1, 2):.foo") - "accept [/1.2.3foo]" inThe test("List(1, 2, 3):foo") - "accept [/1.2.3.foo]" inThe test("List(1, 2, 3):.foo") - "accept [/1.2.3.4foo]" inThe test("List(1, 2, 3, 4):foo") - "accept [/1.2.3.4.foo]" inThe test("List(1, 2, 3, 4):.foo") - "accept [/1.2.3.4.5foo]" inThe test("List(1, 2, 3, 4, 5):foo") - "accept [/1.2.3.4.5.foo]" inThe test("List(1, 2, 3, 4, 5):.foo") - "accept [/1.2.3.4.5.6foo]" inThe test("List(1, 2, 3, 4, 5):.6foo") - "accept [/1.2.3.]" inThe test("List(1, 2, 3):.") - "accept [/1.2.3/]" inThe test("List(1, 2, 3):/") - "accept [/1.2.3./]" inThe test("List(1, 2, 3):./") - } - { - val test = testFor(pathPrefix(IntNumber.repeat(2, ".")) { echoCaptureAndUnmatchedPath }) - "reject [/bar]" inThe test() - "reject [/1bar]" inThe test() - "reject [/1.bar]" inThe test() - "accept [/1.2bar]" inThe test("List(1, 2):bar") - "accept [/1.2.bar]" inThe test("List(1, 2):.bar") - "accept [/1.2.3bar]" inThe test("List(1, 2):.3bar") - } - } - - """rawPathPrefix(Slash ~ "a" / Segment ~ Slash)""" should { - val test = testFor(rawPathPrefix(Slash ~ "a" / Segment ~ Slash) { echoCaptureAndUnmatchedPath }) - "accept [/a/bc/]" inThe test("bc:") - "reject [/a/bc]" inThe test() - "reject [/ab/]" inThe test() - } - - """rawPathPrefixTest(Slash ~ "a" / Segment ~ Slash)""" should { - val test = testFor(rawPathPrefixTest(Slash ~ "a" / Segment ~ Slash) { echoCaptureAndUnmatchedPath }) - "accept [/a/bc/]" inThe test("bc:/a/bc/") - "reject [/a/bc]" inThe test() - "reject [/ab/]" inThe test() - } - - "PathMatchers" should { - { - val test = testFor(path(Remaining.tmap { case Tuple1(s) ⇒ Tuple1(s.split('-').toList) }) { echoComplete }) - "support the hmap modifier in accept [/yes-no]" inThe test("List(yes, no)") - } - { - val test = testFor(path(Remaining.map(_.split('-').toList)) { echoComplete }) - "support the map modifier in accept [/yes-no]" inThe test("List(yes, no)") - } - { - val test = testFor(path(Remaining.tflatMap { case Tuple1(s) ⇒ Some(s).filter("yes" ==).map(x ⇒ Tuple1(x)) }) { echoComplete }) - "support the hflatMap modifier in accept [/yes]" inThe test("yes") - "support the hflatMap modifier in reject [/blub]" inThe test() - } - { - val test = testFor(path(Remaining.flatMap(s ⇒ Some(s).filter("yes" ==))) { echoComplete }) - "support the flatMap modifier in accept [/yes]" inThe test("yes") - "support the flatMap modifier reject [/blub]" inThe test() - } - } - - implicit class WithIn(str: String) { - def inThe(f: String ⇒ Unit) = convertToWordSpecStringWrapper(str) in f(str) - def inThe(body: ⇒ Unit) = convertToWordSpecStringWrapper(str) in body - } - - case class testFor(route: Route) { - def apply(expectedResponse: String = null): String ⇒ Unit = exampleString ⇒ - """(accept|reject)\s+\[([^\]]+)\]""".r.findFirstMatchIn(exampleString) match { - case Some(uri) ⇒ - uri.group(1) match { - case "accept" if expectedResponse eq null ⇒ - failTest("Example '" + exampleString + "' was missing an expectedResponse") - case "reject" if expectedResponse ne null ⇒ - failTest("Example '" + exampleString + "' had an expectedResponse") - case _ ⇒ - } - - Get(uri.group(2)) ~> route ~> check { - if (expectedResponse eq null) handled shouldEqual false - else responseAs[String] shouldEqual expectedResponse - } - case None ⇒ failTest("Example '" + exampleString + "' doesn't contain a test uri") - } - } - - import akka.http.scaladsl.model.StatusCodes._ - - "the Remaining path matcher" should { - "extract complete path if nothing previously consumed" in { - val route = path(Remaining) { echoComplete } - Get("/pets/afdaoisd/asda/sfasfasf/asf") ~> route ~> check { responseAs[String] shouldEqual "pets/afdaoisd/asda/sfasfasf/asf" } - } - "extract remaining path when parts of path already matched" in { - val route = path("pets" / Remaining) { echoComplete } - Get("/pets/afdaoisd/asda/sfasfasf/asf") ~> route ~> check { responseAs[String] shouldEqual "afdaoisd/asda/sfasfasf/asf" } - } - } - - "the `redirectToTrailingSlashIfMissing` directive" should { - val route = redirectToTrailingSlashIfMissing(Found) { completeOk } - - "pass if the request path already has a trailing slash" in { - Get("/foo/bar/") ~> route ~> check { response shouldEqual Ok } - } - - "redirect if the request path doesn't have a trailing slash" in { - Get("/foo/bar") ~> route ~> checkRedirectTo("/foo/bar/") - } - - "preserves the query and the frag when redirect" in { - Get("/foo/bar?query#frag") ~> route ~> checkRedirectTo("/foo/bar/?query#frag") - } - - "redirect with the given redirection status code" in { - Get("/foo/bar") ~> - redirectToTrailingSlashIfMissing(MovedPermanently) { completeOk } ~> - check { status shouldEqual MovedPermanently } - - Get("/foo/bar/") ~> - redirectToTrailingSlashIfMissing(MovedPermanently) { completeOk } ~> - check { status shouldEqual StatusCodes.OK } - } - } - - "the `redirectToNoTrailingSlashIfPresent` directive" should { - val route = redirectToNoTrailingSlashIfPresent(Found) { completeOk } - - "pass if the request path already doesn't have a trailing slash" in { - Get("/foo/bar") ~> route ~> check { response shouldEqual Ok } - } - - "redirect if the request path has a trailing slash" in { - Get("/foo/bar/") ~> route ~> checkRedirectTo("/foo/bar") - } - - "preserves the query and the frag when redirect" in { - Get("/foo/bar/?query#frag") ~> route ~> checkRedirectTo("/foo/bar?query#frag") - } - - "redirect with the given redirection status code" in { - Get("/foo/bar/") ~> - redirectToNoTrailingSlashIfPresent(MovedPermanently) { completeOk } ~> - check { status shouldEqual MovedPermanently } - } - } - - import akka.http.scaladsl.model.headers.Location - import akka.http.scaladsl.model.Uri - - private def checkRedirectTo(expectedUri: Uri) = - check { - status shouldBe a[Redirection] - inside(header[Location]) { - case Some(Location(uri)) ⇒ - (if (expectedUri.isAbsolute) uri else uri.toRelative) shouldEqual expectedUri - } - } -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/RangeDirectivesSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/RangeDirectivesSpec.scala deleted file mode 100644 index 7e9ad641c7..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/RangeDirectivesSpec.scala +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import scala.concurrent.Await -import scala.concurrent.duration._ -import akka.http.scaladsl.model.StatusCodes._ -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers._ -import akka.http.impl.util._ -import akka.stream.scaladsl.{ Sink, Source } -import akka.util.ByteString -import org.scalatest.{ Inside, Inspectors } - -class RangeDirectivesSpec extends RoutingSpec with Inspectors with Inside { - lazy val wrs = - mapSettings(_.withRangeCountLimit(10).withRangeCoalescingThreshold(1L)) & - withRangeSupport - - def bytes(length: Byte) = Array.tabulate[Byte](length)(_.toByte) - - "The `withRangeSupport` directive" should { - def completeWithRangedBytes(length: Byte) = - wrs(complete(HttpEntity.Default(ContentTypes.`application/octet-stream`, length, Source(bytes(length).map(ByteString(_)).toVector)))) - - "return an Accept-Ranges(bytes) header for GET requests" in { - Get() ~> { wrs { complete("any") } } ~> check { - headers should contain(`Accept-Ranges`(RangeUnits.Bytes)) - } - } - - "not return an Accept-Ranges(bytes) header for non-GET requests" in { - Put() ~> { wrs { complete("any") } } ~> check { - headers should not contain `Accept-Ranges`(RangeUnits.Bytes) - } - } - - "return a Content-Range header for a ranged request with a single range" in { - Get() ~> addHeader(Range(ByteRange(0, 1))) ~> completeWithRangedBytes(10) ~> check { - headers should contain(`Content-Range`(ContentRange(0, 1, 10))) - status shouldEqual PartialContent - responseAs[Array[Byte]] shouldEqual bytes(2) - } - } - - "return a partial response for a ranged request with a single range with undefined lastBytePosition" in { - Get() ~> addHeader(Range(ByteRange.fromOffset(5))) ~> completeWithRangedBytes(10) ~> check { - responseAs[Array[Byte]] shouldEqual Array[Byte](5, 6, 7, 8, 9) - } - } - - "return a partial response for a ranged request with a single suffix range" in { - Get() ~> addHeader(Range(ByteRange.suffix(1))) ~> completeWithRangedBytes(10) ~> check { - responseAs[Array[Byte]] shouldEqual Array[Byte](9) - } - } - - "return a partial response for a ranged request with a overlapping suffix range" in { - Get() ~> addHeader(Range(ByteRange.suffix(100))) ~> completeWithRangedBytes(10) ~> check { - responseAs[Array[Byte]] shouldEqual bytes(10) - } - } - - "be transparent to non-GET requests" in { - Post() ~> addHeader(Range(ByteRange(1, 2))) ~> completeWithRangedBytes(5) ~> check { - responseAs[Array[Byte]] shouldEqual bytes(5) - } - } - - "be transparent to non-200 responses" in { - Get() ~> addHeader(Range(ByteRange(1, 2))) ~> Route.seal(wrs(reject())) ~> check { - status == NotFound - headers.exists { case `Content-Range`(_, _) ⇒ true; case _ ⇒ false } shouldEqual false - } - } - - "reject an unsatisfiable single range" in { - Get() ~> addHeader(Range(ByteRange(100, 200))) ~> completeWithRangedBytes(10) ~> check { - rejection shouldEqual UnsatisfiableRangeRejection(ByteRange(100, 200) :: Nil, 10) - } - } - - "reject an unsatisfiable single suffix range with length 0" in { - Get() ~> addHeader(Range(ByteRange.suffix(0))) ~> completeWithRangedBytes(42) ~> check { - rejection shouldEqual UnsatisfiableRangeRejection(ByteRange.suffix(0) :: Nil, 42) - } - } - - "return a mediaType of 'multipart/byteranges' for a ranged request with multiple ranges" in { - Get() ~> addHeader(Range(ByteRange(0, 10), ByteRange(0, 10))) ~> completeWithRangedBytes(10) ~> check { - mediaType.withParams(Map.empty) shouldEqual MediaTypes.`multipart/byteranges` - } - } - - "return a 'multipart/byteranges' for a ranged request with multiple coalesced ranges and expect ranges in ascending order" in { - Get() ~> addHeader(Range(ByteRange(5, 10), ByteRange(0, 1), ByteRange(1, 2))) ~> { - wrs { complete("Some random and not super short entity.") } - } ~> check { - header[`Content-Range`] should be(None) - val parts = Await.result(responseAs[Multipart.ByteRanges].parts.limit(1000).runWith(Sink.seq), 1.second) - parts.size shouldEqual 2 - inside(parts(0)) { - case Multipart.ByteRanges.BodyPart(range, entity, unit, headers) ⇒ - range shouldEqual ContentRange.Default(0, 2, Some(39)) - unit shouldEqual RangeUnits.Bytes - Await.result(entity.dataBytes.utf8String, 100.millis) shouldEqual "Som" - } - inside(parts(1)) { - case Multipart.ByteRanges.BodyPart(range, entity, unit, headers) ⇒ - range shouldEqual ContentRange.Default(5, 10, Some(39)) - unit shouldEqual RangeUnits.Bytes - Await.result(entity.dataBytes.utf8String, 100.millis) shouldEqual "random" - } - } - } - - "return a 'multipart/byteranges' for a ranged request with multiple ranges if entity data source isn't reusable" in { - val content = "Some random and not super short entity." - def entityData() = StreamUtils.oneTimeSource(Source.single(ByteString(content))) - - Get() ~> addHeader(Range(ByteRange(5, 10), ByteRange(0, 1), ByteRange(1, 2))) ~> { - wrs { complete(HttpEntity.Default(ContentTypes.`text/plain(UTF-8)`, content.length, entityData())) } - } ~> check { - header[`Content-Range`] should be(None) - val parts = Await.result(responseAs[Multipart.ByteRanges].parts.limit(1000).runWith(Sink.seq), 1.second) - parts.size shouldEqual 2 - } - } - - "reject a request with too many requested ranges" in { - val ranges = (1 to 20).map(a ⇒ ByteRange.fromOffset(a)) - Get() ~> addHeader(Range(ranges)) ~> completeWithRangedBytes(100) ~> check { - rejection shouldEqual TooManyRangesRejection(10) - } - } - } -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/RespondWithDirectivesSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/RespondWithDirectivesSpec.scala deleted file mode 100644 index e28cc21899..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/RespondWithDirectivesSpec.scala +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server.directives - -import akka.http.scaladsl.model._ -import headers._ - -import akka.http.scaladsl.server._ - -class RespondWithDirectivesSpec extends RoutingSpec { - - val customHeader = RawHeader("custom", "custom") - val customHeader2 = RawHeader("custom2", "custom2") - val existingHeader = RawHeader("custom", "existing") - - "respondWithHeader" should { - val customHeader = RawHeader("custom", "custom") - "add the given header to successful responses" in { - Get() ~> { - respondWithHeader(customHeader) { completeOk } - } ~> check { response shouldEqual HttpResponse(headers = customHeader :: Nil) } - } - } - "respondWithHeaders" should { - "add the given headers to successful responses" in { - Get() ~> { - respondWithHeaders(customHeader, customHeader2) { completeOk } - } ~> check { response shouldEqual HttpResponse(headers = customHeader :: customHeader2 :: Nil) } - } - } - "respondWithDefaultHeader" should { - def route(extraHeaders: HttpHeader*) = respondWithDefaultHeader(customHeader) { - respondWithHeaders(extraHeaders: _*) { - completeOk - } - } - - "add the given header to a response if the header was missing before" in { - Get() ~> route() ~> check { response shouldEqual HttpResponse(headers = customHeader :: Nil) } - } - "not change a response if the header already existed" in { - Get() ~> route(existingHeader) ~> check { response shouldEqual HttpResponse(headers = existingHeader :: Nil) } - } - } - "respondWithDefaultHeaders" should { - def route(extraHeaders: HttpHeader*) = respondWithDefaultHeaders(customHeader, customHeader2) { - respondWithHeaders(extraHeaders: _*) { - completeOk - } - } - - "add the given headers to a response if the header was missing before" in { - Get() ~> route() ~> check { response shouldEqual HttpResponse(headers = customHeader :: customHeader2 :: Nil) } - } - "not update an existing header" in { - Get() ~> route(existingHeader) ~> check { - response shouldEqual HttpResponse(headers = List(customHeader2, existingHeader)) - } - } - } -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/RouteDirectivesSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/RouteDirectivesSpec.scala deleted file mode 100644 index df9ee7c2b5..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/RouteDirectivesSpec.scala +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server.directives - -import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport -import akka.http.scaladsl.marshallers.xml.ScalaXmlSupport -import org.scalatest.FreeSpec -import scala.concurrent.{ Future, Promise } -import akka.testkit.EventFilter -import akka.http.scaladsl.marshallers.xml.ScalaXmlSupport._ -import akka.http.scaladsl.marshalling._ -import akka.http.scaladsl.server._ -import akka.http.scaladsl.model._ -import akka.http.impl.util._ -import headers._ -import StatusCodes._ - -class RouteDirectivesSpec extends FreeSpec with GenericRoutingSpec { - - "The `complete` directive should" - { - "by chainable with the `&` operator" in { - Get() ~> (get & complete("yeah")) ~> check { responseAs[String] shouldEqual "yeah" } - } - "be lazy in its argument evaluation, independently of application style" in { - var i = 0 - Put() ~> { - get { complete { i += 1; "get" } } ~ - put { complete { i += 1; "put" } } ~ - (post & complete { i += 1; "post" }) - } ~> check { - responseAs[String] shouldEqual "put" - i shouldEqual 1 - } - } - "support completion from response futures" - { - "simple case without marshaller" in { - Get() ~> { - get & complete(Promise.successful(HttpResponse(entity = "yup")).future) - } ~> check { responseAs[String] shouldEqual "yup" } - } - "for successful futures and marshalling" in { - Get() ~> complete(Promise.successful("yes").future) ~> check { responseAs[String] shouldEqual "yes" } - } - "for failed futures and marshalling" in EventFilter[RuntimeException](occurrences = 1).intercept { - object TestException extends RuntimeException - Get() ~> complete(Promise.failed[String](TestException).future) ~> - check { - status shouldEqual StatusCodes.InternalServerError - responseAs[String] shouldEqual "There was an internal server error." - } - } - "for futures failed with a RejectionError" in { - Get() ~> complete(Promise.failed[String](RejectionError(AuthorizationFailedRejection)).future) ~> - check { - rejection shouldEqual AuthorizationFailedRejection - } - } - } - "allow easy handling of futured ToResponseMarshallers" in { - trait RegistrationStatus - case class Registered(name: String) extends RegistrationStatus - case object AlreadyRegistered extends RegistrationStatus - - val route = - get { - path("register" / Segment) { name ⇒ - def registerUser(name: String): Future[RegistrationStatus] = Future.successful { - name match { - case "otto" ⇒ AlreadyRegistered - case _ ⇒ Registered(name) - } - } - complete { - registerUser(name).map[ToResponseMarshallable] { - case Registered(_) ⇒ HttpEntity.Empty - case AlreadyRegistered ⇒ - import spray.json.DefaultJsonProtocol._ - import SprayJsonSupport._ - StatusCodes.BadRequest → Map("error" → "User already Registered") - } - } - } - } - - Get("/register/otto") ~> route ~> check { - status shouldEqual StatusCodes.BadRequest - } - Get("/register/karl") ~> route ~> check { - status shouldEqual StatusCodes.OK - responseAs[String] shouldEqual "" - } - } - "do Content-Type negotiation for multi-marshallers" in { - val route = get & complete(Data("Ida", 83)) - - import akka.http.scaladsl.model.headers.Accept - Get().withHeaders(Accept(MediaTypes.`application/json`)) ~> route ~> check { - responseAs[String] shouldEqual - """{"name":"Ida","age":83}""" - } - Get().withHeaders(Accept(MediaTypes.`text/xml`)) ~> route ~> check { - responseAs[xml.NodeSeq] shouldEqual Ida83 - } - Get().withHeaders(Accept(MediaTypes.`text/plain`)) ~> Route.seal(route) ~> check { - status shouldEqual StatusCodes.NotAcceptable - } - } - } - - "the redirect directive should" - { - "produce proper 'Found' redirections" in { - Get() ~> { - redirect("/foo", Found) - } ~> check { - response shouldEqual HttpResponse( - status = 302, - entity = HttpEntity( - ContentTypes.`text/html(UTF-8)`, - "The requested resource temporarily resides under this URI."), - headers = Location("/foo") :: Nil) - } - } - - "produce proper 'NotModified' redirections" in { - Get() ~> { - redirect("/foo", NotModified) - } ~> check { response shouldEqual HttpResponse(304, headers = Location("/foo") :: Nil) } - } - } - - case class Data(name: String, age: Int) - object Data { - import spray.json.DefaultJsonProtocol._ - import SprayJsonSupport._ - import ScalaXmlSupport._ - - val jsonMarshaller: ToEntityMarshaller[Data] = jsonFormat2(Data.apply) - - val xmlMarshaller: ToEntityMarshaller[Data] = Marshaller.combined { (data: Data) ⇒ - { data.name }{ data.age } - } - - implicit val dataMarshaller: ToResponseMarshaller[Data] = - Marshaller.oneOf(jsonMarshaller, xmlMarshaller) - } -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/SchemeDirectivesSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/SchemeDirectivesSpec.scala deleted file mode 100644 index b0faa3f2c2..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/SchemeDirectivesSpec.scala +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -class SchemeDirectivesSpec extends RoutingSpec { - "the extractScheme directive" should { - "extract the Uri scheme" in { - Put("http://localhost/", "Hello") ~> extractScheme { echoComplete } ~> check { responseAs[String] shouldEqual "http" } - } - } - - """the scheme("http") directive""" should { - "let requests with an http Uri scheme pass" in { - Put("http://localhost/", "Hello") ~> scheme("http") { completeOk } ~> check { response shouldEqual Ok } - } - "reject requests with an https Uri scheme" in { - Get("https://localhost/") ~> scheme("http") { completeOk } ~> check { rejections shouldEqual List(SchemeRejection("http")) } - } - "cancel SchemeRejection if other scheme passed" in { - val route = - scheme("https") { completeOk } ~ - scheme("http") { reject } - - Put("http://localhost/", "Hello") ~> route ~> check { - rejections should be(Nil) - } - } - } - - """the scheme("https") directive""" should { - "let requests with an https Uri scheme pass" in { - Put("https://localhost/", "Hello") ~> scheme("https") { completeOk } ~> check { response shouldEqual Ok } - } - "reject requests with an http Uri scheme" in { - Get("http://localhost/") ~> scheme("https") { completeOk } ~> check { rejections shouldEqual List(SchemeRejection("https")) } - } - } -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/SecurityDirectivesSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/SecurityDirectivesSpec.scala deleted file mode 100644 index 82b61d1f63..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/SecurityDirectivesSpec.scala +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import scala.concurrent.Future -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers._ -import akka.http.scaladsl.server.AuthenticationFailedRejection.{ CredentialsRejected, CredentialsMissing } -import akka.testkit.EventFilter - -class SecurityDirectivesSpec extends RoutingSpec { - val dontBasicAuth = authenticateBasicAsync[String]("MyRealm", _ ⇒ Future.successful(None)) - val dontOAuth2Auth = authenticateOAuth2Async[String]("MyRealm", _ ⇒ Future.successful(None)) - val doBasicAuth = authenticateBasicPF("MyRealm", { case Credentials.Provided(identifier) ⇒ identifier }) - val doOAuth2Auth = authenticateOAuth2PF("MyRealm", { case Credentials.Provided(identifier) ⇒ identifier }) - val authWithAnonymous = doBasicAuth.withAnonymousUser("We are Legion") - - val basicChallenge = HttpChallenges.basic("MyRealm") - val oAuth2Challenge = HttpChallenges.oAuth2("MyRealm") - - "basic authentication" should { - "reject requests without Authorization header with an AuthenticationFailedRejection" in { - Get() ~> { - dontBasicAuth { echoComplete } - } ~> check { rejection shouldEqual AuthenticationFailedRejection(CredentialsMissing, basicChallenge) } - } - "reject unauthenticated requests with Authorization header with an AuthenticationFailedRejection" in { - Get() ~> Authorization(BasicHttpCredentials("Bob", "")) ~> { - dontBasicAuth { echoComplete } - } ~> check { rejection shouldEqual AuthenticationFailedRejection(CredentialsRejected, basicChallenge) } - } - "reject requests with an OAuth2 Bearer Token Authorization header with 401" in { - Get() ~> Authorization(OAuth2BearerToken("myToken")) ~> Route.seal { - dontOAuth2Auth { echoComplete } - } ~> check { - status shouldEqual StatusCodes.Unauthorized - responseAs[String] shouldEqual "The supplied authentication is invalid" - header[`WWW-Authenticate`] shouldEqual Some(`WWW-Authenticate`(oAuth2Challenge)) - } - } - "reject requests with illegal Authorization header with 401" in { - Get() ~> RawHeader("Authorization", "bob alice") ~> Route.seal { - dontBasicAuth { echoComplete } - } ~> check { - status shouldEqual StatusCodes.Unauthorized - responseAs[String] shouldEqual "The resource requires authentication, which was not supplied with the request" - header[`WWW-Authenticate`] shouldEqual Some(`WWW-Authenticate`(basicChallenge)) - } - } - "extract the object representing the user identity created by successful authentication" in { - Get() ~> Authorization(BasicHttpCredentials("Alice", "")) ~> { - doBasicAuth { echoComplete } - } ~> check { responseAs[String] shouldEqual "Alice" } - } - "extract the object representing the user identity created for the anonymous user" in { - Get() ~> { - authWithAnonymous { echoComplete } - } ~> check { responseAs[String] shouldEqual "We are Legion" } - } - "properly handle exceptions thrown in its inner route" in { - object TestException extends RuntimeException - EventFilter[TestException.type](occurrences = 1).intercept { - Get() ~> Authorization(BasicHttpCredentials("Alice", "")) ~> { - Route.seal { - doBasicAuth { _ ⇒ throw TestException } - } - } ~> check { status shouldEqual StatusCodes.InternalServerError } - } - } - } - "bearer token authentication" should { - "reject requests without Authorization header with an AuthenticationFailedRejection" in { - Get() ~> { - dontOAuth2Auth { echoComplete } - } ~> check { rejection shouldEqual AuthenticationFailedRejection(CredentialsMissing, oAuth2Challenge) } - } - "reject unauthenticated requests with Authorization header with an AuthenticationFailedRejection" in { - Get() ~> Authorization(OAuth2BearerToken("myToken")) ~> { - dontOAuth2Auth { echoComplete } - } ~> check { rejection shouldEqual AuthenticationFailedRejection(CredentialsRejected, oAuth2Challenge) } - } - "reject requests with a Basic Authorization header with 401" in { - Get() ~> Authorization(BasicHttpCredentials("Alice", "")) ~> Route.seal { - dontBasicAuth { echoComplete } - } ~> check { - status shouldEqual StatusCodes.Unauthorized - responseAs[String] shouldEqual "The supplied authentication is invalid" - header[`WWW-Authenticate`] shouldEqual Some(`WWW-Authenticate`(basicChallenge)) - } - } - "reject requests with illegal Authorization header with 401" in { - Get() ~> RawHeader("Authorization", "bob alice") ~> Route.seal { - dontOAuth2Auth { echoComplete } - } ~> check { - status shouldEqual StatusCodes.Unauthorized - responseAs[String] shouldEqual "The resource requires authentication, which was not supplied with the request" - header[`WWW-Authenticate`] shouldEqual Some(`WWW-Authenticate`(oAuth2Challenge)) - } - } - "extract the object representing the user identity created by successful authentication" in { - Get() ~> Authorization(OAuth2BearerToken("myToken")) ~> { - doOAuth2Auth { echoComplete } - } ~> check { responseAs[String] shouldEqual "myToken" } - } - "extract the object representing the user identity created for the anonymous user" in { - Get() ~> { - authWithAnonymous { echoComplete } - } ~> check { responseAs[String] shouldEqual "We are Legion" } - } - "properly handle exceptions thrown in its inner route" in { - object TestException extends RuntimeException - EventFilter[TestException.type](occurrences = 1).intercept { - Get() ~> Authorization(OAuth2BearerToken("myToken")) ~> { - Route.seal { - doOAuth2Auth { _ ⇒ throw TestException } - } - } ~> check { status shouldEqual StatusCodes.InternalServerError } - } - } - } - "authentication directives" should { - "properly stack" in { - val otherChallenge = HttpChallenge("MyAuth", Some("MyRealm2")) - val otherAuth: Directive1[String] = authenticateOrRejectWithChallenge { (cred: Option[HttpCredentials]) ⇒ - Future.successful(Left(otherChallenge)) - } - val bothAuth = dontBasicAuth | otherAuth - - Get() ~> Route.seal(bothAuth { echoComplete }) ~> check { - status shouldEqual StatusCodes.Unauthorized - headers.collect { - case `WWW-Authenticate`(challenge +: Nil) ⇒ challenge - } shouldEqual Seq(basicChallenge, otherChallenge) - } - } - } - - "authorization directives" should { - "authorize" in { - Get() ~> { - authorize(_ ⇒ true) { complete("OK") } - } ~> check { responseAs[String] shouldEqual "OK" } - } - "not authorize" in { - Get() ~> { - authorize(_ ⇒ false) { complete("OK") } - } ~> check { rejection shouldEqual AuthorizationFailedRejection } - } - - "authorizeAsync" in { - Get() ~> { - authorizeAsync(_ ⇒ Future.successful(true)) { complete("OK") } - } ~> check { responseAs[String] shouldEqual "OK" } - } - "not authorizeAsync" in { - Get() ~> { - authorizeAsync(_ ⇒ Future.successful(false)) { complete("OK") } - } ~> check { rejection shouldEqual AuthorizationFailedRejection } - } - "not authorizeAsync when future fails" in { - Get() ~> { - authorizeAsync(_ ⇒ Future.failed(new Exception("Boom!"))) { complete("OK") } - } ~> check { rejection shouldEqual AuthorizationFailedRejection } - } - } - -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/TimeoutDirectivesSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/TimeoutDirectivesSpec.scala deleted file mode 100644 index 243955b585..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/TimeoutDirectivesSpec.scala +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server.directives - -import akka.http.scaladsl.model.{ HttpResponse, StatusCodes } -import akka.http.scaladsl.server.IntegrationRoutingSpec - -import scala.concurrent.duration._ -import scala.concurrent.{ Future, Promise } - -class TimeoutDirectivesSpec extends IntegrationRoutingSpec { - - "Request Timeout" should { - "be configurable in routing layer" in { - - val route = path("timeout") { - withRequestTimeout(3.seconds) { - val response: Future[String] = slowFuture() // very slow - complete(response) - } - } - - Get("/timeout") ~!> route ~!> { response ⇒ - import response._ - - status should ===(StatusCodes.ServiceUnavailable) - } - } - } - - "allow mapping the response" in { - val timeoutResponse = HttpResponse( - StatusCodes.EnhanceYourCalm, - entity = "Unable to serve response within time limit, please enchance your calm.") - - val route = - path("timeout") { - // needs to be long because of the race between wRT and wRTR - withRequestTimeout(1.second) { - withRequestTimeoutResponse(request ⇒ timeoutResponse) { - val response: Future[String] = slowFuture() // very slow - complete(response) - } - } - } ~ - path("equivalent") { - // updates timeout and handler at - withRequestTimeout(1.second, request ⇒ timeoutResponse) { - val response: Future[String] = slowFuture() // very slow - complete(response) - } - } - - Get("/timeout") ~!> route ~!> { response ⇒ - import response._ - status should ===(StatusCodes.EnhanceYourCalm) - } - - Get("/equivalent") ~!> route ~!> { response ⇒ - import response._ - status should ===(StatusCodes.EnhanceYourCalm) - } - } - - def slowFuture(): Future[String] = Promise[String].future - -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/WebSocketDirectivesSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/WebSocketDirectivesSpec.scala deleted file mode 100644 index 14e5860dee..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/WebSocketDirectivesSpec.scala +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server.directives - -import akka.util.ByteString - -import akka.stream.OverflowStrategy -import akka.stream.scaladsl.{ Source, Sink, Flow } - -import akka.http.scaladsl.testkit.WSProbe - -import akka.http.scaladsl.model.headers.`Sec-WebSocket-Protocol` -import akka.http.scaladsl.model.StatusCodes -import akka.http.scaladsl.model.ws._ -import akka.http.scaladsl.server.{ UnsupportedWebSocketSubprotocolRejection, ExpectedWebSocketRequestRejection, Route, RoutingSpec } - -class WebSocketDirectivesSpec extends RoutingSpec { - "the handleWebSocketMessages directive" should { - "handle websocket requests" in { - val wsClient = WSProbe() - - WS("http://localhost/", wsClient.flow) ~> websocketRoute ~> - check { - isWebSocketUpgrade shouldEqual true - wsClient.sendMessage("Peter") - wsClient.expectMessage("Hello Peter!") - - wsClient.sendMessage(BinaryMessage(ByteString("abcdef"))) - // wsClient.expectNoMessage() // will be checked implicitly by next expectation - - wsClient.sendMessage("John") - wsClient.expectMessage("Hello John!") - - wsClient.sendCompletion() - wsClient.expectCompletion() - } - } - "choose subprotocol from offered ones" in { - val wsClient = WSProbe() - - WS("http://localhost/", wsClient.flow, List("other", "echo", "greeter")) ~> websocketMultipleProtocolRoute ~> - check { - expectWebSocketUpgradeWithProtocol { protocol ⇒ - protocol shouldEqual "echo" - - wsClient.sendMessage("Peter") - wsClient.expectMessage("Peter") - - wsClient.sendMessage(BinaryMessage(ByteString("abcdef"))) - wsClient.expectMessage(ByteString("abcdef")) - - wsClient.sendMessage("John") - wsClient.expectMessage("John") - - wsClient.sendCompletion() - wsClient.expectCompletion() - } - } - } - "reject websocket requests if no subprotocol matches" in { - WS("http://localhost/", Flow[Message], List("other")) ~> websocketMultipleProtocolRoute ~> check { - rejections.collect { - case UnsupportedWebSocketSubprotocolRejection(p) ⇒ p - }.toSet shouldEqual Set("greeter", "echo") - } - - WS("http://localhost/", Flow[Message], List("other")) ~> Route.seal(websocketMultipleProtocolRoute) ~> check { - status shouldEqual StatusCodes.BadRequest - responseAs[String] shouldEqual "None of the websocket subprotocols offered in the request are supported. Supported are 'echo','greeter'." - header[`Sec-WebSocket-Protocol`].get.protocols.toSet shouldEqual Set("greeter", "echo") - } - } - "reject non-websocket requests" in { - Get("http://localhost/") ~> websocketRoute ~> check { - rejection shouldEqual ExpectedWebSocketRequestRejection - } - - Get("http://localhost/") ~> Route.seal(websocketRoute) ~> check { - status shouldEqual StatusCodes.BadRequest - responseAs[String] shouldEqual "Expected WebSocket Upgrade request" - } - } - } - - def websocketRoute = handleWebSocketMessages(greeter) - def websocketMultipleProtocolRoute = - handleWebSocketMessagesForProtocol(echo, "echo") ~ - handleWebSocketMessagesForProtocol(greeter, "greeter") - - def greeter: Flow[Message, Message, Any] = - Flow[Message].mapConcat { - case tm: TextMessage ⇒ TextMessage(Source.single("Hello ") ++ tm.textStream ++ Source.single("!")) :: Nil - case bm: BinaryMessage ⇒ // ignore binary messages - bm.dataStream.runWith(Sink.ignore) - Nil - } - - def echo: Flow[Message, Message, Any] = - Flow[Message] - .buffer(1, OverflowStrategy.backpressure) // needed because a noop flow hasn't any buffer that would start processing -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/util/TupleOpsSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/util/TupleOpsSpec.scala deleted file mode 100644 index bebbe3b607..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/util/TupleOpsSpec.scala +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server.util - -import org.scalatest.{ Matchers, WordSpec } - -class TupleOpsSpec extends WordSpec with Matchers { - import TupleOps._ - - "The TupleOps" should { - - "support folding over tuples using a binary poly-function" in { - object Funky extends BinaryPolyFunc { - implicit def step1 = at[Double, Int](_ + _) - implicit def step2 = at[Double, Symbol]((d, s) ⇒ (d + s.name.tail.toInt).toByte) - implicit def step3 = at[Byte, String]((byte, s) ⇒ byte + s.toLong) - } - (1, 'X2, "3").foldLeft(0.0)(Funky) shouldEqual 6L - } - - "support joining tuples" in { - (1, 'X2, "3") join (()) shouldEqual ((1, 'X2, "3")) - () join ((1, 'X2, "3")) shouldEqual ((1, 'X2, "3")) - (1, 'X2, "3") join ((4.0, 5L)) shouldEqual ((1, 'X2, "3", 4.0, 5L)) - } - } -} \ No newline at end of file diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/unmarshalling/MultipartUnmarshallersSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/unmarshalling/MultipartUnmarshallersSpec.scala deleted file mode 100644 index e81b302e46..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/unmarshalling/MultipartUnmarshallersSpec.scala +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.unmarshalling - -import scala.concurrent.duration._ -import scala.concurrent.{ Future, Await } -import org.scalatest.matchers.Matcher -import org.scalatest.{ BeforeAndAfterAll, FreeSpec, Matchers } -import akka.http.scaladsl.testkit.ScalatestUtils -import akka.util.ByteString -import akka.actor.ActorSystem -import akka.stream.ActorMaterializer -import akka.stream.scaladsl._ -import akka.http.scaladsl.model._ -import akka.http.scaladsl.util.FastFuture._ -import akka.http.impl.util._ -import akka.http.scaladsl.model.headers._ -import MediaTypes._ -import HttpCharsets._ - -class MultipartUnmarshallersSpec extends FreeSpec with Matchers with BeforeAndAfterAll with ScalatestUtils { - implicit val system = ActorSystem(getClass.getSimpleName) - implicit val materializer = ActorMaterializer() - import system.dispatcher - - "The MultipartUnmarshallers." - { - - "multipartGeneralUnmarshaller should correctly unmarshal 'multipart/*' content with" - { - "an empty part" in { - Unmarshal(HttpEntity( - `multipart/mixed` withBoundary "XYZABC" withCharset `UTF-8`, - """--XYZABC - |--XYZABC--""".stripMarginWithNewline("\r\n"))).to[Multipart.General] should haveParts( - Multipart.General.BodyPart.Strict(HttpEntity.empty(ContentTypes.`text/plain(UTF-8)`))) - } - "two empty parts" in { - Unmarshal(HttpEntity( - `multipart/mixed` withBoundary "XYZABC" withCharset `UTF-8`, - """--XYZABC - |--XYZABC - |--XYZABC--""".stripMarginWithNewline("\r\n"))).to[Multipart.General] should haveParts( - Multipart.General.BodyPart.Strict(HttpEntity.empty(ContentTypes.`text/plain(UTF-8)`)), - Multipart.General.BodyPart.Strict(HttpEntity.empty(ContentTypes.`text/plain(UTF-8)`))) - } - "a part without entity and missing header separation CRLF" in { - Unmarshal(HttpEntity( - `multipart/mixed` withBoundary "XYZABC" withCharset `UTF-8`, - """--XYZABC - |Content-type: text/xml - |Age: 12 - |--XYZABC--""".stripMarginWithNewline("\r\n"))).to[Multipart.General] should haveParts( - Multipart.General.BodyPart.Strict(HttpEntity.empty(ContentTypes.`text/xml(UTF-8)`), List(Age(12)))) - } - "an implicitly typed part (without headers) (Strict)" in { - Unmarshal(HttpEntity( - `multipart/mixed` withBoundary "XYZABC" withCharset `UTF-8`, - """--XYZABC - | - |Perfectly fine part content. - |--XYZABC--""".stripMarginWithNewline("\r\n"))).to[Multipart.General] should haveParts( - Multipart.General.BodyPart.Strict(HttpEntity(ContentTypes.`text/plain(UTF-8)`, "Perfectly fine part content."))) - } - "an implicitly typed part (without headers) (Default)" in { - val content = """--XYZABC - | - |Perfectly fine part content. - |--XYZABC--""".stripMarginWithNewline("\r\n") - val byteStrings = content.map(c ⇒ ByteString(c.toString)) // one-char ByteStrings - Unmarshal(HttpEntity.Default(`multipart/mixed` withBoundary "XYZABC" withCharset `UTF-8`, content.length, Source(byteStrings))) - .to[Multipart.General] should haveParts( - Multipart.General.BodyPart.Strict(HttpEntity(ContentTypes.`text/plain(UTF-8)`, "Perfectly fine part content."))) - } - "one non-empty form-data part" in { - Unmarshal(HttpEntity( - `multipart/form-data` withBoundary "-" withCharset `UTF-8`, - """--- - |Content-type: text/plain; charset=UTF8 - |content-disposition: form-data; name="email" - | - |test@there.com - |-----""".stripMarginWithNewline("\r\n"))).to[Multipart.General] should haveParts( - Multipart.General.BodyPart.Strict( - HttpEntity(ContentTypes.`text/plain(UTF-8)`, "test@there.com"), - List(`Content-Disposition`(ContentDispositionTypes.`form-data`, Map("name" → "email"))))) - } - "two different parts" in { - Unmarshal(HttpEntity( - `multipart/mixed` withBoundary "12345" withCharset `UTF-8`, - """--12345 - | - |first part, with a trailing newline - | - |--12345 - |Content-Type: application/octet-stream - |Content-Transfer-Encoding: binary - | - |filecontent - |--12345--""".stripMarginWithNewline("\r\n"))).to[Multipart.General] should haveParts( - Multipart.General.BodyPart.Strict(HttpEntity(ContentTypes.`text/plain(UTF-8)`, "first part, with a trailing newline\r\n")), - Multipart.General.BodyPart.Strict( - HttpEntity(`application/octet-stream`, ByteString("filecontent")), - List(RawHeader("Content-Transfer-Encoding", "binary")))) - } - "illegal headers" in ( - Unmarshal(HttpEntity( - `multipart/form-data` withBoundary "XYZABC" withCharset `UTF-8`, - """--XYZABC - |Date: unknown - |content-disposition: form-data; name=email - | - |test@there.com - |--XYZABC--""".stripMarginWithNewline("\r\n"))).to[Multipart.General] should haveParts( - Multipart.General.BodyPart.Strict( - HttpEntity(ContentTypes.`text/plain(UTF-8)`, "test@there.com"), - List( - RawHeader("date", "unknown"), - `Content-Disposition`(ContentDispositionTypes.`form-data`, Map("name" → "email")))))) - "a full example (Strict)" in { - Unmarshal(HttpEntity( - `multipart/mixed` withBoundary "12345" withCharset `UTF-8`, - """preamble and - |more preamble - |--12345 - | - |first part, implicitly typed - |--12345 - |Content-Type: application/octet-stream - | - |second part, explicitly typed - |--12345-- - |epilogue and - |more epilogue""".stripMarginWithNewline("\r\n"))).to[Multipart.General] should haveParts( - Multipart.General.BodyPart.Strict(HttpEntity(ContentTypes.`text/plain(UTF-8)`, "first part, implicitly typed")), - Multipart.General.BodyPart.Strict(HttpEntity(`application/octet-stream`, ByteString("second part, explicitly typed")))) - } - "a full example (Default)" in { - val content = """preamble and - |more preamble - |--12345 - | - |first part, implicitly typed - |--12345 - |Content-Type: application/octet-stream - | - |second part, explicitly typed - |--12345-- - |epilogue and - |more epilogue""".stripMarginWithNewline("\r\n") - val byteStrings = content.map(c ⇒ ByteString(c.toString)) // one-char ByteStrings - Unmarshal(HttpEntity.Default(`multipart/mixed` withBoundary "12345" withCharset `UTF-8`, content.length, Source(byteStrings))) - .to[Multipart.General] should haveParts( - Multipart.General.BodyPart.Strict(HttpEntity(ContentTypes.`text/plain(UTF-8)`, "first part, implicitly typed")), - Multipart.General.BodyPart.Strict(HttpEntity(`application/octet-stream`, ByteString("second part, explicitly typed")))) - } - "a boundary with spaces" in { - Unmarshal(HttpEntity( - `multipart/mixed` withBoundary "simple boundary" withCharset `UTF-8`, - """--simple boundary - |--simple boundary--""".stripMarginWithNewline("\r\n"))).to[Multipart.General] should haveParts( - Multipart.General.BodyPart.Strict(HttpEntity.empty(ContentTypes.`text/plain(UTF-8)`))) - } - } - - "multipartGeneralUnmarshaller should reject illegal multipart content with" - { - "an empty entity" in { - Await.result(Unmarshal(HttpEntity(`multipart/mixed` withBoundary "XYZABC" withCharset `UTF-8`, ByteString.empty)) - .to[Multipart.General].failed, 1.second).getMessage shouldEqual "Unexpected end of multipart entity" - } - "an entity without initial boundary" in { - Await.result(Unmarshal(HttpEntity( - `multipart/mixed` withBoundary "XYZABC" withCharset `UTF-8`, - """this is - |just preamble text""".stripMarginWithNewline("\r\n"))) - .to[Multipart.General].failed, 1.second).getMessage shouldEqual "Unexpected end of multipart entity" - } - "a stray boundary" in { - Await.result(Unmarshal(HttpEntity( - `multipart/form-data` withBoundary "ABC" withCharset `UTF-8`, - """--ABC - |Content-type: text/plain; charset=UTF8 - |--ABCContent-type: application/json - |content-disposition: form-data; name="email" - |-----""".stripMarginWithNewline("\r\n"))) - .to[Multipart.General].failed, 1.second).getMessage shouldEqual "Illegal multipart boundary in message content" - } - "duplicate Content-Type header" in { - Await.result(Unmarshal(HttpEntity( - `multipart/form-data` withBoundary "-" withCharset `UTF-8`, - """--- - |Content-type: text/plain; charset=UTF8 - |Content-type: application/json - |content-disposition: form-data; name="email" - | - |test@there.com - |-----""".stripMarginWithNewline("\r\n"))) - .to[Multipart.General].failed, 1.second).getMessage shouldEqual - "multipart part must not contain more than one Content-Type header" - } - "a missing header-separating CRLF (in Strict entity)" in { - Await.result(Unmarshal(HttpEntity( - `multipart/form-data` withBoundary "-" withCharset `UTF-8`, - """--- - |not good here - |-----""".stripMarginWithNewline("\r\n"))) - .to[Multipart.General].failed, 1.second).getMessage shouldEqual "Illegal character ' ' in header name" - } - "a missing header-separating CRLF (in Default entity)" in { - val content = """--- - | - |ok - |--- - |not ok - |-----""".stripMarginWithNewline("\r\n") - val byteStrings = content.map(c ⇒ ByteString(c.toString)) // one-char ByteStrings - val contentType = `multipart/form-data` withBoundary "-" withCharset `UTF-8` - Await.result(Unmarshal(HttpEntity.Default(contentType, content.length, Source(byteStrings))) - .to[Multipart.General] - .flatMap(_ toStrict 1.second).failed, 1.second).getMessage shouldEqual "Illegal character ' ' in header name" - } - "a boundary with a trailing space" in { - Await.result( - Unmarshal(HttpEntity(`multipart/mixed` withBoundary "simple boundary " withCharset `UTF-8`, ByteString.empty)) - .to[Multipart.General].failed, 1.second).getMessage shouldEqual - "requirement failed: 'boundary' parameter of multipart Content-Type must not end with a space char" - } - "a boundary with an illegal character" in { - Await.result( - Unmarshal(HttpEntity(`multipart/mixed` withBoundary "simple&boundary" withCharset `UTF-8`, ByteString.empty)) - .to[Multipart.General].failed, 1.second).getMessage shouldEqual - "requirement failed: 'boundary' parameter of multipart Content-Type contains illegal character '&'" - } - } - - "multipartByteRangesUnmarshaller should correctly unmarshal multipart/byteranges content with two different parts" in { - Unmarshal(HttpEntity( - `multipart/byteranges` withBoundary "12345" withCharset `UTF-8`, - """--12345 - |Content-Range: bytes 0-2/26 - |Content-Type: text/plain - | - |ABC - |--12345 - |Content-Range: bytes 23-25/26 - |Content-Type: text/plain - | - |XYZ - |--12345--""".stripMarginWithNewline("\r\n"))).to[Multipart.ByteRanges] should haveParts( - Multipart.ByteRanges.BodyPart.Strict(ContentRange(0, 2, 26), HttpEntity(ContentTypes.`text/plain(UTF-8)`, "ABC")), - Multipart.ByteRanges.BodyPart.Strict(ContentRange(23, 25, 26), HttpEntity(ContentTypes.`text/plain(UTF-8)`, "XYZ"))) - } - - "multipartFormDataUnmarshaller should correctly unmarshal 'multipart/form-data' content" - { - "with one element and no explicit content-type" in { - Unmarshal(HttpEntity( - `multipart/form-data` withBoundary "XYZABC" withCharset `UTF-8`, - """--XYZABC - |content-disposition: form-data; name=email - | - |test@there.com - |--XYZABC--""".stripMarginWithNewline("\r\n"))).to[Multipart.FormData] should haveParts( - Multipart.FormData.BodyPart.Strict("email", HttpEntity(ContentTypes.`text/plain(UTF-8)`, "test@there.com"))) - } - "with one element" in { - Unmarshal(HttpEntity( - `multipart/form-data` withBoundary "XYZABC" withCharset `UTF-8`, - """--XYZABC - |content-disposition: form-data; name=email - |Content-Type: application/octet-stream - | - |test@there.com - |--XYZABC--""".stripMarginWithNewline("\r\n"))).to[Multipart.FormData] should haveParts( - Multipart.FormData.BodyPart.Strict("email", HttpEntity(`application/octet-stream`, ByteString("test@there.com")))) - } - "with a file" in { - Unmarshal { - HttpEntity.Default( - contentType = `multipart/form-data` withBoundary "XYZABC" withCharset `UTF-8`, - contentLength = 1, // not verified during unmarshalling - data = Source { - List( - ByteString { - """--XYZABC - |Content-Disposition: form-data; name="email" - |Content-Type: application/octet-stream - | - |test@there.com - |--XYZABC - |Content-Dispo""".stripMarginWithNewline("\r\n") - }, - ByteString { - """sition: form-data; name="userfile"; filename="test€.dat" - |Content-Type: application/pdf - |Content-Transfer-Encoding: binary - |Content-Additional-1: anything - |Content-Additional-2: really-anything - | - |filecontent - |--XYZABC--""".stripMarginWithNewline("\r\n") - }) - }) - }.to[Multipart.FormData].flatMap(_.toStrict(1.second)) should haveParts( - Multipart.FormData.BodyPart.Strict("email", HttpEntity(`application/octet-stream`, ByteString("test@there.com"))), - Multipart.FormData.BodyPart.Strict("userfile", HttpEntity(`application/pdf`, ByteString("filecontent")), Map("filename" → "test€.dat"), - List( - RawHeader("Content-Transfer-Encoding", "binary"), - RawHeader("Content-Additional-1", "anything"), - RawHeader("Content-Additional-2", "really-anything")))) // verifies order of headers is preserved - } - // TODO: reactivate after multipart/form-data unmarshalling integrity verification is implemented - // see https://github.com/akka/akka/issues/18908 - // - // "reject illegal multipart content" in { - // val Left(MalformedContent(msg, _)) = HttpEntity(`multipart/form-data` withBoundary "XYZABC", "--noboundary--").as[MultipartFormData] - // msg shouldEqual "Missing start boundary" - // } - // "reject illegal form-data content" in { - // val Left(MalformedContent(msg, _)) = HttpEntity(`multipart/form-data` withBoundary "XYZABC", - // """|--XYZABC - // |content-disposition: form-data; named="email" - // | - // |test@there.com - // |--XYZABC--""".stripMargin).as[MultipartFormData] - // msg shouldEqual "Illegal multipart/form-data content: unnamed body part (no Content-Disposition header or no 'name' parameter)" - // } - } - } - - override def afterAll() = system.terminate() - - def haveParts[T <: Multipart](parts: Multipart.BodyPart.Strict*): Matcher[Future[T]] = - equal(parts).matcher[Seq[Multipart.BodyPart.Strict]] compose { x ⇒ - Await.result(x - .fast.flatMap { - _.parts - .mapAsync(Int.MaxValue)(_ toStrict 1.second) - .grouped(100) - .runWith(Sink.head) - } - .fast.recover { case _: NoSuchElementException ⇒ Nil }, 1.second) - } -} diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/unmarshalling/UnmarshallingSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/unmarshalling/UnmarshallingSpec.scala deleted file mode 100644 index 88aca7fa40..0000000000 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/unmarshalling/UnmarshallingSpec.scala +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.unmarshalling - -import akka.http.scaladsl.unmarshalling.Unmarshaller.EitherUnmarshallingException -import org.scalatest.{ BeforeAndAfterAll, FreeSpec, Matchers } -import akka.http.scaladsl.testkit.ScalatestUtils -import akka.actor.ActorSystem -import akka.stream.ActorMaterializer -import akka.http.scaladsl.model._ -import scala.concurrent.duration._ - -import scala.concurrent.Await - -class UnmarshallingSpec extends FreeSpec with Matchers with BeforeAndAfterAll with ScalatestUtils { - implicit val system = ActorSystem(getClass.getSimpleName) - implicit val materializer = ActorMaterializer() - import system.dispatcher - - "The PredefinedFromEntityUnmarshallers" - { - "stringUnmarshaller should unmarshal `text/plain` content in UTF-8 to Strings" in { - Unmarshal(HttpEntity("Hällö")).to[String] should evaluateTo("Hällö") - } - "charArrayUnmarshaller should unmarshal `text/plain` content in UTF-8 to char arrays" in { - Unmarshal(HttpEntity("árvíztűrő ütvefúrógép")).to[Array[Char]] should evaluateTo("árvíztűrő ütvefúrógép".toCharArray) - } - } - - "The PredefinedFromStringUnmarshallers" - { - "booleanUnmarshaller should unmarshal '1' => true '0' => false" in { - Unmarshal("1").to[Boolean] should evaluateTo(true) - Unmarshal("0").to[Boolean] should evaluateTo(false) - } - } - - "The GenericUnmarshallers" - { - implicit val rawInt: FromEntityUnmarshaller[Int] = Unmarshaller(implicit ex ⇒ bs ⇒ bs.toStrict(1.second).map(_.data.utf8String.toInt)) - implicit val rawlong: FromEntityUnmarshaller[Long] = Unmarshaller(implicit ex ⇒ bs ⇒ bs.toStrict(1.second).map(_.data.utf8String.toLong)) - - "eitherUnmarshaller should unmarshal its Right value" in { - // we'll find: - // PredefinedFromEntityUnmarshallers.eitherUnmarshaller[String, Int] will be found - // - // which finds: - // rawInt: FromEntityUnmarshaller[Int] - // + - // stringUnmarshaller: FromEntityUnmarshaller[String] - - val testRight = Unmarshal(HttpEntity("42")).to[Either[String, Int]] - Await.result(testRight, 1.second) should ===(Right(42)) - } - - "eitherUnmarshaller should unmarshal its Left value" in { - val testLeft = Unmarshal(HttpEntity("I'm not a number, I'm a free man!")).to[Either[String, Int]] - Await.result(testLeft, 1.second) should ===(Left("I'm not a number, I'm a free man!")) - } - - "eitherUnmarshaller report both error messages if unmarshalling failed" in { - type ImmenseChoice = Either[Long, Int] - val testLeft = Unmarshal(HttpEntity("I'm not a number, I'm a free man!")).to[ImmenseChoice] - val ex = intercept[EitherUnmarshallingException] { - Await.result(testLeft, 1.second) - } - - ex.getMessage should include("Either[long, int]") - ex.getMessage should include("attempted int first") - ex.getMessage should include("Right failure: For input string") - ex.getMessage should include("Left failure: For input string") - } - - } - - override def afterAll() = system.terminate() -} diff --git a/akka-http/build.sbt b/akka-http/build.sbt deleted file mode 100644 index 54f1a31a9d..0000000000 --- a/akka-http/build.sbt +++ /dev/null @@ -1,11 +0,0 @@ -import akka._ - -AkkaBuild.defaultSettings -AkkaBuild.experimentalSettings -Formatting.formatSettings -OSGi.http -Dependencies.http - -disablePlugins(MimaPlugin) // still experimental -enablePlugins(spray.boilerplate.BoilerplatePlugin) -scalacOptions in Compile += "-language:_" diff --git a/akka-http/src/main/boilerplate/akka/http/javadsl/server/JavaPathMatchers.scala.template b/akka-http/src/main/boilerplate/akka/http/javadsl/server/JavaPathMatchers.scala.template deleted file mode 100644 index cac37f262e..0000000000 --- a/akka-http/src/main/boilerplate/akka/http/javadsl/server/JavaPathMatchers.scala.template +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.javadsl.server - -import java.util.List -import java.util.function.{BiFunction, Function} -import akka.http.scaladsl.server.{ PathMatcher ⇒ SPathMatcher } -import akka.http.scaladsl.server.{ PathMatchers ⇒ SPathMatchers } -import akka.http.scaladsl.server.PathMatcher -import java.util.{ List ⇒ JList } -import java.util.UUID -import java.util.function.BiFunction -import java.util.function.{ Function ⇒ JFunction } -import akka.japi.function._ -import java.util.regex.Pattern -import scala.collection.JavaConverters._ - -import akka.http.scaladsl.server.PathMatcher -import java.util.{List => JList} - -import scala.language.implicitConversions - -object JavaPathMatchers { - - /* This is a workaround for sbt-boilerplate being limited with 2 as the max distance between number and generated number */ - type PathMatcherTwoMoreThan1[A, B, T1] = PathMatcher3[A, B, T1] - type PathMatcherTwoMoreThan2[A, B, T1, T2] = PathMatcher4[A, B, T1, T2] - type PathMatcherTwoMoreThan3[A, B, T1, T2, T3] = PathMatcher5[A, B, T1, T2, T3] - type PathMatcherTwoMoreThan4[A, B, T1, T2, T3, T4] = PathMatcher6[A, B, T1, T2, T3, T4] - type PathMatcherTwoMoreThan5[A, B, T1, T2, T3, T4, T5] = PathMatcher7[A, B, T1, T2, T3, T4, T5] - type PathMatcherTwoMoreThan6[A, B, T1, T2, T3, T4, T5, T6] = PathMatcher8[A, B, T1, T2, T3, T4, T5, T6] - type PathMatcherTwoMoreThan7[A, B, T1, T2, T3, T4, T5, T6, T7] = PathMatcher9[A, B, T1, T2, T3, T4, T5, T6, T7] - type PathMatcherTwoMoreThan8[A, B, T1, T2, T3, T4, T5, T6, T7, T8] = PathMatcher10[A, B, T1, T2, T3, T4, T5, T6, T7, T8] - type PathMatcherTwoMoreThan9[A, B, T1, T2, T3, T4, T5, T6, T7, T8, T9] = PathMatcher11[A, B, T1, T2, T3, T4, T5, T6, T7, T8, T9] - type PathMatcherTwoMoreThan10[A, B, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10] = PathMatcher12[A, B, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10] - type PathMatcherTwoMoreThan11[A, B, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11] = PathMatcher13[A, B, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11] - type PathMatcherTwoMoreThan12[A, B, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12] = PathMatcher14[A, B, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12] - type PathMatcherTwoMoreThan13[A, B, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13] = PathMatcher15[A, B, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13] - type PathMatcherTwoMoreThan14[A, B, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14] = PathMatcher16[A, B, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14] - type PathMatcherTwoMoreThan15[A, B, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15] = PathMatcher17[A, B, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15] - type PathMatcherTwoMoreThan16[A, B, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16] = PathMatcher18[A, B, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16] - type PathMatcherTwoMoreThan17[A, B, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17] = PathMatcher19[A, B, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17] - type PathMatcherTwoMoreThan18[A, B, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18] = PathMatcher20[A, B, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18] - type PathMatcherTwoMoreThan19[A, B, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19] = PathMatcher21[A, B, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19] - type PathMatcherTwoMoreThan20[A, B, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20] = PathMatcher22[A, B, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20] - - /** INTERNAL API */ - private[server] implicit def fromScala0(scalaMatcher: SPathMatcher[Unit]): PathMatcher0 = - new PathMatcher0(scalaMatcher) - - [..22#/** INTERNAL API */ - private[server] def fromScala1[[#T1#]](scalaMatcher: SPathMatcher[Tuple1[[#T1#]]]): PathMatcher1[[#T1#]] = - new PathMatcher1(scalaMatcher) - # - ] - - - [..20#/** INTERNAL API */ - private[server] def fromScalaTwoMoreThan1[A, B, [#T1#]](scalaMatcher: SPathMatcher[(A, B, [#T1#])]) = - new PathMatcherTwoMoreThan1[A, B, [#T1#]](scalaMatcher) - # - ] - -} - -/** - * A PathMatcher tries to match a prefix of a given string and returns either a PathMatcher.Matched instance - * if matched, otherwise PathMatchers.Unmatched. - */ -// Generated class, do not edit in place, but edit template instead. -final class PathMatcher0(val toScala: SPathMatcher[Unit]) { - import JavaPathMatchers._ - - def slash() = fromScala0(toScala./) - - def slash(segment: String) : PathMatcher0 = fromScala0(toScala / segment) - def slash(next: PathMatcher0) : PathMatcher0 = fromScala0(toScala / next.toScala) - def slash[T](next: PathMatcher1[T]) : PathMatcher1[T] = fromScala1(toScala / next.toScala) - def slash[T1, T2](next: PathMatcher2[T1, T2]): PathMatcher2[T1, T2] = fromScala2(toScala / next.toScala) - - def concat(segment: String) : PathMatcher0 = fromScala0(toScala ~ segment) - def concat(next: PathMatcher0) : PathMatcher0 = fromScala0(toScala ~ next.toScala) - def concat[T](next: PathMatcher1[T]) : PathMatcher1[T] = fromScala1(toScala ~ next.toScala) - def concat[T1, T2](next: PathMatcher2[T1, T2]): PathMatcher2[T1, T2] = fromScala2(toScala ~ next.toScala) - - def orElse(segment: String) = fromScala0(toScala | segment) - def orElse(alternative: PathMatcher0) = fromScala0(toScala | alternative.toScala) - - def invert = fromScala0(!toScala) - - def repeat(min: Int, max: Int): PathMatcher0 = repeat(min, max, SPathMatchers.Neutral) - def repeat(min: Int, max: Int, separator: PathMatcher0): PathMatcher0 = fromScala0(toScala.repeat(min, max, separator.toScala)) - -} - -/** - * A PathMatcher tries to match a prefix of a given string and returns either a PathMatcher.Matched instance - * if matched, otherwise PathMatchers.Unmatched. - */ -// Generated class, do not edit in place, but edit template instead. -final class PathMatcher1[T1](val toScala: SPathMatcher[Tuple1[T1]]) { - import JavaPathMatchers._ - - def slash() : PathMatcher1[T1] = fromScala1(toScala./) - def slash(segment: String) : PathMatcher1[T1] = fromScala1(toScala / segment) - def slash(next: PathMatcher0) : PathMatcher1[T1] = fromScala1(toScala./(next.toScala)) - def slash[N](next: PathMatcher1[N]) = fromScala2(toScala / next.toScala) - def slash[N1, N2](next: PathMatcher2[N1, N2]) = fromScalaTwoMoreThan1(toScala / next.toScala) - - def concat(segment: String) : PathMatcher1[T1] = fromScala1(toScala ~ segment) - def concat(next: PathMatcher0) : PathMatcher1[T1] = fromScala1(toScala ~ next.toScala) - def concat[N](next: PathMatcher1[N]) = fromScala2(toScala ~ next.toScala) - def concat[N1, N2](next: PathMatcher2[N1, N2]) = fromScalaTwoMoreThan1(toScala ~ next.toScala) - - def orElse(alternative: PathMatcher1[T1]) = fromScala1(toScala | alternative.toScala) - - def invert = fromScala0(!toScala) - - def repeat(min: Int, max: Int): PathMatcher1[JList[T1]] = repeat(min, max, SPathMatchers.Neutral) - def repeat(min: Int, max: Int, separator: PathMatcher0): PathMatcher1[JList[T1]] = - fromScala1(toScala.repeat(min, max, separator.toScala).map(_.asJava)) - - def map[U](f: JFunction[T1, U]) = - fromScala1(toScala.map(t ⇒ f.apply(t))) -} - -[2..20#/** - * A PathMatcher tries to match a prefix of a given string and returns either a PathMatcher.Matched instance - * if matched, otherwise PathMatchers.Unmatched. - */ -// Generated class, do not edit in place, but edit template instead. -final class PathMatcher1[[#T1#]](val toScala: SPathMatcher[Tuple1[[#T1#]]]) { - import JavaPathMatchers._ - - def slash() : PathMatcher1[[#T1#]] = fromScala1(toScala./) - def slash(segment: String) : PathMatcher1[[#T1#]] = fromScala1(toScala / segment) - def slash(next: PathMatcher##0) : PathMatcher1[[#T1#]] = fromScala1(toScala./(next.toScala)) - def slash[N](next: PathMatcher##1[N]) = fromScalaTwoMoreThan0(toScala / next.toScala) - def slash[[..2#N1#]](next: PathMatcher##2[[..2#N1#]]) = fromScalaTwoMoreThan1(toScala / next.toScala) - - def concat(segment: String) : PathMatcher1[[#T1#]] = fromScala1(toScala ~ segment) - def concat(next: PathMatcher##0) : PathMatcher1[[#T1#]] = fromScala1(toScala ~ next.toScala) - def concat[N](next: PathMatcher##1[N]) : PathMatcher2[[#T1#], N] = fromScalaTwoMoreThan0(toScala ~ next.toScala) - def concat[[..2#N1#]](next: PathMatcher##2[[..2#N1#]]) = fromScalaTwoMoreThan1(toScala ~ next.toScala) - - def orElse(alternative: PathMatcher1[[#T1#]]) = fromScala1(toScala | alternative.toScala) - - def invert = fromScala##0(!toScala) - -} -# -] - -/* The last 21 and 22 are special as we are not able to generate slash/concat for them (would need to add more params) */ - -/** - * A PathMatcher tries to match a prefix of a given string and returns either a PathMatcher.Matched instance - * if matched, otherwise PathMatchers.Unmatched. - * - * It is not possible to append more matchers with keeping their values as parameters (due to limit of Tuple22). - */ -// Generated class, do not edit in place, but edit template instead. -final class PathMatcher21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21](val toScala: SPathMatcher[Tuple21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21]]) { - import JavaPathMatchers._ - - def slash() : PathMatcher21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21] = fromScala21(toScala./) - def slash(segment: String) : PathMatcher21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21] = fromScala21(toScala / segment) - def slash(next: PathMatcher0) : PathMatcher21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21] = fromScala21(toScala./(next.toScala)) - def slash[N](next: PathMatcher1[N]) = fromScala22(toScala / next.toScala) - - def concat(segment: String) : PathMatcher21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21] = fromScala21(toScala ~ segment) - def concat(next: PathMatcher0) : PathMatcher21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21] = fromScala21(toScala ~ next.toScala) - def concat[N](next: PathMatcher1[N]) = fromScala22(toScala ~ next.toScala) - - def orElse(alternative: PathMatcher21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21]) = fromScala21(toScala | alternative.toScala) - - def invert = fromScala0(!toScala) - -} - - -/** - * A PathMatcher tries to match a prefix of a given string and returns either a PathMatcher.Matched instance - * if matched, otherwise PathMatchers.Unmatched. - * - * It is not possible to append more matchers with keeping their values as parameters (due to limit of Tuple22). - */ -// Generated class, do not edit in place, but edit template instead. -final class PathMatcher22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22](val toScala: SPathMatcher[Tuple22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22]]) { - import JavaPathMatchers._ - - def slash() : PathMatcher22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22] = fromScala22(toScala./) - def slash(segment: String) : PathMatcher22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22] = fromScala22(toScala / segment) - def slash(next: PathMatcher0) : PathMatcher22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22] = fromScala22(toScala./(next.toScala)) - - def concat(segment: String) : PathMatcher22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22] = fromScala22(toScala ~ segment) - def concat(next: PathMatcher0) : PathMatcher22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22] = fromScala22(toScala ~ next.toScala) - - def orElse(alternative: PathMatcher22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22]) = fromScala22(toScala | alternative.toScala) - - def invert = fromScala0(!toScala) - -} diff --git a/akka-http/src/main/boilerplate/akka/http/scaladsl/server/util/ApplyConverterInstances.scala.template b/akka-http/src/main/boilerplate/akka/http/scaladsl/server/util/ApplyConverterInstances.scala.template deleted file mode 100644 index a8e265e11a..0000000000 --- a/akka-http/src/main/boilerplate/akka/http/scaladsl/server/util/ApplyConverterInstances.scala.template +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (C) 2009-2014 Lightbend Inc. - */ - -package akka.http.scaladsl.server.util - -import akka.http.scaladsl.server.Route - -private[util] abstract class ApplyConverterInstances { - [#implicit def hac1[[#T1#]]: ApplyConverter[Tuple1[[#T1#]]] { type In = ([#T1#]) ⇒ Route } = new ApplyConverter[Tuple1[[#T1#]]] { - type In = ([#T1#]) ⇒ Route - def apply(fn: In): (Tuple1[[#T1#]]) ⇒ Route = { - case Tuple1([#t1#]) ⇒ fn([#t1#]) - } - }# - ] -} \ No newline at end of file diff --git a/akka-http/src/main/boilerplate/akka/http/scaladsl/server/util/ConstructFromTupleInstances.scala.template b/akka-http/src/main/boilerplate/akka/http/scaladsl/server/util/ConstructFromTupleInstances.scala.template deleted file mode 100644 index 0f21eb0384..0000000000 --- a/akka-http/src/main/boilerplate/akka/http/scaladsl/server/util/ConstructFromTupleInstances.scala.template +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (C) 2009-2014 Lightbend Inc. - */ - -package akka.http.scaladsl.server.util - -private[util] abstract class ConstructFromTupleInstances { - [#implicit def instance1[[#T1#], R](construct: ([#T1#]) => R): ConstructFromTuple[Tuple1[[#T1#]], R] = - new ConstructFromTuple[Tuple1[[#T1#]], R] { - def apply(tup: Tuple1[[#T1#]]): R = construct([#tup._1#]) - }# - ] -} diff --git a/akka-http/src/main/boilerplate/akka/http/scaladsl/server/util/TupleAppendOneInstances.scala.template b/akka-http/src/main/boilerplate/akka/http/scaladsl/server/util/TupleAppendOneInstances.scala.template deleted file mode 100644 index c368ce9457..0000000000 --- a/akka-http/src/main/boilerplate/akka/http/scaladsl/server/util/TupleAppendOneInstances.scala.template +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2009-2014 Lightbend Inc. - */ - -package akka.http.scaladsl.server.util - -import TupleOps.AppendOne - -private[util] abstract class TupleAppendOneInstances { - type Aux[P, S, Out0] = AppendOne[P, S] { type Out = Out0 } - - implicit def append0[T1]: Aux[Unit, T1, Tuple1[T1]] = - new AppendOne[Unit, T1] { - type Out = Tuple1[T1] - def apply(prefix: Unit, last: T1): Tuple1[T1] = Tuple1(last) - } - - [1..21#implicit def append1[[#T1#], L]: Aux[Tuple1[[#T1#]], L, Tuple2[[#T1#], L]] = - new AppendOne[Tuple1[[#T1#]], L] { - type Out = Tuple2[[#T1#], L] - def apply(prefix: Tuple1[[#T1#]], last: L): Tuple2[[#T1#], L] = Tuple2([#prefix._1#], last) - }# - ] -} \ No newline at end of file diff --git a/akka-http/src/main/boilerplate/akka/http/scaladsl/server/util/TupleFoldInstances.scala.template b/akka-http/src/main/boilerplate/akka/http/scaladsl/server/util/TupleFoldInstances.scala.template deleted file mode 100644 index d8b7421966..0000000000 --- a/akka-http/src/main/boilerplate/akka/http/scaladsl/server/util/TupleFoldInstances.scala.template +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2009-2014 Lightbend Inc. - */ - -package akka.http.scaladsl.server.util - -import TupleOps.FoldLeft -import BinaryPolyFunc.Case - -private[util] abstract class TupleFoldInstances { - - type Aux[In, T, Op, Out0] = FoldLeft[In, T, Op] { type Out = Out0 } - - implicit def t0[In, Op]: Aux[In, Unit, Op, In] = - new FoldLeft[In, Unit, Op] { - type Out = In - def apply(zero: In, tuple: Unit) = zero - } - - implicit def t1[In, A, Op](implicit f: Case[In, A, Op]): Aux[In, Tuple1[A], Op, f.Out] = - new FoldLeft[In, Tuple1[A], Op] { - type Out = f.Out - def apply(zero: In, tuple: Tuple1[A]) = f(zero, tuple._1) - } - - [2..22#implicit def t1[In, [2..#T0#], X, T1, Op](implicit fold: Aux[In, Tuple0[[2..#T0#]], Op, X], f: Case[X, T1, Op]): Aux[In, Tuple1[[#T1#]], Op, f.Out] = - new FoldLeft[In, Tuple1[[#T1#]], Op] { - type Out = f.Out - def apply(zero: In, t: Tuple1[[#T1#]]) = - f(fold(zero, Tuple0([2..#t._0#])), t._1) - }# - ] -} \ No newline at end of file diff --git a/akka-http/src/main/java/akka/http/javadsl/coding/Coder.java b/akka-http/src/main/java/akka/http/javadsl/coding/Coder.java deleted file mode 100644 index 7d2196a305..0000000000 --- a/akka-http/src/main/java/akka/http/javadsl/coding/Coder.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.coding; - -import java.util.concurrent.CompletionStage; - -import akka.http.scaladsl.coding.Deflate$; -import akka.http.scaladsl.coding.Gzip$; -import akka.http.scaladsl.coding.NoCoding$; -import akka.stream.Materializer; -import akka.util.ByteString; -import scala.compat.java8.FutureConverters; - -/** - * A coder is an implementation of the predefined encoders/decoders defined for HTTP. - */ -public enum Coder { - NoCoding(NoCoding$.MODULE$), Deflate(Deflate$.MODULE$), Gzip(Gzip$.MODULE$); - - private akka.http.scaladsl.coding.Coder underlying; - - Coder(akka.http.scaladsl.coding.Coder underlying) { - this.underlying = underlying; - } - - public ByteString encode(ByteString input) { - return underlying.encode(input); - } - public CompletionStage decode(ByteString input, Materializer mat) { - return FutureConverters.toJava(underlying.decode(input, mat)); - } - public akka.http.scaladsl.coding.Coder _underlyingScalaCoder() { - return underlying; - } -} diff --git a/akka-http/src/main/java/akka/http/javadsl/common/RegexConverters.java b/akka-http/src/main/java/akka/http/javadsl/common/RegexConverters.java deleted file mode 100644 index 51bd2a24e5..0000000000 --- a/akka-http/src/main/java/akka/http/javadsl/common/RegexConverters.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.javadsl.common; - -import java.util.regex.Pattern; - -import scala.collection.Seq; -import scala.collection.immutable.VectorBuilder; -import scala.util.matching.Regex; - -public final class RegexConverters { - private static final Seq empty = new VectorBuilder().result(); - - /** - * Converts the given Java Pattern into a scala Regex, without recompiling it. - */ - public static Regex toScala(Pattern p) { - return new Regex(p, empty); - } -} diff --git a/akka-http/src/main/java/akka/http/javadsl/server/ExceptionHandlerBuilder.java b/akka-http/src/main/java/akka/http/javadsl/server/ExceptionHandlerBuilder.java deleted file mode 100644 index b12520ac0a..0000000000 --- a/akka-http/src/main/java/akka/http/javadsl/server/ExceptionHandlerBuilder.java +++ /dev/null @@ -1,71 +0,0 @@ -package akka.http.javadsl.server; - -import akka.japi.pf.FI; -import akka.japi.pf.PFBuilder; - -public class ExceptionHandlerBuilder { - private final PFBuilder delegate; - - public ExceptionHandlerBuilder() { - this(new PFBuilder<>()); - } - - private ExceptionHandlerBuilder(PFBuilder delegate) { - this.delegate = delegate; - } - - /** - * Add a new case statement to this builder. - * - * @param type a type to match the argument against - * @param apply an action to apply to the argument if the type matches - * @return a builder with the case statement added - */ - public

ExceptionHandlerBuilder match(final Class

type, FI.Apply apply) { - delegate.match(type, apply); - return this; - } - - /** - * Add a new case statement to this builder. - * - * @param type a type to match the argument against - * @param predicate a predicate that will be evaluated on the argument if the type matches - * @param apply an action to apply to the argument if the type matches and the predicate returns true - * @return a builder with the case statement added - */ - public

ExceptionHandlerBuilder match(final Class

type, - final FI.TypedPredicate

predicate, - final FI.Apply apply) { - delegate.match(type, predicate, apply); - return this; - } - - /** - * Add a new case statement to this builder. - * - * @param object the object to compare equals with - * @param apply an action to apply to the argument if the object compares equal - * @return a builder with the case statement added - */ - public

ExceptionHandlerBuilder matchEquals(final P object, - final FI.Apply apply) { - delegate.matchEquals(object, apply); - return this; - } - - /** - * Add a new case statement to this builder, that matches any argument. - * - * @param apply an action to apply to the argument - * @return a builder with the case statement added - */ - public ExceptionHandlerBuilder matchAny(final FI.Apply apply) { - delegate.matchAny(apply); - return this; - } - - public ExceptionHandler build() { - return ExceptionHandler.of(delegate.build()); - } -} diff --git a/akka-http/src/main/java/akka/http/javadsl/server/directives/CorrespondsTo.java b/akka-http/src/main/java/akka/http/javadsl/server/directives/CorrespondsTo.java deleted file mode 100644 index a44d09283c..0000000000 --- a/akka-http/src/main/java/akka/http/javadsl/server/directives/CorrespondsTo.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.javadsl.server.directives; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * INTERNAL API – used for consistency specs - * - * Used to hint at consistency spec implementations that a given JavaDSL method corresponds - * to a method of given name in ScalaDSL. - * - * E.g. a Java method paramsList could be hinted using @CorrespondsTo("paramsSeq"). - */ -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -public @interface CorrespondsTo { - String value(); -} diff --git a/akka-http/src/main/java/akka/http/javadsl/unmarshalling/StringUnmarshallers.java b/akka-http/src/main/java/akka/http/javadsl/unmarshalling/StringUnmarshallers.java deleted file mode 100644 index 4cd74b89d8..0000000000 --- a/akka-http/src/main/java/akka/http/javadsl/unmarshalling/StringUnmarshallers.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.javadsl.unmarshalling; - -import java.util.function.Function; - -public final class StringUnmarshallers { - /** - * An unmarshaller that returns the input String unchanged. - */ - public static final Unmarshaller STRING = StringUnmarshaller.sync(Function.identity()); - - /** - * An unmarshaller that parses the input String as a Byte in decimal notation. - */ - public static final Unmarshaller BYTE = assumeBoxed(StringUnmarshallerPredef.byteFromStringUnmarshaller()); - - /** - * An unmarshaller that parses the input String as a Short in decimal notation. - */ - public static final Unmarshaller SHORT = assumeBoxed(StringUnmarshallerPredef.shortFromStringUnmarshaller()); - - /** - * An unmarshaller that parses the input String as an Integer in decimal notation. - */ - public static final Unmarshaller INTEGER = assumeBoxed(StringUnmarshallerPredef.intFromStringUnmarshaller()); - - /** - * An unmarshaller that parses the input String as a Long in decimal notation. - */ - public static final Unmarshaller LONG = assumeBoxed(StringUnmarshallerPredef.longFromStringUnmarshaller()); - - /** - * An unmarshaller that parses the input String as a Byte in hexadecimal notation. - */ - public static final Unmarshaller BYTE_HEX = assumeBoxed(StringUnmarshallerPredef.HexByte()); - - /** - * An unmarshaller that parses the input String as a Short in hexadecimal notation. - */ - public static final Unmarshaller SHORT_HEX = assumeBoxed(StringUnmarshallerPredef.HexShort()); - - /** - * An unmarshaller that parses the input String as an Integer in hexadecimal notation. - */ - public static final Unmarshaller INTEGER_HEX = assumeBoxed(StringUnmarshallerPredef.HexInt()); - - /** - * An unmarshaller that parses the input String as a Long in hexadecimal notation. - */ - public static final Unmarshaller LONG_HEX = assumeBoxed(StringUnmarshallerPredef.HexLong()); - - /** - * An unmarshaller that parses the input String as a Float in decimal notation. - */ - public static final Unmarshaller FLOAT = assumeBoxed(StringUnmarshallerPredef.floatFromStringUnmarshaller()); - - /** - * An unmarshaller that parses the input String as a Double in decimal notation. - */ - public static final Unmarshaller DOUBLE = assumeBoxed(StringUnmarshallerPredef.doubleFromStringUnmarshaller()); - - /** - * An unmarshaller that parses the input String as a Boolean, matching "true", "yes", "on" as true, and "false", "no", "off" as false. - */ - public static final Unmarshaller BOOLEAN = assumeBoxed(StringUnmarshallerPredef.booleanFromStringUnmarshaller()); - - /** - * An unmarshaller that parses the input String as a UUID. - */ - public static final Unmarshaller UUID = StringUnmarshaller.sync(java.util.UUID::fromString); - - /** - * Assume that the given [src] is a marshaller to a Scala boxed primitive type, and coerces into the given Java boxed type T. - */ - @SuppressWarnings("unchecked") - private static Unmarshaller assumeBoxed(akka.http.scaladsl.unmarshalling.Unmarshaller src) { - return (Unmarshaller) src; - } -} diff --git a/akka-http/src/main/resources/reference.conf b/akka-http/src/main/resources/reference.conf deleted file mode 100644 index 87a3f24d1e..0000000000 --- a/akka-http/src/main/resources/reference.conf +++ /dev/null @@ -1,43 +0,0 @@ -####################################### -# akka-http Reference Config File # -####################################### - -# This is the reference config file that contains all the default settings. -# Make your edits/overrides in your application.conf. - -akka.http.routing { - # Enables/disables the returning of more detailed error messages to the - # client in the error response - # Should be disabled for browser-facing APIs due to the risk of XSS attacks - # and (probably) enabled for internal or non-browser APIs - # (Note that akka-http will always produce log messages containing the full error details) - verbose-error-messages = off - - # Enables/disables ETag and `If-Modified-Since` support for FileAndResourceDirectives - file-get-conditional = on - - # Enables/disables the rendering of the "rendered by" footer in directory listings - render-vanity-footer = yes - - # The maximum size between two requested ranges. Ranges with less space in between will be coalesced. - # - # When multiple ranges are requested, a server may coalesce any of the ranges that overlap or that are separated - # by a gap that is smaller than the overhead of sending multiple parts, regardless of the order in which the - # corresponding byte-range-spec appeared in the received Range header field. Since the typical overhead between - # parts of a multipart/byteranges payload is around 80 bytes, depending on the selected representation's - # media type and the chosen boundary parameter length, it can be less efficient to transfer many small - # disjoint parts than it is to transfer the entire selected representation. - range-coalescing-threshold = 80 - - # The maximum number of allowed ranges per request. - # Requests with more ranges will be rejected due to DOS suspicion. - range-count-limit = 16 - - # The maximum number of bytes per ByteString a decoding directive will produce - # for an entity data stream. - decode-max-bytes-per-chunk = 1m - - # Fully qualified config path which holds the dispatcher configuration - # to be used by FlowMaterialiser when creating Actors for IO operations. - file-io-dispatcher = ${akka.stream.blocking-io-dispatcher} -} diff --git a/akka-http/src/main/scala/akka/http/javadsl/common/EntityStreamingSupport.scala b/akka-http/src/main/scala/akka/http/javadsl/common/EntityStreamingSupport.scala deleted file mode 100644 index b2c461f4c8..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/common/EntityStreamingSupport.scala +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (C) 2016 Lightbend Inc. - */ - -package akka.http.javadsl.common - -import akka.NotUsed -import akka.http.javadsl.model.{ ContentType, ContentTypeRange } -import akka.http.scaladsl.common -import akka.stream.javadsl.Flow -import akka.util.ByteString - -/** - * Entity streaming support trait allowing rendering and receiving incoming ``Source[T, _]`` from HTTP entities. - * - * See [[JsonEntityStreamingSupport]] or [[CsvEntityStreamingSupport]] for default implementations. - */ -abstract class EntityStreamingSupport { - - /** Read-side, what content types it is able to frame and unmarshall. */ - def supported: ContentTypeRange - /** Write-side, defines what Content-Type the Marshaller should offer and the final Content-Type of the response. */ - def contentType: ContentType - - /** - * Read-side, decode incoming framed entity. - * For example with an incoming JSON array, chunk it up into JSON objects contained within that array. - */ - def getFramingDecoder: Flow[ByteString, ByteString, NotUsed] - /** - * Write-side, apply framing to outgoing entity stream. - * - * Most typical usage will be a variant of `Flow[ByteString].intersperse`. - * - * For example for rendering a JSON array one would return - * `Flow[ByteString].intersperse(ByteString("["), ByteString(","), ByteString("]"))` - * and for rendering a new-line separated CSV simply `Flow[ByteString].intersperse(ByteString("\n"))`. - */ - def getFramingRenderer: Flow[ByteString, ByteString, NotUsed] - - /** - * Read-side, allows changing what content types are accepted by this framing. - * - * EntityStreamingSupport traits MUST support re-configuring the accepted [[ContentTypeRange]]. - * - * This is in order to support a-typical APIs which users still want to communicate with using - * the provided support trait. Typical examples include APIs which return valid `application/json` - * however advertise the content type as being `application/javascript` or vendor specific content types, - * which still parse correctly as JSON, CSV or something else that a provided support trait is built for. - * - * NOTE: Implementations should specialize the return type to their own Type! - */ - def withSupported(range: ContentTypeRange): EntityStreamingSupport - - /** - * Write-side, defines what Content-Type the Marshaller should offer and the final Content-Type of the response. - * - * EntityStreamingSupport traits MUST support re-configuring the offered [[ContentType]]. - * This is due to the need integrating with existing systems which sometimes excpect custom Content-Types, - * however really are just plain JSON or something else internally (perhaps with slight extensions). - * - * NOTE: Implementations should specialize the return type to their own Type! - */ - def withContentType(range: ContentType): EntityStreamingSupport - - /** - * Write-side / read-side, defines if (un)marshalling should be done in parallel. - * - * This may be beneficial marshalling the bottleneck in the pipeline. - * - * See also [[parallelism]] and [[withParallelMarshalling]]. - */ - def parallelism: Int - - /** - * Write-side / read-side, defines if (un)marshalling of incoming stream elements should be perserved or not. - * - * Allowing for parallel and unordered (un)marshalling often yields higher throughput and also allows avoiding - * head-of-line blocking if some elements are much larger than others. - * - * See also [[parallelism]] and [[withParallelMarshalling]]. - */ - def unordered: Boolean - - /** - * Write-side / read-side, defines parallelism and if ordering should be preserved or not of Source element marshalling. - * - * Sometimes marshalling multiple elements at once (esp. when elements are not evenly sized, and ordering is not enforced) - * may yield in higher throughput. - * - * NOTE: Implementations should specialize the return type to their own Type! - */ - def withParallelMarshalling(parallelism: Int, unordered: Boolean): EntityStreamingSupport - -} - -/** - * Entity streaming support, independent of used Json parsing library etc. - */ -object EntityStreamingSupport { - - /** - * Default `application/json` entity streaming support. - * - * Provides framing (based on scanning the incoming dataBytes for valid JSON objects, so for example uploads using arrays or - * new-line separated JSON objects are all parsed correctly) and rendering of Sources as JSON Arrays. - * A different very popular style of returning streaming JSON is to separate JSON objects on a line-by-line basis, - * you can configure the support trait to do so by calling `withFramingRendererFlow`. - * - * Limits the maximum JSON object length to 8KB, if you want to increase this limit provide a value explicitly. - * - * See also https://en.wikipedia.org/wiki/JSON_Streaming - */ - def json(): JsonEntityStreamingSupport = json(8 * 1024) - /** - * Default `application/json` entity streaming support. - * - * Provides framing (based on scanning the incoming dataBytes for valid JSON objects, so for example uploads using arrays or - * new-line separated JSON objects are all parsed correctly) and rendering of Sources as JSON Arrays. - * A different very popular style of returning streaming JSON is to separate JSON objects on a line-by-line basis, - * you can configure the support trait to do so by calling `withFramingRendererFlow`. - * - * See also https://en.wikipedia.org/wiki/JSON_Streaming - */ - def json(maxObjectLength: Int): JsonEntityStreamingSupport = common.EntityStreamingSupport.json(maxObjectLength) - - /** - * Default `text/csv(UTF-8)` entity streaming support. - * Provides framing and rendering of `\n` separated lines and marshalling Sources into such values. - * - * Limits the maximum line-length to 8KB, if you want to increase this limit provide a value explicitly. - */ - def csv(): CsvEntityStreamingSupport = csv(8 * 1024) - /** - * Default `text/csv(UTF-8)` entity streaming support. - * Provides framing and rendering of `\n` separated lines and marshalling Sources into such values. - */ - def csv(maxLineLength: Int): CsvEntityStreamingSupport = common.EntityStreamingSupport.csv(maxLineLength) -} - -// extends Scala base, in order to get linearization right and (as we can't go into traits here, because companion object needed) -abstract class JsonEntityStreamingSupport extends common.EntityStreamingSupport { - def withFramingRendererFlow(flow: Flow[ByteString, ByteString, NotUsed]): JsonEntityStreamingSupport -} - -// extends Scala base, in order to get linearization right and (as we can't go into traits here, because companion object needed) -abstract class CsvEntityStreamingSupport extends common.EntityStreamingSupport { - def withFramingRendererFlow(flow: Flow[ByteString, ByteString, NotUsed]): CsvEntityStreamingSupport -} diff --git a/akka-http/src/main/scala/akka/http/javadsl/marshalling/Marshaller.scala b/akka-http/src/main/scala/akka/http/javadsl/marshalling/Marshaller.scala deleted file mode 100644 index dfd7388652..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/marshalling/Marshaller.scala +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.marshalling - -import java.util.function - -import akka.http.impl.util.JavaMapping -import akka.http.javadsl.model.{ ContentType, HttpHeader, HttpResponse, MediaType, RequestEntity, StatusCode } -import akka.http.scaladsl -import akka.http.scaladsl.marshalling -import akka.http.scaladsl.marshalling._ -import akka.http.scaladsl.model.{ FormData, HttpCharset } -import akka.japi.Util -import akka.util.ByteString - -import scala.concurrent.ExecutionContext -import scala.annotation.unchecked.uncheckedVariance -import scala.language.implicitConversions - -object Marshaller { - - import JavaMapping.Implicits._ - - def fromScala[A, B](scalaMarshaller: marshalling.Marshaller[A, B]): Marshaller[A, B] = new Marshaller()(scalaMarshaller) - - /** - * Safe downcasting of the output type of the marshaller to a superclass. - * - * Marshaller is covariant in B, i.e. if B2 is a subclass of B1, - * then Marshaller[X,B2] is OK to use where Marshaller[X,B1] is expected. - */ - def downcast[A, B1, B2 <: B1](m: Marshaller[A, B2]): Marshaller[A, B1] = m.asInstanceOf[Marshaller[A, B1]] - - /** - * Safe downcasting of the output type of the marshaller to a superclass. - * - * Marshaller is covariant in B, i.e. if B2 is a subclass of B1, - * then Marshaller[X,B2] is OK to use where Marshaller[X,B1] is expected. - */ - def downcast[A, B1, B2 <: B1](m: Marshaller[A, B2], target: Class[B1]): Marshaller[A, B1] = m.asInstanceOf[Marshaller[A, B1]] - - def stringToEntity: Marshaller[String, RequestEntity] = fromScala(marshalling.Marshaller.StringMarshaller) - - def byteArrayToEntity: Marshaller[Array[Byte], RequestEntity] = fromScala(marshalling.Marshaller.ByteArrayMarshaller) - - def charArrayToEntity: Marshaller[Array[Char], RequestEntity] = fromScala(marshalling.Marshaller.CharArrayMarshaller) - - def byteStringToEntity: Marshaller[ByteString, RequestEntity] = fromScala(marshalling.Marshaller.ByteStringMarshaller) - - def fromDataToEntity: Marshaller[FormData, RequestEntity] = fromScala(marshalling.Marshaller.FormDataMarshaller) - - def byteStringMarshaller(t: ContentType): Marshaller[ByteString, RequestEntity] = - fromScala(scaladsl.marshalling.Marshaller.byteStringMarshaller(t.asScala)) - - // TODO make sure these are actually usable in a sane way - - def wrapEntity[A, C](f: function.BiFunction[ExecutionContext, C, A], m: Marshaller[A, RequestEntity], mediaType: MediaType): Marshaller[C, RequestEntity] = { - val scalaMarshaller = m.asScalaCastOutput - fromScala(scalaMarshaller.wrapWithEC(mediaType.asScala) { ctx ⇒ c: C ⇒ f(ctx, c) }(ContentTypeOverrider.forEntity)) - } - - def wrapEntity[A, C, E <: RequestEntity](f: function.Function[C, A], m: Marshaller[A, E], mediaType: MediaType): Marshaller[C, RequestEntity] = { - val scalaMarshaller = m.asScalaCastOutput - fromScala(scalaMarshaller.wrap(mediaType.asScala)((in: C) ⇒ f.apply(in))(ContentTypeOverrider.forEntity)) - } - - def entityToOKResponse[A](m: Marshaller[A, _ <: RequestEntity]): Marshaller[A, HttpResponse] = { - fromScala(marshalling.Marshaller.fromToEntityMarshaller[A]()(m.asScalaCastOutput)) - } - - def entityToResponse[A, R <: RequestEntity](status: StatusCode, m: Marshaller[A, R]): Marshaller[A, HttpResponse] = { - fromScala(marshalling.Marshaller.fromToEntityMarshaller[A](status.asScala)(m.asScalaCastOutput)) - } - - def entityToResponse[A](status: StatusCode, headers: java.lang.Iterable[HttpHeader], m: Marshaller[A, _ <: RequestEntity]): Marshaller[A, HttpResponse] = { - fromScala(marshalling.Marshaller.fromToEntityMarshaller[A](status.asScala, Util.immutableSeq(headers).map(_.asScala))(m.asScalaCastOutput)) // TODO can we avoid the map() ? - } - - def entityToOKResponse[A](headers: java.lang.Iterable[HttpHeader], m: Marshaller[A, _ <: RequestEntity]): Marshaller[A, HttpResponse] = { - fromScala(marshalling.Marshaller.fromToEntityMarshaller[A](headers = Util.immutableSeq(headers).map(_.asScala))(m.asScalaCastOutput)) // TODO avoid the map() - } - - // these are methods not varargs to avoid call site warning about unchecked type params - - /** - * Helper for creating a "super-marshaller" from a number of "sub-marshallers". - * Content-negotiation determines, which "sub-marshaller" eventually gets to do the job. - */ - def oneOf[A, B](ms: Marshaller[A, B]*): Marshaller[A, B] = { - fromScala(marshalling.Marshaller.oneOf[A, B](ms.map(_.asScala): _*)) - } - - /** - * Helper for creating a "super-marshaller" from a number of "sub-marshallers". - * Content-negotiation determines, which "sub-marshaller" eventually gets to do the job. - */ - def oneOf[A, B](m1: Marshaller[A, B], m2: Marshaller[A, B], m3: Marshaller[A, B]): Marshaller[A, B] = { - fromScala(marshalling.Marshaller.oneOf(m1.asScala, m2.asScala, m3.asScala)) - } - - /** - * Helper for creating a "super-marshaller" from a number of "sub-marshallers". - * Content-negotiation determines, which "sub-marshaller" eventually gets to do the job. - */ - def oneOf[A, B](m1: Marshaller[A, B], m2: Marshaller[A, B], m3: Marshaller[A, B], m4: Marshaller[A, B]): Marshaller[A, B] = { - fromScala(marshalling.Marshaller.oneOf(m1.asScala, m2.asScala, m3.asScala, m4.asScala)) - } - - /** - * Helper for creating a "super-marshaller" from a number of "sub-marshallers". - * Content-negotiation determines, which "sub-marshaller" eventually gets to do the job. - */ - def oneOf[A, B](m1: Marshaller[A, B], m2: Marshaller[A, B], m3: Marshaller[A, B], m4: Marshaller[A, B], m5: Marshaller[A, B]): Marshaller[A, B] = { - fromScala(marshalling.Marshaller.oneOf(m1.asScala, m2.asScala, m3.asScala, m4.asScala, m5.asScala)) - } - - /** - * Helper for creating a synchronous [[Marshaller]] to content with a fixed charset from the given function. - */ - def withFixedContentType[A, B](contentType: ContentType, f: java.util.function.Function[A, B]): Marshaller[A, B] = - fromScala(marshalling.Marshaller.withFixedContentType(contentType.asScala)(f.apply)) - - /** - * Helper for creating a synchronous [[Marshaller]] to content with a negotiable charset from the given function. - */ - def withOpenCharset[A, B](mediaType: MediaType.WithOpenCharset, f: java.util.function.BiFunction[A, HttpCharset, B]): Marshaller[A, B] = - fromScala(marshalling.Marshaller.withOpenCharset(mediaType.asScala)(f.apply)) - - /** - * Helper for creating a synchronous [[Marshaller]] to non-negotiable content from the given function. - */ - def opaque[A, B](f: function.Function[A, B]): Marshaller[A, B] = - fromScala(scaladsl.marshalling.Marshaller.opaque[A, B] { a ⇒ f.apply(a) }) - - implicit def asScalaToResponseMarshaller[T](m: Marshaller[T, akka.http.javadsl.model.HttpResponse]): ToResponseMarshaller[T] = - m.asScala.map(_.asScala) - - implicit def asScalaEntityMarshaller[T](m: Marshaller[T, akka.http.javadsl.model.RequestEntity]): akka.http.scaladsl.marshalling.Marshaller[T, akka.http.scaladsl.model.RequestEntity] = - m.asScala.map(_.asScala) -} - -class Marshaller[-A, +B] private (implicit val asScala: marshalling.Marshaller[A, B]) { - import Marshaller.fromScala - - /** INTERNAL API: involves unsafe cast (however is very fast) */ - // TODO would be nice to not need this special case - private[akka] def asScalaCastOutput[C]: marshalling.Marshaller[A, C] = asScala.asInstanceOf[marshalling.Marshaller[A, C]] - - def map[C](f: function.Function[B @uncheckedVariance, C]): Marshaller[A, C] = fromScala(asScala.map(f.apply)) - - def compose[C](f: function.Function[C, A @uncheckedVariance]): Marshaller[C, B] = fromScala(asScala.compose(f.apply)) -} diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/Directives.scala b/akka-http/src/main/scala/akka/http/javadsl/server/Directives.scala deleted file mode 100644 index 2f966a1a7e..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/Directives.scala +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server - -import akka.http.impl.util.JavaMapping -import akka.http.javadsl.server.directives.{ FramedEntityStreamingDirectives, TimeoutDirectives } - -import scala.annotation.varargs - -abstract class AllDirectives extends FramedEntityStreamingDirectives - -/** - * INTERNAL API - */ -object Directives extends AllDirectives { - import JavaMapping.Implicits._ - import RoutingJavaMapping._ - - // These are repeated here since sometimes (?) the Scala compiler won't actually generate java-compatible - // signatures for varargs methods, making them show up as Seq instead of T... in Java. - - @varargs override def route(alternatives: Route*): Route = - super.route(alternatives: _*) - - @varargs override def getFromBrowseableDirectories(directories: String*): Route = - super.getFromBrowseableDirectories(directories: _*) -} diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/ExceptionHandler.scala b/akka-http/src/main/scala/akka/http/javadsl/server/ExceptionHandler.scala deleted file mode 100644 index 105bcaf226..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/ExceptionHandler.scala +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server - -import akka.http.scaladsl.server -import akka.japi.pf.PFBuilder -import akka.http.javadsl.settings.RoutingSettings -import akka.http.impl.util.JavaMapping.Implicits._ -import RoutingJavaMapping._ - -object ExceptionHandler { - /** - * Creates a new builder DSL for creating an ExceptionHandler - */ - def newBuilder: ExceptionHandlerBuilder = new ExceptionHandlerBuilder() - - /** INTERNAL API */ - def of(pf: PartialFunction[Throwable, Route]) = new ExceptionHandler(server.ExceptionHandler(pf.andThen(_.delegate))) -} - -/** - * Handles exceptions by turning them into routes. You can create an exception handler in Java code like the following example: - *
- *     ExceptionHandler myHandler = ExceptionHandler.of (ExceptionHandler.newPFBuilder()
- *         .match(IllegalArgumentException.class, x -> Directives.complete(StatusCodes.BAD_REQUEST))
- *         .build()
- *     ));
- * 
- */ -final class ExceptionHandler private (val asScala: server.ExceptionHandler) { - /** - * Creates a new [[ExceptionHandler]] which uses the given one as fallback for this one. - */ - def withFallback(that: ExceptionHandler): ExceptionHandler = new ExceptionHandler(asScala.withFallback(that.asScala)) - - /** - * "Seals" this handler by attaching a default handler as fallback if necessary. - */ - def seal(settings: RoutingSettings): ExceptionHandler = new ExceptionHandler(asScala.seal(settings.asScala)) -} diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/PathMatchers.scala b/akka-http/src/main/scala/akka/http/javadsl/server/PathMatchers.scala deleted file mode 100644 index ec3d933799..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/PathMatchers.scala +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.javadsl.server - -import java.util.UUID -import java.util.regex.Pattern - -import akka.http.scaladsl.model.Uri.Path - -import scala.collection.JavaConverters._ - -import akka.http.scaladsl.server.{ PathMatcher ⇒ SPathMatcher } -import akka.http.scaladsl.server.{ PathMatchers ⇒ SPathMatchers } -import akka.http.javadsl.common.RegexConverters.toScala - -final class PathMatchers - -object PathMatchers { - import JavaPathMatchers._ - - private[this] val IntegerSegment: PathMatcher1[java.lang.Integer] = fromScala1(SPathMatchers.IntNumber.map { i ⇒ i: java.lang.Integer }) - private[this] val LongSegment: PathMatcher1[java.lang.Long] = fromScala1(SPathMatchers.LongNumber.map { i ⇒ i: java.lang.Long }) - private[this] val HexIntegerSegment: PathMatcher1[java.lang.Integer] = fromScala1(SPathMatchers.HexIntNumber.map { i ⇒ i: java.lang.Integer }) - private[this] val HexLongSegment: PathMatcher1[java.lang.Long] = fromScala1(SPathMatchers.HexLongNumber.map { i ⇒ i: java.lang.Long }) - private[this] val DoubleSegment: PathMatcher1[java.lang.Double] = fromScala1(SPathMatchers.DoubleNumber.map { i ⇒ i: java.lang.Double }) - private[this] val UUIDSegment: PathMatcher1[UUID] = fromScala1(SPathMatchers.JavaUUID) - - private[this] val Neutral = fromScala0(SPathMatchers.Neutral) - private[this] val Slash = new PathMatcher0(SPathMatchers.Slash) - private[this] val PathEnd = new PathMatcher0(SPathMatchers.PathEnd) - private[this] val Remaining = new PathMatcher1[String](SPathMatchers.Remaining) - private[this] val RemainingPath = new PathMatcher1[Path](SPathMatchers.RemainingPath) - private[this] val Segment = new PathMatcher1[String](SPathMatchers.Segment) - private[this] val Segments = new PathMatcher1[java.util.List[String]](SPathMatchers.Segments.map(_.asJava)) - - /** - * Converts a path string containing slashes into a PathMatcher that interprets slashes as - * path segment separators. - */ - def separateOnSlashes(segments: String): PathMatcher0 = fromScala0(SPathMatchers.separateOnSlashes(segments)) - - /** - * A PathMatcher that matches a single slash character ('/'). - */ - def slash(): PathMatcher0 = Slash - - /** - * Creates a PathMatcher that consumes (a prefix of) the first path segment - * (if the path begins with a segment). - */ - def segment(segment: String): PathMatcher0 = new PathMatcher0(SPathMatcher(segment)) - - /** - * Creates a PathMatcher that consumes (a prefix of) the first path segment - * if the path begins with a segment (a prefix of) which matches the given regex. - * Extracts either the complete match (if the regex doesn't contain a capture group) or - * the capture group (if the regex contains exactly one). - * If the regex contains more than one capture group the method throws an IllegalArgumentException. - */ - def segment(regex: Pattern): PathMatcher1[String] = new PathMatcher1[String](SPathMatcher[Tuple1[String]](toScala(regex))) - - /** - * A PathMatcher that matches between `min` and `max` (both inclusively) path segments (separated by slashes) - * as a List[String]. If there are more than `count` segments present the remaining ones will be left unmatched. - * If the path has a trailing slash this slash will *not* be matched. - */ - def segments(min: Int, max: Int): PathMatcher1[java.util.List[String]] = new PathMatcher1[java.util.List[String]](SPathMatchers.Segments(min, max).map(_.asJava)) - - /** - * A PathMatcher that matches the given number of path segments (separated by slashes) as a List[String]. - * If there are more than `count` segments present the remaining ones will be left unmatched. - * If the path has a trailing slash this slash will *not* be matched. - */ - def segments(count: Int) = new PathMatcher1[java.util.List[String]](SPathMatchers.Segments(count).map(_.asJava)) - - /** - * A PathMatcher that efficiently matches a number of digits and extracts their (non-negative) Int value. - * The matcher will not match 0 digits or a sequence of digits that would represent an Int value larger - * than Int.MaxValue. - */ - def integerSegment: PathMatcher1[java.lang.Integer] = IntegerSegment - - /** - * A PathMatcher that efficiently matches a number of digits and extracts their (non-negative) Long value. - * The matcher will not match 0 digits or a sequence of digits that would represent an Long value larger - * than Long.MaxValue. - */ - def longSegment: PathMatcher1[java.lang.Long] = LongSegment - - /** - * A PathMatcher that efficiently matches a number of hex-digits and extracts their (non-negative) Int value. - * The matcher will not match 0 digits or a sequence of digits that would represent an Int value larger - * than Int.MaxValue. - */ - def hexIntegerSegment: PathMatcher1[java.lang.Integer] = HexIntegerSegment - - /** - * A PathMatcher that efficiently matches a number of hex-digits and extracts their (non-negative) Long value. - * The matcher will not match 0 digits or a sequence of digits that would represent an Long value larger - * than Long.MaxValue. - */ - def hexLongSegment: PathMatcher1[java.lang.Long] = HexLongSegment - - /** - * A PathMatcher that matches and extracts a Double value. The matched string representation is the pure decimal, - * optionally signed form of a double value, i.e. without exponent. - */ - def doubleSegment: PathMatcher1[java.lang.Double] = DoubleSegment - - /** - * A PathMatcher that matches and extracts a java.util.UUID instance. - */ - def uuidSegment: PathMatcher1[UUID] = UUIDSegment - - /** - * A PathMatcher that always matches, doesn't consume anything and extracts nothing. - * Serves mainly as a neutral element in PathMatcher composition. - */ - def neutral: PathMatcher0 = Neutral - - /** - * A PathMatcher that matches the very end of the requests URI path. - */ - def pathEnd: PathMatcher0 = PathEnd - - /** - * A PathMatcher that matches and extracts the complete remaining, - * unmatched part of the request's URI path as an (encoded!) String. - */ - def remaining: PathMatcher1[String] = Remaining - - /** - * A PathMatcher that matches and extracts the complete remaining, - * unmatched part of the request's URI path. - */ - def remainingPath: PathMatcher1[Path] = RemainingPath - - /** - * A PathMatcher that matches if the unmatched path starts with a path segment. - * If so the path segment is extracted as a String. - */ - def segment: PathMatcher1[String] = Segment - - /** - * A PathMatcher that matches up to 128 remaining segments as a List[String]. - * This can also be no segments resulting in the empty list. - * If the path has a trailing slash this slash will *not* be matched. - */ - def segments: PathMatcher1[java.util.List[String]] = Segments - -} - diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/RejectionHandler.scala b/akka-http/src/main/scala/akka/http/javadsl/server/RejectionHandler.scala deleted file mode 100644 index 727f9e3529..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/RejectionHandler.scala +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.javadsl.server - -import akka.http.scaladsl.server -import java.util.function -import scala.reflect.ClassTag -import scala.collection.JavaConverters._ - -object RejectionHandler { - /** - * Creates a new [[RejectionHandler]] builder. - */ - def newBuilder = new RejectionHandlerBuilder(server.RejectionHandler.newBuilder) - - def defaultHandler = new RejectionHandler(server.RejectionHandler.default) -} - -final class RejectionHandler(val asScala: server.RejectionHandler) { - /** - * Creates a new [[RejectionHandler]] which uses the given one as fallback for this one. - */ - def withFallback(fallback: RejectionHandler) = new RejectionHandler(asScala.withFallback(fallback.asScala)) - - /** - * "Seals" this handler by attaching a default handler as fallback if necessary. - */ - def seal = new RejectionHandler(asScala.seal) -} - -class RejectionHandlerBuilder(asScala: server.RejectionHandler.Builder) { - def build = new RejectionHandler(asScala.result()) - - /** - * Handles a single [[Rejection]] with the given partial function. - */ - def handle[T <: Rejection](t: Class[T], handler: function.Function[T, Route]): RejectionHandlerBuilder = { - asScala.handle { case r if t.isInstance(r) ⇒ handler.apply(t.cast(r)).delegate } - this - } - - /** - * Handles several Rejections of the same type at the same time. - * The list passed to the given function is guaranteed to be non-empty. - */ - def handleAll[T <: Rejection](t: Class[T], handler: function.Function[java.util.List[T], Route]): RejectionHandlerBuilder = { - asScala.handleAll { rejections: collection.immutable.Seq[T] ⇒ handler.apply(rejections.asJava).delegate }(ClassTag(t)) - this - } - - /** - * Handles the special "not found" case using the given [[Route]]. - */ - def handleNotFound(route: Route): RejectionHandlerBuilder = { - asScala.handleNotFound(route.delegate) - this - } - - /** - * Convenience method for handling rejections created by created by the onCompleteWithBreaker directive. - * Signals that the request was rejected because the supplied circuit breaker is open and requests are failing fast. - * - * Use to customise the error response being written instead of the default [[akka.http.javadsl.model.StatusCodes.SERVICE_UNAVAILABLE]] response. - */ - def handleCircuitBreakerOpenRejection(handler: function.Function[CircuitBreakerOpenRejection, Route]): RejectionHandlerBuilder = { - asScala.handleCircuitBreakerOpenRejection(t ⇒ handler.apply(t).delegate) - this - } -} diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/Rejections.scala b/akka-http/src/main/scala/akka/http/javadsl/server/Rejections.scala deleted file mode 100644 index 089aa25a31..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/Rejections.scala +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.javadsl.server - -import akka.http.impl.util.JavaMapping -import akka.http.scaladsl.server.ContentNegotiator.Alternative -import akka.http.scaladsl.server._ -import akka.http.javadsl.model._ -import akka.http.javadsl.model.headers.{ ByteRange, HttpEncoding, HttpChallenge } -import java.util.Optional -import java.util.function.{ Function ⇒ JFunction } -import java.lang.{ Iterable ⇒ JIterable } - -import akka.http.scaladsl -import akka.japi.Util -import akka.pattern.CircuitBreakerOpenException - -import scala.compat.java8.OptionConverters._ -import scala.collection.immutable -import scala.collection.JavaConverters._ - -/** - * A rejection encapsulates a specific reason why a Route was not able to handle a request. Rejections are gathered - * up over the course of a Route evaluation and finally converted to [[akka.http.scaladsl.model.HttpResponse]]s by the - * `handleRejections` directive, if there was no way for the request to be completed. - * - * If providing custom rejections, extend [[CustomRejection]] instead. - */ -trait Rejection - -/** To be extended by user-provided custom rejections, such that they may be consumed in either Java or Scala DSLs. */ -trait CustomRejection extends akka.http.scaladsl.server.Rejection - -/** - * Rejection created by method filters. - * Signals that the request was rejected because the HTTP method is unsupported. - */ -trait MethodRejection extends Rejection { - def supported: HttpMethod -} - -/** - * Rejection created by scheme filters. - * Signals that the request was rejected because the Uri scheme is unsupported. - */ -trait SchemeRejection extends Rejection { - def supported: String -} - -/** - * Rejection created by parameter filters. - * Signals that the request was rejected because a query parameter was not found. - */ -trait MissingQueryParamRejection extends Rejection { - def parameterName: String -} - -/** - * Rejection created by parameter filters. - * Signals that the request was rejected because a query parameter could not be interpreted. - */ -trait MalformedQueryParamRejection extends Rejection { - def parameterName: String - def errorMsg: String - def getCause: Optional[Throwable] -} - -/** - * Rejection created by form field filters. - * Signals that the request was rejected because a form field was not found. - */ -trait MissingFormFieldRejection extends Rejection { - def fieldName: String -} - -/** - * Rejection created by form field filters. - * Signals that the request was rejected because a form field could not be interpreted. - */ -trait MalformedFormFieldRejection extends Rejection { - def fieldName: String - def errorMsg: String - def getCause: Optional[Throwable] -} - -/** - * Rejection created by header directives. - * Signals that the request was rejected because a required header could not be found. - */ -trait MissingHeaderRejection extends Rejection { - def headerName: String -} - -/** - * Rejection created by header directives. - * Signals that the request was rejected because a header value is malformed. - */ -trait MalformedHeaderRejection extends Rejection { - def headerName: String - def errorMsg: String - def getCause: Optional[Throwable] -} - -/** - * Rejection created by [[akka.http.scaladsl.server.directives.HeaderDirectives.checkSameOrigin]]. - * Signals that the request was rejected because `Origin` header value is invalid. - */ -trait InvalidOriginRejection extends Rejection { - def getAllowedOrigins: java.util.List[akka.http.javadsl.model.headers.HttpOrigin] -} - -/** - * Rejection created by unmarshallers. - * Signals that the request was rejected because the requests content-type is unsupported. - */ -trait UnsupportedRequestContentTypeRejection extends Rejection { - def getSupported: java.util.Set[akka.http.javadsl.model.ContentTypeRange] -} - -/** - * Rejection created by decoding filters. - * Signals that the request was rejected because the requests content encoding is unsupported. - */ -trait UnsupportedRequestEncodingRejection extends Rejection { - def supported: HttpEncoding -} - -/** - * Rejection created by range directives. - * Signals that the request was rejected because the requests contains only unsatisfiable ByteRanges. - * The actualEntityLength gives the client a hint to create satisfiable ByteRanges. - */ -trait UnsatisfiableRangeRejection extends Rejection { - def getUnsatisfiableRanges: JIterable[ByteRange] - def actualEntityLength: Long -} - -/** - * Rejection created by range directives. - * Signals that the request contains too many ranges. An irregular high number of ranges - * indicates a broken client or a denial of service attack. - */ -trait TooManyRangesRejection extends Rejection { - def maxRanges: Int -} - -/** - * Rejection created by unmarshallers. - * Signals that the request was rejected because unmarshalling failed with an error that wasn't - * an `IllegalArgumentException`. Usually that means that the request content was not of the expected format. - * Note that semantic issues with the request content (e.g. because some parameter was out of range) - * will usually trigger a `ValidationRejection` instead. - */ -trait MalformedRequestContentRejection extends Rejection { - def message: String - def getCause: Throwable -} - -/** - * Rejection created by unmarshallers. - * Signals that the request was rejected because an message body entity was expected but not supplied. - */ -abstract class RequestEntityExpectedRejection extends Rejection -object RequestEntityExpectedRejection { - def get: RequestEntityExpectedRejection = scaladsl.server.RequestEntityExpectedRejection -} - -/** - * Rejection created by marshallers. - * Signals that the request was rejected because the service is not capable of producing a response entity whose - * content type is accepted by the client - */ -trait UnacceptedResponseContentTypeRejection extends Rejection { - def supported: immutable.Set[ContentNegotiator.Alternative] -} - -/** - * Rejection created by encoding filters. - * Signals that the request was rejected because the service is not capable of producing a response entity whose - * content encoding is accepted by the client - */ -trait UnacceptedResponseEncodingRejection extends Rejection { - def getSupported: java.util.Set[HttpEncoding] -} -object UnacceptedResponseEncodingRejection { - def create(supported: HttpEncoding): UnacceptedResponseEncodingRejection = - scaladsl.server.UnacceptedResponseEncodingRejection(JavaMapping.toScala(supported)) -} - -/** - * Rejection created by the various [[akka.http.javadsl.server.directives.SecurityDirectives]]. - * Signals that the request was rejected because the user could not be authenticated. The reason for the rejection is - * specified in the cause. - */ -trait AuthenticationFailedRejection extends Rejection { - def cause: AuthenticationFailedRejection.Cause - def challenge: HttpChallenge -} - -object AuthenticationFailedRejection { - /** - * Signals the cause of the failed authentication. - */ - trait Cause - - /** - * Signals the cause of the rejecting was that the user could not be authenticated, because the `WWW-Authenticate` - * header was not supplied. - */ - trait CredentialsMissing extends Cause - - /** - * Signals the cause of the rejecting was that the user could not be authenticated, because the supplied credentials - * are invalid. - */ - trait CredentialsRejected extends Cause -} - -/** - * Rejection created by the 'authorize' directive. - * Signals that the request was rejected because the user is not authorized. - */ -trait AuthorizationFailedRejection extends Rejection -object AuthorizationFailedRejection { - def get = scaladsl.server.AuthorizationFailedRejection -} - -/** - * Rejection created by the `cookie` directive. - * Signals that the request was rejected because a cookie was not found. - */ -trait MissingCookieRejection extends Rejection { - def cookieName: String -} - -/** - * Rejection created when a websocket request was expected but none was found. - */ -trait ExpectedWebSocketRequestRejection extends Rejection -object ExpectedWebSocketRequestRejection { - def get: ExpectedWebSocketRequestRejection = scaladsl.server.ExpectedWebSocketRequestRejection -} - -/** - * Rejection created when a websocket request was not handled because none of the given subprotocols - * was supported. - */ -trait UnsupportedWebSocketSubprotocolRejection extends Rejection { - def supportedProtocol: String -} - -/** - * Rejection created by the `validation` directive as well as for `IllegalArgumentExceptions` - * thrown by domain model constructors (e.g. via `require`). - * It signals that an expected value was semantically invalid. - */ -trait ValidationRejection extends Rejection { - def message: String - def getCause: Optional[Throwable] -} - -/** - * Rejection created by the `onCompleteWithBreaker` directive. - * Signals that the request was rejected because the supplied circuit breaker is open and requests are failing fast. - */ -trait CircuitBreakerOpenRejection extends Rejection { - def cause: CircuitBreakerOpenException -} - -/** - * A special Rejection that serves as a container for a transformation function on rejections. - * It is used by some directives to "cancel" rejections that are added by later directives of a similar type. - * - * Consider this route structure for example: - * - * put { reject(ValidationRejection("no") } ~ get { ... } - * - * If this structure is applied to a PUT request the list of rejections coming back contains three elements: - * - * 1. A ValidationRejection - * 2. A MethodRejection - * 3. A TransformationRejection holding a function filtering out the MethodRejection - * - * so that in the end the RejectionHandler will only see one rejection (the ValidationRejection), because the - * MethodRejection added by the `get` directive is canceled by the `put` directive (since the HTTP method - * did indeed match eventually). - */ -trait TransformationRejection extends Rejection { - def getTransform: JFunction[JIterable[Rejection], JIterable[Rejection]] -} - -/** - * A Throwable wrapping a Rejection. - * Can be used for marshalling `Future[T]` or `Try[T]` instances, whose failure side is supposed to trigger a route - * rejection rather than an Exception that is handled by the nearest ExceptionHandler. - * (Custom marshallers can of course use it as well.) - */ -trait RejectionError extends RuntimeException { - def rejection: Rejection -} - -object Rejections { - import akka.http.scaladsl.{ model ⇒ m } - import akka.http.scaladsl.{ server ⇒ s } - import scala.language.implicitConversions - import JavaMapping.Implicits._ - import RoutingJavaMapping._ - - def method(supported: HttpMethod): MethodRejection = - s.MethodRejection(JavaMapping.toScala(supported)) - - def scheme(supported: String): SchemeRejection = - s.SchemeRejection(supported) - - def missingQueryParam(parameterName: String): MissingQueryParamRejection = - s.MissingQueryParamRejection(parameterName) - - def malformedQueryParam(parameterName: String, errorMsg: String): MalformedQueryParamRejection = - s.MalformedQueryParamRejection(parameterName, errorMsg) - def malformedQueryParam(parameterName: String, errorMsg: String, cause: Optional[Throwable]): MalformedQueryParamRejection = - s.MalformedQueryParamRejection(parameterName, errorMsg, cause.asScala) - - def missingFormField(fieldName: String): MissingFormFieldRejection = - s.MissingFormFieldRejection(fieldName) - - def malformedFormField(fieldName: String, errorMsg: String): MalformedFormFieldRejection = - s.MalformedFormFieldRejection(fieldName, errorMsg) - def malformedFormField(fieldName: String, errorMsg: String, cause: Optional[Throwable]): s.MalformedFormFieldRejection = - s.MalformedFormFieldRejection(fieldName, errorMsg, cause.asScala) - - def missingHeader(headerName: String): MissingHeaderRejection = - s.MissingHeaderRejection(headerName) - - def malformedHeader(headerName: String, errorMsg: String): MalformedHeaderRejection = - s.MalformedHeaderRejection(headerName, errorMsg) - def malformedHeader(headerName: String, errorMsg: String, cause: Optional[Throwable]): s.MalformedHeaderRejection = - s.MalformedHeaderRejection(headerName, errorMsg, cause.asScala) - - def unsupportedRequestContentType(supported: java.lang.Iterable[MediaType]): UnsupportedRequestContentTypeRejection = - s.UnsupportedRequestContentTypeRejection(supported.asScala.map(m ⇒ scaladsl.model.ContentTypeRange(m.asScala)).toSet) - - def unsupportedRequestEncoding(supported: HttpEncoding): UnsupportedRequestEncodingRejection = - s.UnsupportedRequestEncodingRejection(supported.asScala) - - def unsatisfiableRange(unsatisfiableRanges: java.lang.Iterable[ByteRange], actualEntityLength: Long) = - UnsatisfiableRangeRejection(Util.immutableSeq(unsatisfiableRanges).map(_.asScala), actualEntityLength) - - def tooManyRanges(maxRanges: Int) = TooManyRangesRejection(maxRanges) - - def malformedRequestContent(message: String, cause: Throwable) = - MalformedRequestContentRejection(message, cause) - - def requestEntityExpected = RequestEntityExpectedRejection - - def unacceptedResponseContentType( - supportedContentTypes: java.lang.Iterable[ContentType], - supportedMediaTypes: java.lang.Iterable[MediaType]): UnacceptedResponseContentTypeRejection = { - val s1: Set[Alternative] = supportedContentTypes.asScala.map(_.asScala).map(ct ⇒ ContentNegotiator.Alternative(ct)).toSet - val s2: Set[Alternative] = supportedMediaTypes.asScala.map(_.asScala).map(mt ⇒ ContentNegotiator.Alternative(mt)).toSet - s.UnacceptedResponseContentTypeRejection(s1 ++ s2) - } - - def unacceptedResponseEncoding(supported: HttpEncoding) = - s.UnacceptedResponseEncodingRejection(supported.asScala) - def unacceptedResponseEncoding(supported: java.lang.Iterable[HttpEncoding]) = - s.UnacceptedResponseEncodingRejection(supported.asScala.map(_.asScala).toSet) - - def authenticationCredentialsMissing(challenge: HttpChallenge): AuthenticationFailedRejection = - s.AuthenticationFailedRejection(s.AuthenticationFailedRejection.CredentialsMissing, challenge.asScala) - def authenticationCredentialsRejected(challenge: HttpChallenge): AuthenticationFailedRejection = - s.AuthenticationFailedRejection(s.AuthenticationFailedRejection.CredentialsRejected, challenge.asScala) - - def authorizationFailed = - s.AuthorizationFailedRejection - - def missingCookie(cookieName: String) = - s.MissingCookieRejection(cookieName) - - def expectedWebSocketRequest = - s.ExpectedWebSocketRequestRejection - - def validationRejection(message: String) = - s.ValidationRejection(message) - def validationRejection(message: String, cause: Optional[Throwable]) = - s.ValidationRejection(message, cause.asScala) - - def transformationRejection(f: java.util.function.Function[java.util.List[Rejection], java.util.List[Rejection]]) = - s.TransformationRejection(rejections ⇒ f.apply(rejections.map(_.asJava).asJava).asScala.toVector.map(_.asScala)) // TODO this is maddness - - def rejectionError(rejection: Rejection) = - s.RejectionError(convertToScala(rejection)) -} diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/RequestContext.scala b/akka-http/src/main/scala/akka/http/javadsl/server/RequestContext.scala deleted file mode 100644 index 9484b922e4..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/RequestContext.scala +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server - -import akka.http.javadsl.marshalling.Marshaller -import akka.http.javadsl.model.HttpRequest -import akka.http.scaladsl.util.FastFuture._ - -import scala.concurrent.ExecutionContextExecutor -import akka.stream.Materializer -import akka.event.LoggingAdapter -import akka.http.javadsl.settings.RoutingSettings -import akka.http.javadsl.settings.ParserSettings -import akka.http.javadsl.model.HttpResponse -import akka.http.javadsl.model.StatusCode -import akka.http.javadsl.model.Uri -import akka.http.javadsl.model.headers.Location -import java.util.concurrent.CompletionStage -import java.util.function.{ Function ⇒ JFunction } - -import akka.http.scaladsl -import akka.http.impl.util.JavaMapping.Implicits._ -import akka.http.scaladsl.marshalling.ToResponseMarshallable - -import scala.compat.java8.FutureConverters._ -import scala.annotation.varargs -import akka.http.scaladsl.model.Uri.Path - -import akka.http.scaladsl.marshalling.PredefinedToResponseMarshallers - -class RequestContext private (val delegate: scaladsl.server.RequestContext) { - import RequestContext._ - import RoutingJavaMapping._ - - def getRequest: HttpRequest = delegate.request - def getUnmatchedPath: String = delegate.unmatchedPath.toString() - def getExecutionContext: ExecutionContextExecutor = delegate.executionContext - def getMaterializer: Materializer = delegate.materializer - def getLog: LoggingAdapter = delegate.log - def getSettings: RoutingSettings = delegate.settings - def getParserSettings: ParserSettings = delegate.parserSettings - - def reconfigure( - executionContext: ExecutionContextExecutor, - materializer: Materializer, - log: LoggingAdapter, - settings: RoutingSettings): RequestContext = wrap(delegate.reconfigure(executionContext, materializer, log, settings.asScala)) - - def complete[T](value: T, marshaller: Marshaller[T, HttpResponse]): CompletionStage[RouteResult] = { - delegate.complete(ToResponseMarshallable(value)(marshaller)) - .fast.map(r ⇒ r: RouteResult)(akka.dispatch.ExecutionContexts.sameThreadExecutionContext).toJava - } - - def completeWith(response: HttpResponse): CompletionStage[RouteResult] = { - delegate.complete(response.asScala) - .fast.map(r ⇒ r: RouteResult)(akka.dispatch.ExecutionContexts.sameThreadExecutionContext).toJava - } - - @varargs def reject(rejections: Rejection*): CompletionStage[RouteResult] = { - val scalaRejections = rejections.map(_.asScala) - delegate.reject(scalaRejections: _*) - .fast.map(r ⇒ r: RouteResult)(akka.dispatch.ExecutionContexts.sameThreadExecutionContext).toJava - } - - def redirect(uri: Uri, redirectionType: StatusCode): CompletionStage[RouteResult] = { - completeWith(HttpResponse.create().withStatus(redirectionType).addHeader(Location.create(uri))) - } - - def fail(error: Throwable): CompletionStage[RouteResult] = - delegate.fail(error) - .fast.map(r ⇒ r: RouteResult)(akka.dispatch.ExecutionContexts.sameThreadExecutionContext).toJava - - def withRequest(req: HttpRequest): RequestContext = wrap(delegate.withRequest(req.asScala)) - def withExecutionContext(ec: ExecutionContextExecutor): RequestContext = wrap(delegate.withExecutionContext(ec)) - def withMaterializer(materializer: Materializer): RequestContext = wrap(delegate.withMaterializer(materializer)) - def withLog(log: LoggingAdapter): RequestContext = wrap(delegate.withLog(log)) - def withRoutingSettings(settings: RoutingSettings): RequestContext = wrap(delegate.withRoutingSettings(settings.asScala)) - def withParserSettings(settings: ParserSettings): RequestContext = wrap(delegate.withParserSettings(settings.asScala)) - - def mapRequest(f: JFunction[HttpRequest, HttpRequest]): RequestContext = wrap(delegate.mapRequest(r ⇒ f.apply(r.asJava).asScala)) - def withUnmatchedPath(path: String): RequestContext = wrap(delegate.withUnmatchedPath(Path(path))) - def mapUnmatchedPath(f: JFunction[String, String]): RequestContext = wrap(delegate.mapUnmatchedPath(p ⇒ Path(f.apply(p.toString())))) - def withAcceptAll: RequestContext = wrap(delegate.withAcceptAll) -} - -object RequestContext { - /** INTERNAL API */ - private[http] def wrap(delegate: scaladsl.server.RequestContext) = new RequestContext(delegate) -} diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/Route.scala b/akka-http/src/main/scala/akka/http/javadsl/server/Route.scala deleted file mode 100644 index e16cc0a2f0..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/Route.scala +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server - -import akka.stream.javadsl.Flow -import akka.http.javadsl.model.HttpRequest -import akka.http.javadsl.model.HttpResponse -import akka.http.scaladsl -import akka.actor.ActorSystem -import akka.stream.Materializer -import akka.NotUsed -import akka.http.javadsl.settings.{ ParserSettings, RoutingSettings } - -/** - * In the Java DSL, a Route can only consist of combinations of the built-in directives. A Route can not be - * instantiated directly. - * - * However, the built-in directives may be combined methods like: - * - *
- * Route myDirective(String test, Supplier inner) {
- *   return
- *     path("fixed", () ->
- *       path(test),
- *         inner
- *       )
- *     );
- * }
- * 
- * - * The above example will invoke [inner] whenever the path "fixed/{test}" is matched, where "{test}" - * is the actual String that was given as method argument. - */ -trait Route { - /** INTERNAL API */ - private[http] def delegate: scaladsl.server.Route - - def flow(system: ActorSystem, materializer: Materializer): Flow[HttpRequest, HttpResponse, NotUsed] - - /** - * "Seals" a route by wrapping it with default exception handling and rejection conversion. - */ - def seal(system: ActorSystem, materializer: Materializer): Route - - /** - * "Seals" a route by wrapping it with explicit exception handling and rejection conversion. - */ - def seal( - routingSettings: RoutingSettings, - parserSettings: ParserSettings, - rejectionHandler: RejectionHandler, - exceptionHandler: ExceptionHandler, - system: ActorSystem, - materializer: Materializer): Route - - def orElse(alternative: Route): Route -} diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/RouteResult.scala b/akka-http/src/main/scala/akka/http/javadsl/server/RouteResult.scala deleted file mode 100644 index 0985375a2e..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/RouteResult.scala +++ /dev/null @@ -1,31 +0,0 @@ -package akka.http.javadsl.server - -import akka.http.javadsl.model.HttpResponse - -trait RouteResult {} - -trait Complete extends RouteResult { - def getResponse: HttpResponse -} - -trait Rejected extends RouteResult { - def getRejections: java.lang.Iterable[Rejection] -} - -object RouteResults { - import akka.http.scaladsl.{ server ⇒ s } - import akka.japi.Util - import scala.language.implicitConversions - import akka.http.impl.util.JavaMapping - import JavaMapping.Implicits._ - import RoutingJavaMapping._ - - def complete(response: HttpResponse): Complete = { - s.RouteResult.Complete(JavaMapping.toScala(response)) - } - - def rejected(rejections: java.lang.Iterable[Rejection]): Rejected = { - s.RouteResult.Rejected(Util.immutableSeq(rejections).map(_.asScala)) - } - -} diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/RoutingJavaMapping.scala b/akka-http/src/main/scala/akka/http/javadsl/server/RoutingJavaMapping.scala deleted file mode 100644 index ebb6eeb313..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/RoutingJavaMapping.scala +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server - -import java.util.concurrent.CompletionStage - -import akka.http.impl.util.JavaMapping._ -import akka.http.impl.util._ -import akka.http.javadsl.common.EntityStreamingSupport -import akka.http.{ javadsl, scaladsl } -import akka.http.scaladsl.server.{ directives ⇒ sdirectives } -import akka.http.scaladsl.{ common ⇒ scommon } -import akka.http.javadsl.server.{ directives ⇒ jdirectives } -import akka.http.javadsl.{ common ⇒ jcommon } - -import scala.collection.immutable - -/** - * INTERNAL API - */ -private[http] object RoutingJavaMapping { - - object Implicits { - import scala.language.implicitConversions - - implicit def convertToScala[J](j: J)(implicit mapping: J2SMapping[J]): mapping.S = mapping.toScala(j) - implicit def convertSeqToScala[J](j: Seq[J])(implicit mapping: J2SMapping[J]): immutable.Seq[mapping.S] = - j.map(mapping.toScala(_)).toList - - implicit def AddAsScala[J](javaObject: J)(implicit mapping: J2SMapping[J]): AsScala[mapping.S] = new AsScala[mapping.S] { - def asScala = convertToScala(javaObject) - } - implicit def AddAsJava[S](scalaObject: S)(implicit mapping: S2JMapping[S]): AsJava[mapping.J] = new AsJava[mapping.J] { - def asJava = mapping.toJava(scalaObject) - } - } - - implicit object Rejection extends Inherited[javadsl.server.Rejection, scaladsl.server.Rejection] - - implicit object RequestContext extends JavaMapping[javadsl.server.RequestContext, scaladsl.server.RequestContext] { - // TODO make it inhierit - // extends Inherited[javadsl.server.RequestContext, scaladsl.server.RequestContext] - override def toScala(javaObject: javadsl.server.RequestContext): scaladsl.server.RequestContext = javaObject.delegate - override def toJava(scalaObject: scaladsl.server.RequestContext): javadsl.server.RequestContext = javadsl.server.RequestContext.wrap(scalaObject) - } - implicit object convertRouteResult extends Inherited[javadsl.server.RouteResult, scaladsl.server.RouteResult] - - implicit object convertEntityStreamingSupport extends Inherited[EntityStreamingSupport, scommon.EntityStreamingSupport] - - implicit object convertDirectoryRenderer extends Inherited[jdirectives.DirectoryRenderer, sdirectives.FileAndResourceDirectives.DirectoryRenderer] - implicit object convertContentTypeResolver extends Inherited[jdirectives.ContentTypeResolver, sdirectives.ContentTypeResolver] - implicit object convertDirectoryListing extends Inherited[jdirectives.DirectoryListing, sdirectives.DirectoryListing] - - // implicit object javaToScalaMediaType extends Inherited[javadsl.model.MediaType, scaladsl.model.MediaType] - // implicit object javaToScalaContentType extends Inherited[javadsl.model.ContentType, scaladsl.model.ContentType] - // implicit object javaToScalaHttpMethod extends Inherited[javadsl.model.HttpMethod, scaladsl.model.HttpMethod] - // implicit object javaToScalaHttpRequest extends Inherited[javadsl.model.HttpRequest, scaladsl.model.HttpRequest] - // implicit object javaToScalaRequestEntity extends Inherited[javadsl.model.RequestEntity, scaladsl.model.RequestEntity] - // implicit object javaToScalaHttpResponse extends Inherited[javadsl.model.HttpResponse, scaladsl.model.HttpResponse] - // implicit object javaToScalaStatusCode extends Inherited[javadsl.model.StatusCode, scaladsl.model.StatusCode] - // implicit object javaToScalaHttpEncoding extends Inherited[javadsl.model.headers.HttpEncoding, scaladsl.model.headers.HttpEncoding] - // implicit object javaToScalaByteRange extends Inherited[javadsl.model.headers.ByteRange, scaladsl.model.headers.ByteRange] - // implicit object javaToScalaHttpChallenge extends Inherited[javadsl.model.headers.HttpChallenge, scaladsl.model.headers.HttpChallenge] - // implicit object javaToScalaHttpHeader extends Inherited[javadsl.model.HttpHeader, scaladsl.model.HttpHeader] - // implicit object javaToScalaLanguage extends Inherited[javadsl.model.headers.Language, scaladsl.model.headers.Language] - // implicit object javaToScalaHttpCookiePair extends Inherited[javadsl.model.headers.HttpCookiePair, scaladsl.model.headers.HttpCookiePair] - // implicit object javaToScalaHttpCookie extends Inherited[javadsl.model.headers.HttpCookie, scaladsl.model.headers.HttpCookie] - // implicit object javaToScalaHttpCredentials extends Inherited[javadsl.model.headers.HttpCredentials, scaladsl.model.headers.HttpCredentials] - // implicit object javaToScalaMessage extends Inherited[javadsl.model.ws.Message, scaladsl.model.ws.Message] - // implicit object javaToScalaEntityTag extends Inherited[javadsl.model.headers.EntityTag, scaladsl.model.headers.EntityTag] - // implicit object javaToScalaDateTime extends Inherited[javadsl.model.DateTime, scaladsl.model.DateTime] - implicit object convertRouteSettings extends Inherited[javadsl.settings.RoutingSettings, scaladsl.settings.RoutingSettings] - implicit object convertParserSettings extends Inherited[javadsl.settings.ParserSettings, scaladsl.settings.ParserSettings] - implicit object convertLogEntry extends Inherited[javadsl.server.directives.LogEntry, scaladsl.server.directives.LogEntry] - - // // not made implicit since these are subtypes of RequestEntity - // val javaToScalaHttpEntity extends Inherited[javadsl.model.HttpEntity, scaladsl.model.HttpEntity] - // val javaToScalaResponseEntity extends Inherited[javadsl.model.ResponseEntity, scaladsl.model.ResponseEntity] - - implicit final class ConvertCompletionStage[T](val stage: CompletionStage[T]) extends AnyVal { - import scala.compat.java8.FutureConverters._ - def asScala = stage.toScala - } -} - diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/directives/BasicDirectives.scala b/akka-http/src/main/scala/akka/http/javadsl/server/directives/BasicDirectives.scala deleted file mode 100644 index 8b61671039..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/directives/BasicDirectives.scala +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server.directives - -import java.util.function.{ Function ⇒ JFunction } - -import akka.actor.ActorSystem -import akka.http.impl.util.JavaMapping -import akka.http.javadsl.settings.ParserSettings -import akka.http.javadsl.settings.RoutingSettings -import akka.japi.Util -import akka.stream.javadsl.Source -import akka.util.ByteString - -import scala.concurrent.ExecutionContextExecutor -import akka.http.impl.model.JavaUri -import akka.http.javadsl.model.HttpRequest -import akka.http.javadsl.model.HttpEntity -import akka.http.javadsl.model.RequestEntity -import akka.http.javadsl.model.Uri -import akka.http.javadsl.server._ -import akka.http.scaladsl.server.{ Directives ⇒ D } -import akka.http.scaladsl -import akka.stream.Materializer -import java.util.function.Supplier -import java.util.{ List ⇒ JList } - -import akka.http.javadsl.model.HttpResponse -import akka.http.javadsl.model.ResponseEntity -import akka.http.javadsl.model.HttpHeader -import akka.http.scaladsl.util.FastFuture._ -import java.lang.{ Iterable ⇒ JIterable } -import java.util.concurrent.CompletionStage -import java.util.function.Predicate - -import akka.dispatch.ExecutionContexts -import akka.event.LoggingAdapter -import akka.http.javadsl.server - -import scala.compat.java8.FutureConverters._ -import scala.concurrent.duration.FiniteDuration - -abstract class BasicDirectives { - import akka.http.impl.util.JavaMapping.Implicits._ - import RoutingJavaMapping._ - - def mapRequest(f: JFunction[HttpRequest, HttpRequest], inner: Supplier[Route]): Route = RouteAdapter { - D.mapRequest(rq ⇒ f.apply(rq.asJava).asScala) { inner.get.delegate } - } - - def mapRequestContext(f: JFunction[RequestContext, RequestContext], inner: Supplier[Route]): Route = RouteAdapter { - D.mapRequestContext(rq ⇒ f.apply(RequestContext.toJava(rq)).asScala) { inner.get.delegate } - } - - def mapRejections(f: JFunction[JList[Rejection], JList[Rejection]], inner: Supplier[Route]): Route = RouteAdapter { - D.mapRejections(rejections ⇒ Util.immutableSeq(f.apply(Util.javaArrayList(rejections.map(_.asJava)))).map(_.asScala)) { inner.get.delegate } - } - - def mapResponse(f: JFunction[HttpResponse, HttpResponse], inner: Supplier[Route]): Route = RouteAdapter { - D.mapResponse(resp ⇒ f.apply(resp.asJava).asScala) { inner.get.delegate } - } - - def mapResponseEntity(f: JFunction[ResponseEntity, ResponseEntity], inner: Supplier[Route]): Route = RouteAdapter { - D.mapResponseEntity(e ⇒ f.apply(e.asJava).asScala) { inner.get.delegate } - } - - def mapResponseHeaders(f: JFunction[JList[HttpHeader], JList[HttpHeader]], inner: Supplier[Route]): Route = RouteAdapter { - D.mapResponseHeaders(l ⇒ Util.immutableSeq(f.apply(Util.javaArrayList(l))).map(_.asScala)) { inner.get.delegate } // TODO try to remove map() - } - - def mapInnerRoute(f: JFunction[Route, Route], inner: Supplier[Route]): Route = RouteAdapter { - D.mapInnerRoute(route ⇒ f(RouteAdapter(route)).delegate) { inner.get.delegate } - } - - def mapRouteResult(f: JFunction[RouteResult, RouteResult], inner: Supplier[Route]): Route = RouteAdapter { - D.mapRouteResult(route ⇒ f(route.asJava).asScala) { inner.get.delegate } - } - - def mapRouteResultPF(f: PartialFunction[RouteResult, RouteResult], inner: Supplier[Route]): Route = RouteAdapter { - D.mapRouteResult(route ⇒ f(route.asJava).asScala) { inner.get.delegate } - } - - def mapRouteResultFuture(f: JFunction[CompletionStage[RouteResult], CompletionStage[RouteResult]], inner: Supplier[Route]): Route = RouteAdapter { - D.mapRouteResultFuture(stage ⇒ - f(toJava(stage.fast.map(_.asJava)(ExecutionContexts.sameThreadExecutionContext))).toScala.fast.map(_.asScala)(ExecutionContexts.sameThreadExecutionContext)) { - inner.get.delegate - } - } - - def mapRouteResultWith(f: JFunction[RouteResult, CompletionStage[RouteResult]], inner: Supplier[Route]): Route = RouteAdapter { - D.mapRouteResultWith(r ⇒ f(r.asJava).toScala.fast.map(_.asScala)(ExecutionContexts.sameThreadExecutionContext)) { inner.get.delegate } - } - - def mapRouteResultWithPF(f: PartialFunction[RouteResult, CompletionStage[RouteResult]], inner: Supplier[Route]): Route = RouteAdapter { - D.mapRouteResultWith(r ⇒ f(r.asJava).toScala.fast.map(_.asScala)(ExecutionContexts.sameThreadExecutionContext)) { inner.get.delegate } - } - - /** - * Runs the inner route with settings mapped by the given function. - */ - def mapSettings(f: JFunction[RoutingSettings, RoutingSettings], inner: Supplier[Route]): Route = RouteAdapter { - D.mapSettings(rs ⇒ f(rs.asJava).asScala) { inner.get.delegate } - } - - /** - * Always passes the request on to its inner route - * (i.e. does nothing with the request or the response). - */ - def pass(inner: Supplier[Route]): Route = RouteAdapter { - D.pass { inner.get.delegate } - } - - /** - * Injects the given value into a directive. - */ - def provide[T](t: T, inner: JFunction[T, Route]): Route = RouteAdapter { - D.provide(t) { t ⇒ inner.apply(t).delegate } - } - - /** - * Adds a TransformationRejection cancelling all rejections equal to the given one - * to the list of rejections potentially coming back from the inner route. - */ - def cancelRejection(rejection: Rejection, inner: Supplier[Route]): Route = RouteAdapter { - D.cancelRejection(rejection.asScala) { inner.get.delegate } - } - - /** - * Adds a TransformationRejection cancelling all rejections of one of the given classes - * to the list of rejections potentially coming back from the inner route. - */ - def cancelRejections(classes: JIterable[Class[_]], inner: Supplier[Route]): Route = RouteAdapter { - D.cancelRejections(Util.immutableSeq(classes): _*) { inner.get.delegate } - } - - /** - * Adds a TransformationRejection cancelling all rejections for which the given filter function returns true - * to the list of rejections potentially coming back from the inner route. - */ - def cancelRejections(filter: Predicate[Rejection], inner: Supplier[Route]): Route = RouteAdapter { - D.cancelRejections(r ⇒ filter.test(r)) { inner.get.delegate } - } - - def recoverRejections(f: JFunction[JIterable[Rejection], RouteResult], inner: Supplier[Route]): Route = RouteAdapter { - D.recoverRejections(rs ⇒ f.apply(Util.javaArrayList(rs.map(_.asJava))).asScala) { inner.get.delegate } - } - - def recoverRejectionsWith(f: JFunction[JIterable[Rejection], CompletionStage[RouteResult]], inner: Supplier[Route]): Route = RouteAdapter { - D.recoverRejectionsWith(rs ⇒ f.apply(Util.javaArrayList(rs.map(_.asJava))).toScala.fast.map(_.asScala)(ExecutionContexts.sameThreadExecutionContext)) { inner.get.delegate } - } - - /** - * Transforms the unmatchedPath of the RequestContext using the given function. - */ - def mapUnmatchedPath(f: JFunction[String, String], inner: Supplier[Route]): Route = RouteAdapter { - D.mapUnmatchedPath(path ⇒ scaladsl.model.Uri.Path(f.apply(path.toString))) { inner.get.delegate } - } - - /** - * Extracts the yet unmatched path from the RequestContext. - */ - def extractUnmatchedPath(inner: JFunction[String, Route]) = RouteAdapter { - D.extractUnmatchedPath { path ⇒ - inner.apply(path.toString).delegate - } - } - - /** - * Extracts the current [[HttpRequest]] instance. - */ - def extractRequest(inner: JFunction[HttpRequest, Route]) = RouteAdapter { - D.extractRequest { rq ⇒ - inner.apply(rq).delegate - } - } - - /** - * Extracts the complete request URI. - */ - def extractUri(inner: JFunction[Uri, Route]) = RouteAdapter { - D.extractUri { uri ⇒ - inner.apply(JavaUri(uri)).delegate - } - } - - /** - * Extracts the current http request entity. - */ - @CorrespondsTo("extract") - def extractEntity(inner: JFunction[RequestEntity, Route]): Route = RouteAdapter { - D.extractRequest { rq ⇒ - inner.apply(rq.entity).delegate - } - } - - /** - * Extracts the [[Materializer]] from the [[RequestContext]]. - */ - def extractMaterializer(inner: JFunction[Materializer, Route]): Route = RouteAdapter( - D.extractMaterializer { m ⇒ inner.apply(m).delegate }) - - /** - * Extracts the [[akka.actor.ActorSystem]] if the available Materializer is an [[akka.stream.ActorMaterializer]]. - * Otherwise throws an exception as it won't be able to extract the system from arbitrary materializers. - */ - def extractActorSystem(inner: JFunction[ActorSystem, Route]): Route = RouteAdapter( - D.extractActorSystem { system ⇒ inner.apply(system).delegate }) - - /** - * Extracts the [[ExecutionContextExecutor]] from the [[RequestContext]]. - */ - def extractExecutionContext(inner: JFunction[ExecutionContextExecutor, Route]): Route = RouteAdapter( - D.extractExecutionContext { c ⇒ inner.apply(c).delegate }) - - /** - * Extracts a single value using the given function. - */ - def extract[T](extract: JFunction[RequestContext, T], inner: JFunction[T, Route]): Route = RouteAdapter { - D.extract(sc ⇒ extract.apply(JavaMapping.toJava(sc)(server.RoutingJavaMapping.RequestContext))) { c ⇒ inner.apply(c).delegate } - } - - /** - * Runs its inner route with the given alternative [[LoggingAdapter]]. - */ - def withLog(log: LoggingAdapter, inner: Supplier[Route]): Route = RouteAdapter { - D.withLog(log) { inner.get.delegate } - } - - /** - * Runs its inner route with the given alternative [[scala.concurrent.ExecutionContextExecutor]]. - */ - def withExecutionContext(ec: ExecutionContextExecutor, inner: Supplier[Route]): Route = RouteAdapter { - D.withExecutionContext(ec) { inner.get.delegate } - } - - /** - * Runs its inner route with the given alternative [[akka.stream.Materializer]]. - */ - def withMaterializer(mat: Materializer, inner: Supplier[Route]): Route = RouteAdapter { - D.withMaterializer(mat) { inner.get.delegate } - } - - /** - * Runs its inner route with the given alternative [[RoutingSettings]]. - */ - def withSettings(s: RoutingSettings, inner: Supplier[Route]): Route = RouteAdapter { - D.withSettings(s.asScala) { inner.get.delegate } - } - - /** - * Extracts the [[LoggingAdapter]] - */ - def extractLog(inner: JFunction[LoggingAdapter, Route]): Route = RouteAdapter { - D.extractLog { log ⇒ inner.apply(log).delegate } - } - - /** - * Extracts the [[akka.http.javadsl.settings.ParserSettings]] from the [[akka.http.javadsl.server.RequestContext]]. - */ - def extractParserSettings(inner: JFunction[ParserSettings, Route]) = RouteAdapter { - D.extractParserSettings { settings ⇒ - inner.apply(settings).delegate - } - } - - /** - * Extracts the [[RoutingSettings]] from the [[akka.http.javadsl.server.RequestContext]]. - */ - def extractSettings(inner: JFunction[RoutingSettings, Route]) = RouteAdapter { - D.extractSettings { settings ⇒ - inner.apply(settings).delegate - } - } - - /** - * Extracts the [[akka.http.javadsl.server.RequestContext]] itself. - */ - def extractRequestContext(inner: JFunction[RequestContext, Route]) = RouteAdapter { - D.extractRequestContext { ctx ⇒ inner.apply(JavaMapping.toJava(ctx)(server.RoutingJavaMapping.RequestContext)).delegate } - } - - /** - * Extracts the entities `dataBytes` [[akka.stream.javadsl.Source]] from the [[akka.http.javadsl.server.RequestContext]]. - */ - def extractDataBytes(inner: JFunction[Source[ByteString, Any], Route]) = RouteAdapter { - D.extractRequest { ctx ⇒ inner.apply(ctx.entity.dataBytes.asJava).delegate } - } - - /** - * Extracts the [[akka.http.javadsl.model.RequestEntity]] from the [[akka.http.javadsl.server.RequestContext]]. - */ - def extractRequestEntity(inner: JFunction[RequestEntity, Route]): Route = extractEntity(inner) - - /** - * WARNING: This will read the entire request entity into memory regardless of size and effectively disable streaming. - * - * Converts the HttpEntity from the [[akka.http.javadsl.server.RequestContext]] into an - * [[akka.http.javadsl.model.HttpEntity.Strict]] and extracts it, or fails the route if unable to drain the - * entire request body within the timeout. - * - * @param timeout The directive is failed if the stream isn't completed after the given timeout. - */ - def extractStrictEntity(timeout: FiniteDuration, inner: JFunction[HttpEntity.Strict, Route]): Route = RouteAdapter { - D.extractStrictEntity(timeout) { strict ⇒ inner.apply(strict).delegate } - } - - /** - * WARNING: This will read the entire request entity into memory regardless of size and effectively disable streaming. - * - * Extracts the [[akka.http.javadsl.server.RequestContext]] itself with the strict HTTP entity, - * or fails the route if unable to drain the entire request body within the timeout. - * - * @param timeout The directive is failed if the stream isn't completed after the given timeout. - */ - def toStrictEntity(timeout: FiniteDuration, inner: Supplier[Route]): Route = RouteAdapter { - D.toStrictEntity(timeout) { inner.get.delegate } - } - -} diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/directives/CacheConditionDirectives.scala b/akka-http/src/main/scala/akka/http/javadsl/server/directives/CacheConditionDirectives.scala deleted file mode 100644 index ca92b4013e..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/directives/CacheConditionDirectives.scala +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server -package directives - -import java.util.Optional -import java.util.function.Supplier - -import scala.compat.java8.OptionConverters._ - -import akka.http.javadsl.model.DateTime -import akka.http.javadsl.model.headers.EntityTag -import akka.http.scaladsl.server.{ Directives ⇒ D } - -abstract class CacheConditionDirectives extends BasicDirectives { - import akka.http.impl.util.JavaMapping.Implicits._ - - /** - * Wraps its inner route with support for Conditional Requests as defined - * by http://tools.ietf.org/html/rfc7232 - * - * In particular the algorithm defined by http://tools.ietf.org/html/rfc7232#section-6 - * is implemented by this directive. - * - * Note: if you want to combine this directive with `withRangeSupport(...)` you need to put - * it on the *outside* of the `withRangeSupport(...)` directive, i.e. `withRangeSupport(...)` - * must be on a deeper level in your route structure in order to function correctly. - */ - def conditional(eTag: EntityTag, inner: Supplier[Route]): Route = RouteAdapter { - D.conditional(eTag.asScala) { inner.get.delegate } - } - - /** - * Wraps its inner route with support for Conditional Requests as defined - * by http://tools.ietf.org/html/rfc7232 - * - * In particular the algorithm defined by http://tools.ietf.org/html/rfc7232#section-6 - * is implemented by this directive. - * - * Note: if you want to combine this directive with `withRangeSupport(...)` you need to put - * it on the *outside* of the `withRangeSupport(...)` directive, i.e. `withRangeSupport(...)` - * must be on a deeper level in your route structure in order to function correctly. - */ - def conditional(lastModified: DateTime, inner: Supplier[Route]): Route = RouteAdapter { - D.conditional(lastModified.asScala) { inner.get.delegate } - } - - /** - * Wraps its inner route with support for Conditional Requests as defined - * by http://tools.ietf.org/html/rfc7232 - * - * In particular the algorithm defined by http://tools.ietf.org/html/rfc7232#section-6 - * is implemented by this directive. - * - * Note: if you want to combine this directive with `withRangeSupport(...)` you need to put - * it on the *outside* of the `withRangeSupport(...)` directive, i.e. `withRangeSupport(...)` - * must be on a deeper level in your route structure in order to function correctly. - */ - def conditional(eTag: EntityTag, lastModified: DateTime, inner: Supplier[Route]): Route = RouteAdapter { - D.conditional(eTag.asScala, lastModified.asScala) { inner.get.delegate } - } - - /** - * Wraps its inner route with support for Conditional Requests as defined - * by http://tools.ietf.org/html/rfc7232 - * - * In particular the algorithm defined by http://tools.ietf.org/html/rfc7232#section-6 - * is implemented by this directive. - * - * Note: if you want to combine this directive with `withRangeSupport(...)` you need to put - * it on the *outside* of the `withRangeSupport(...)` directive, i.e. `withRangeSupport(...)` - * must be on a deeper level in your route structure in order to function correctly. - */ - def conditional(eTag: Optional[EntityTag], lastModified: Optional[DateTime], inner: Supplier[Route]): Route = RouteAdapter { - D.conditional(eTag.asScala.map(_.asScala), lastModified.asScala.map(_.asScala)) { inner.get.delegate } - } - -} diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/directives/CodingDirectives.scala b/akka-http/src/main/scala/akka/http/javadsl/server/directives/CodingDirectives.scala deleted file mode 100644 index 86236c7647..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/directives/CodingDirectives.scala +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server -package directives - -import java.util.function.Supplier - -import scala.collection.JavaConverters._ -import akka.http.impl.util.JavaMapping.Implicits._ -import RoutingJavaMapping._ -import akka.http.javadsl.coding.Coder -import akka.http.javadsl.model.headers.HttpEncoding -import akka.http.javadsl.server.Route -import akka.http.scaladsl.server.{ Directives ⇒ D } - -abstract class CodingDirectives extends CacheConditionDirectives { - /** - * Rejects the request with an UnacceptedResponseEncodingRejection - * if the given response encoding is not accepted by the client. - */ - def responseEncodingAccepted(encoding: HttpEncoding, inner: Supplier[Route]): Route = RouteAdapter { - D.responseEncodingAccepted(encoding.asScala) { - inner.get.delegate - } - } - - /** - * Encodes the response with the encoding that is requested by the client via the `Accept- - * Encoding` header. The response encoding is determined by the rules specified in - * http://tools.ietf.org/html/rfc7231#section-5.3.4. - * - * If the `Accept-Encoding` header is missing or empty or specifies an encoding other than - * identity, gzip or deflate then no encoding is used. - */ - def encodeResponse(inner: Supplier[Route]): Route = RouteAdapter { - D.encodeResponse { - inner.get.delegate - } - } - - /** - * Encodes the response with the encoding that is requested by the client via the `Accept- - * Encoding` header. The response encoding is determined by the rules specified in - * http://tools.ietf.org/html/rfc7231#section-5.3.4. - * - * If the `Accept-Encoding` header is missing then the response is encoded using the `first` - * encoder. - * - * If the `Accept-Encoding` header is empty and `NoCoding` is part of the encoders then no - * response encoding is used. Otherwise the request is rejected. - * - * If [encoders] is empty, no encoding is performed. - */ - def encodeResponseWith(coders: java.lang.Iterable[Coder], inner: Supplier[Route]): Route = RouteAdapter { - coders.asScala.toList match { - case head :: tail ⇒ - D.encodeResponseWith(head._underlyingScalaCoder, tail.toSeq.map(_._underlyingScalaCoder): _*) { - inner.get.delegate - } - case _ ⇒ - inner.get.delegate - } - } - - /** - * Decodes the incoming request using the given Decoder. - * If the request encoding doesn't match the request is rejected with an `UnsupportedRequestEncodingRejection`. - */ - def decodeRequestWith(coder: Coder, inner: Supplier[Route]): Route = RouteAdapter { - D.decodeRequestWith(coder._underlyingScalaCoder) { - inner.get.delegate - } - } - - /** - * Rejects the request with an UnsupportedRequestEncodingRejection if its encoding doesn't match the given one. - */ - def requestEncodedWith(encoding: HttpEncoding, inner: Supplier[Route]): Route = RouteAdapter { - D.requestEncodedWith(encoding.asScala) { - inner.get.delegate - } - } - - /** - * Decodes the incoming request if it is encoded with one of the given - * encoders. If the request encoding doesn't match one of the given encoders - * the request is rejected with an `UnsupportedRequestEncodingRejection`. - * If no decoders are given the default encoders (`Gzip`, `Deflate`, `NoCoding`) are used. - */ - def decodeRequestWith(coders: java.lang.Iterable[Coder], inner: Supplier[Route]): Route = RouteAdapter { - D.decodeRequestWith(coders.asScala.map(_._underlyingScalaCoder).toSeq: _*) { - inner.get.delegate - } - } - - /** - * Decompresses the incoming request if it is `gzip` or `deflate` compressed. - * Uncompressed requests are passed through untouched. - * If the request encoded with another encoding the request is rejected with an `UnsupportedRequestEncodingRejection`. - */ - def decodeRequest(inner: Supplier[Route]): Route = RouteAdapter { - D.decodeRequest { - inner.get.delegate - } - } - - /** - * Inspects the response entity and adds a `Content-Encoding: gzip` response header if - * the entities media-type is precompressed with gzip and no `Content-Encoding` header is present yet. - */ - def withPrecompressedMediaTypeSupport(inner: Supplier[Route]): Route = RouteAdapter { - D.withPrecompressedMediaTypeSupport { - inner.get.delegate - } - } -} - diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/directives/CookieDirectives.scala b/akka-http/src/main/scala/akka/http/javadsl/server/directives/CookieDirectives.scala deleted file mode 100644 index dc5088b3a1..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/directives/CookieDirectives.scala +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server.directives - -import java.lang.{ Iterable ⇒ JIterable } -import java.util.Optional -import java.util.function.{ Function ⇒ JFunction } -import java.util.function.Supplier - -import scala.collection.JavaConverters._ -import akka.http.impl.util.JavaMapping.Implicits._ - -import akka.http.javadsl.model.headers.HttpCookie -import akka.http.javadsl.model.headers.HttpCookiePair -import akka.http.javadsl.server.Route -import akka.http.scaladsl -import akka.http.scaladsl.server.{ Directives ⇒ D } - -abstract class CookieDirectives extends CodingDirectives { - /** - * Extracts the [[HttpCookiePair]] with the given name. If the cookie is not present the - * request is rejected with a respective [[akka.http.javadsl.server.MissingCookieRejection]]. - */ - def cookie(name: String, inner: JFunction[HttpCookiePair, Route]): Route = RouteAdapter { - D.cookie(name) { c ⇒ inner.apply(c).delegate } - } - - /** - * Extracts the [[HttpCookiePair]] with the given name as an `Option[HttpCookiePair]`. - * If the cookie is not present a value of `None` is extracted. - */ - def optionalCookie(name: String, inner: JFunction[Optional[HttpCookiePair], Route]): Route = RouteAdapter { - D.optionalCookie(name) { c ⇒ inner.apply(c.asJava).delegate } - } - - /** - * Adds a [[Set-Cookie]] response header with the given cookie. - */ - def setCookie(cookie: HttpCookie, inner: Supplier[Route]): Route = RouteAdapter { - D.setCookie(cookie.asScala) { inner.get.delegate } - } - - /** - * Adds a [[Set-Cookie]] response header with the given cookies. - */ - def setCookie(cookies: JIterable[HttpCookie], inner: Supplier[Route]): Route = RouteAdapter { - cookies.asScala.toList match { - case head :: tail ⇒ - D.setCookie(head.asScala, tail.map(_.asScala).toVector: _*) { - inner.get.delegate - } - case _ ⇒ - inner.get.delegate - } - } - - /** - * Adds a [[Set-Cookie]] response header expiring the given cookie. - */ - def deleteCookie(cookie: HttpCookie, inner: Supplier[Route]): Route = RouteAdapter { - D.deleteCookie(cookie.asScala) { inner.get.delegate } - } - - /** - * Adds a [[Set-Cookie]] response header expiring the given cookies. - */ - def deleteCookie(cookies: JIterable[HttpCookie], inner: Supplier[Route]): Route = RouteAdapter { - cookies.asScala.toList match { - case head :: tail ⇒ - D.deleteCookie(head.asScala, tail.map(_.asScala).toSeq: _*) { - inner.get.delegate - } - case _ ⇒ - inner.get.delegate - } - } - - /** - * Adds a [[Set-Cookie]] response header expiring the cookie with the given properties. - * - * @param name Name of the cookie to match - */ - def deleteCookie(name: String, inner: Supplier[Route]): Route = deleteCookie(name, "", "", inner) - - /** - * Adds a [[Set-Cookie]] response header expiring the cookie with the given properties. - * - * @param name Name of the cookie to match - * @param domain Domain of the cookie to match, or empty string to match any domain - */ - def deleteCookie(name: String, domain: String, inner: Supplier[Route]): Route = deleteCookie(name, domain, "", inner) - - /** - * Adds a [[Set-Cookie]] response header expiring the cookie with the given properties. - * - * @param name Name of the cookie to match - * @param domain Domain of the cookie to match, or empty string to match any domain - * @param path Path of the cookie to match, or empty string to match any path - */ - def deleteCookie(name: String, domain: String, path: String, inner: Supplier[Route]): Route = RouteAdapter { - D.deleteCookie(name, domain, path) { - inner.get.delegate - } - } - -} diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/directives/DebuggingDirectives.scala b/akka-http/src/main/scala/akka/http/javadsl/server/directives/DebuggingDirectives.scala deleted file mode 100644 index 00fce2b0bf..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/directives/DebuggingDirectives.scala +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.javadsl.server.directives - -import java.util.function.{ BiFunction, Supplier, Function ⇒ JFunction } -import java.util.{ Optional, List ⇒ JList } - -import akka.event.Logging -import akka.event.Logging.LogLevel -import akka.http.javadsl.model.{ HttpRequest, HttpResponse } -import akka.http.javadsl.server.{ Rejection, Route, RoutingJavaMapping } -import akka.http.scaladsl -import akka.http.scaladsl.server.directives.LoggingMagnet -import akka.http.scaladsl.server.{ RouteResult, Directives ⇒ D } - -import scala.collection.JavaConverters._ - -abstract class DebuggingDirectives extends CookieDirectives { - import akka.http.impl.util.JavaMapping.Implicits._ - import RoutingJavaMapping._ - - /** - * Produces a log entry for every incoming request. - */ - def logRequest(marker: String, inner: Supplier[Route]): Route = RouteAdapter { - D.logRequest(marker) { inner.get.delegate } - } - - /** - * Produces a log entry for every incoming request. - * - * @param level One of the log levels defined in akka.event.Logging - */ - def logRequest(marker: String, level: LogLevel, inner: Supplier[Route]): Route = RouteAdapter { - D.logRequest(marker) { inner.get.delegate } - } - - /** - * Produces a log entry for every incoming request. - */ - def logRequest(show: JFunction[HttpRequest, LogEntry], inner: Supplier[Route]): Route = RouteAdapter { - D.logRequest(LoggingMagnet.forMessageFromFullShow(rq ⇒ show.apply(rq).asScala)) { inner.get.delegate } - } - - /** - * Produces a log entry for every route result. - */ - def logResult(marker: String, inner: Supplier[Route]): Route = RouteAdapter { - D.logResult(marker) { inner.get.delegate } - } - - /** - * Produces a log entry for every route result. - * - * @param level One of the log levels defined in akka.event.Logging - */ - def logResult(marker: String, level: LogLevel, inner: Supplier[Route]): Route = RouteAdapter { - D.logResult(marker) { inner.get.delegate } - } - - /** - * Produces a log entry for every route result. - * - * @param showSuccess Function invoked when the route result was successful and yielded an HTTP response - * @param showRejection Function invoked when the route yielded a rejection - */ - def logResult( - showSuccess: JFunction[HttpResponse, LogEntry], - showRejection: JFunction[JList[Rejection], LogEntry], - inner: Supplier[Route]) = RouteAdapter { - D.logResult(LoggingMagnet.forMessageFromFullShow { - case RouteResult.Complete(response) ⇒ showSuccess.apply(response).asScala - case RouteResult.Rejected(rejections) ⇒ showRejection.apply(rejections.map(_.asJava).asJava).asScala - }) { - inner.get.delegate - } - } - - /** - * Produces a log entry for every request/response combination. - * - * @param showSuccess Function invoked when the route result was successful and yielded an HTTP response - * @param showRejection Function invoked when the route yielded a rejection - */ - def logRequestResult( - showSuccess: BiFunction[HttpRequest, HttpResponse, LogEntry], - showRejection: BiFunction[HttpRequest, JList[Rejection], LogEntry], - inner: Supplier[Route]) = RouteAdapter { - D.logRequestResult(LoggingMagnet.forRequestResponseFromFullShow(request ⇒ { - case RouteResult.Complete(response) ⇒ Some(showSuccess.apply(request, response).asScala) - case RouteResult.Rejected(rejections) ⇒ Some(showRejection.apply(request, rejections.map(_.asJava).asJava).asScala) - })) { - inner.get.delegate - } - } - - /** - * Optionally produces a log entry for every request/response combination. - * - * @param showSuccess Function invoked when the route result was successful and yielded an HTTP response - * @param showRejection Function invoked when the route yielded a rejection - */ - @CorrespondsTo("logRequestResult") - def logRequestResultOptional( - showSuccess: BiFunction[HttpRequest, HttpResponse, Optional[LogEntry]], - showRejection: BiFunction[HttpRequest, JList[Rejection], Optional[LogEntry]], - inner: Supplier[Route]) = RouteAdapter { - D.logRequestResult(LoggingMagnet.forRequestResponseFromFullShow(request ⇒ { - case RouteResult.Complete(response) ⇒ showSuccess.apply(request, response).asScala - case RouteResult.Rejected(rejections) ⇒ showRejection.apply(request, rejections.map(_.asJava).asJava).asScala - })) { - inner.get.delegate - } - } -} - -abstract class LogEntry { - def getObj: Any - def getLevel: LogLevel -} - -object LogEntry { - def create(obj: Any, level: LogLevel): LogEntry = scaladsl.server.directives.LogEntry(obj, level) - def debug(obj: Any): LogEntry = scaladsl.server.directives.LogEntry(obj, Logging.DebugLevel) - def info(obj: Any): LogEntry = scaladsl.server.directives.LogEntry(obj, Logging.InfoLevel) - def warning(obj: Any): LogEntry = scaladsl.server.directives.LogEntry(obj, Logging.WarningLevel) - def error(obj: Any): LogEntry = scaladsl.server.directives.LogEntry(obj, Logging.ErrorLevel) -} diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/directives/ExecutionDirectives.scala b/akka-http/src/main/scala/akka/http/javadsl/server/directives/ExecutionDirectives.scala deleted file mode 100644 index 480a1acccc..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/directives/ExecutionDirectives.scala +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.javadsl.server.directives - -import akka.http.javadsl.server.ExceptionHandler -import akka.http.javadsl.server.RejectionHandler -import akka.http.javadsl.server.Route -import akka.http.scaladsl.server.directives.{ ExecutionDirectives ⇒ D } - -abstract class ExecutionDirectives extends DebuggingDirectives { - - /** - * Transforms exceptions thrown during evaluation of its inner route using the given - * [[akka.http.javadsl.server.ExceptionHandler]]. - */ - def handleExceptions(handler: ExceptionHandler, inner: java.util.function.Supplier[Route]) = RouteAdapter( - D.handleExceptions(handler.asScala) { - inner.get.delegate - }) - - /** - * Transforms rejections produced by its inner route using the given - * [[akka.http.scaladsl.server.RejectionHandler]]. - */ - def handleRejections(handler: RejectionHandler, inner: java.util.function.Supplier[Route]) = RouteAdapter( - D.handleRejections(handler.asScala) { - inner.get.delegate - }) - -} diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/directives/FileAndResourceDirectives.scala b/akka-http/src/main/scala/akka/http/javadsl/server/directives/FileAndResourceDirectives.scala deleted file mode 100644 index 4427da1db7..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/directives/FileAndResourceDirectives.scala +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server.directives - -import java.io.File - -import akka.http.javadsl.marshalling.Marshaller - -import scala.annotation.varargs -import scala.collection.JavaConverters._ -import akka.http.javadsl.model.ContentType -import akka.http.javadsl.model.RequestEntity -import akka.http.javadsl.server.{ Route, RoutingJavaMapping } -import akka.http.scaladsl.server.{ Directives ⇒ D } - -/** - * Implement this interface to provide a custom mapping from a file name to a [[akka.http.javadsl.model.ContentType]]. - */ -trait ContentTypeResolver { - def resolve(fileName: String): ContentType -} - -abstract class DirectoryListing { - def getPath: String - def isRoot: Boolean - def getFiles: java.util.List[File] -} - -trait DirectoryRenderer { - def directoryMarshaller(renderVanityFooter: Boolean): Marshaller[DirectoryListing, RequestEntity] -} - -/** - * Directives that load files and resources. - * - * For the directives in this class, the "default classloader" is defined as the classloader that has loaded - * the akka.actor.ActorSystem class. - */ -abstract class FileAndResourceDirectives extends ExecutionDirectives { - import akka.http.impl.util.JavaMapping.Implicits._ - import RoutingJavaMapping._ - - /** - * Completes GET requests with the content of the given resource loaded from the default ClassLoader, - * using the default content type resolver. - * If the resource cannot be found or read the Route rejects the request. - */ - def getFromResource(path: String): Route = RouteAdapter { - D.getFromResource(path) - } - - /** - * Completes GET requests with the content of the given resource loaded from the default ClassLoader, - * using the given content type resolver. - * If the resource cannot be found or read the Route rejects the request. - */ - def getFromResource(path: String, resolver: ContentTypeResolver): Route = RouteAdapter { - D.getFromResource(path)(resolver.asScala) - } - - /** - * Completes GET requests with the content of the given resource loaded from the default ClassLoader, - * with the given content type. - * If the resource cannot be found or read the Route rejects the request. - */ - def getFromResource(path: String, contentType: ContentType): Route = RouteAdapter { - D.getFromResource(path, contentType.asScala) - } - - /** - * Completes GET requests with the content of the given resource loaded from the given ClassLoader, - * with the given content type. - * If the resource cannot be found or read the Route rejects the request. - */ - def getFromResource(path: String, contentType: ContentType, classLoader: ClassLoader): Route = RouteAdapter { - D.getFromResource(path, contentType.asScala, classLoader) - } - - /** - * Same as "getFromDirectory" except that the file is not fetched from the file system but rather from a - * "resource directory", using the default ClassLoader, resolving content type using the default content type - * resolver. - * - * If the requested resource is itself a directory or cannot be found or read the Route rejects the request. - */ - def getFromResourceDirectory(directoryName: String): Route = RouteAdapter { - D.getFromResourceDirectory(directoryName) - } - - /** - * Same as "getFromDirectory" except that the file is not fetched from the file system but rather from a - * "resource directory", using the given ClassLoader, resolving content type using the default content type - * resolver. - * - * If the requested resource is itself a directory or cannot be found or read the Route rejects the request. - */ - def getFromResourceDirectory(directoryName: String, classLoader: ClassLoader): Route = RouteAdapter { - D.getFromResourceDirectory(directoryName, classLoader) - } - - /** - * Same as "getFromDirectory" except that the file is not fetched from the file system but rather from a - * "resource directory", using the default ClassLoader, resolving content type using the given content type - * resolver. - * - * If the requested resource is itself a directory or cannot be found or read the Route rejects the request. - */ - def getFromResourceDirectory(directoryName: String, resolver: ContentTypeResolver): Route = RouteAdapter { - D.getFromResourceDirectory(directoryName)(resolver.asScala) - } - - /** - * Same as "getFromDirectory" except that the file is not fetched from the file system but rather from a - * "resource directory", using the given ClassLoader, resolving content type using the given content type - * resolver. - * - * If the requested resource is itself a directory or cannot be found or read the Route rejects the request. - */ - def getFromResourceDirectory(directoryName: String, resolver: ContentTypeResolver, classLoader: ClassLoader): Route = RouteAdapter { - D.getFromResourceDirectory(directoryName, classLoader)(resolver.asScala) - } - - /** - * Completes GET requests with the content of the given file, resolving the content type using the default resolver. - * If the file cannot be found or read the request is rejected. - */ - def getFromFile(file: File): Route = RouteAdapter { - D.getFromFile(file) - } - - /** - * Completes GET requests with the content of the given file, resolving the content type using the given resolver. - * If the file cannot be found or read the request is rejected. - */ - def getFromFile(file: File, resolver: ContentTypeResolver): Route = RouteAdapter { - D.getFromFile(file)(resolver.asScala) - } - - /** - * Completes GET requests with the content of the given file, using the content type. - * If the file cannot be found or read the request is rejected. - */ - def getFromFile(file: File, contentType: ContentType): Route = RouteAdapter { - D.getFromFile(file, contentType.asScala) - } - - /** - * Completes GET requests with the content of the given file, resolving the content type using the default resolver. - * If the file cannot be found or read the request is rejected. - */ - def getFromFile(file: String): Route = RouteAdapter { - D.getFromFile(file) - } - - /** - * Completes GET requests with the content of the given file, resolving the content type using the given resolver. - * If the file cannot be found or read the request is rejected. - */ - def getFromFile(file: String, resolver: ContentTypeResolver): Route = RouteAdapter { - D.getFromFile(file)(resolver.asScala) - } - - /** - * Completes GET requests with the content of a file underneath the given directory, using the default content-type resolver. - * If the file cannot be read the Route rejects the request. - */ - def getFromDirectory(directoryPath: String): Route = RouteAdapter { - D.getFromDirectory(directoryPath) - } - - /** - * Completes GET requests with the content of a file underneath the given directory, using the given content-type resolver. - * If the file cannot be read the Route rejects the request. - */ - def getFromDirectory(directoryPath: String, resolver: ContentTypeResolver): Route = RouteAdapter { - D.getFromDirectory(directoryPath)(resolver.asScala) - } - - /** - * Same as `getFromBrowseableDirectories` with only one directory. - */ - def getFromBrowseableDirectory(directory: String, renderer: DirectoryRenderer, resolver: ContentTypeResolver): Route = RouteAdapter { - D.getFromBrowseableDirectory(directory)(renderer.asScala, resolver.asScala) - } - - /** - * Same as `getFromBrowseableDirectories` with only one directory. - */ - def getFromBrowseableDirectory(directory: String, renderer: DirectoryRenderer): Route = RouteAdapter { - D.getFromBrowseableDirectory(directory)(renderer.asScala, defaultContentTypeResolver.asScala) - } - - /** - * Same as `getFromBrowseableDirectories` with only one directory. - */ - def getFromBrowseableDirectory(directory: String, resolver: ContentTypeResolver): Route = RouteAdapter { - D.getFromBrowseableDirectory(directory)(defaultDirectoryRenderer.asScala, resolver.asScala) - } - - /** - * Same as `getFromBrowseableDirectories` with only one directory. - */ - def getFromBrowseableDirectory(directory: String): Route = RouteAdapter { - D.getFromBrowseableDirectory(directory) - } - - /** - * Serves the content of the given directories as a file system browser, i.e. files are sent and directories - * served as browseable listings. - */ - def getFromBrowseableDirectories(directories: java.lang.Iterable[String], renderer: DirectoryRenderer, resolver: ContentTypeResolver): Route = RouteAdapter { - D.getFromBrowseableDirectories(directories.asScala.toSeq: _*)(renderer.asScala, resolver.asScala) - } - - /** - * Serves the content of the given directories as a file system browser, i.e. files are sent and directories - * served as browseable listings. - */ - def getFromBrowseableDirectories(directories: java.lang.Iterable[String], renderer: DirectoryRenderer): Route = RouteAdapter { - D.getFromBrowseableDirectories(directories.asScala.toSeq: _*)(renderer.asScala, defaultContentTypeResolver.asScala) - } - - /** - * Serves the content of the given directories as a file system browser, i.e. files are sent and directories - * served as browseable listings. - */ - def getFromBrowseableDirectories(directories: java.lang.Iterable[String], resolver: ContentTypeResolver): Route = RouteAdapter { - D.getFromBrowseableDirectories(directories.asScala.toSeq: _*)(defaultDirectoryRenderer.asScala, resolver.asScala) - } - - /** - * Serves the content of the given directories as a file system browser, i.e. files are sent and directories - * served as browseable listings. - */ - @varargs def getFromBrowseableDirectories(directories: String*): Route = RouteAdapter { - D.getFromBrowseableDirectories(directories: _*) - } - - /** - * Completes GET requests with a unified listing of the contents of all given directories. - * The actual rendering of the directory contents is performed by the in-scope `Marshaller[DirectoryListing]`. - */ - @varargs def listDirectoryContents(directories: String*): Route = RouteAdapter { - D.listDirectoryContents(directories: _*)(defaultDirectoryRenderer.asScala) - } - /** - * Completes GET requests with a unified listing of the contents of all given directories. - * The actual rendering of the directory contents is performed by the in-scope `Marshaller[DirectoryListing]`. - */ - @varargs def listDirectoryContents(directoryRenderer: DirectoryRenderer, directories: String*): Route = RouteAdapter { - D.listDirectoryContents(directories: _*)(directoryRenderer.asScala) - } - - /** Default [[DirectoryRenderer]] to be used with directory listing directives. */ - def defaultDirectoryRenderer: DirectoryRenderer = - akka.http.scaladsl.server.directives.FileAndResourceDirectives.DirectoryRenderer.defaultDirectoryRenderer - - /** Default [[ContentTypeResolver]]. */ - def defaultContentTypeResolver: ContentTypeResolver = - akka.http.scaladsl.server.directives.ContentTypeResolver.Default -} diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/directives/FileUploadDirectives.scala b/akka-http/src/main/scala/akka/http/javadsl/server/directives/FileUploadDirectives.scala deleted file mode 100644 index 7c656eba4d..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/directives/FileUploadDirectives.scala +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.javadsl.server.directives - -import java.io.File -import java.util.function.BiFunction - -import akka.http.impl.util.JavaMapping.Implicits._ - -import akka.http.javadsl.model.ContentType -import akka.http.javadsl.server.Route -import akka.http.scaladsl.server.{ Directives ⇒ D } -import akka.stream.javadsl.Source -import akka.util.ByteString - -abstract class FileUploadDirectives extends FileAndResourceDirectives { - /** - * Streams the bytes of the file submitted using multipart with the given file name into a temporary file on disk. - * If there is an error writing to disk the request will be failed with the thrown exception, if there is no such - * field the request will be rejected, if there are multiple file parts with the same name, the first one will be - * used and the subsequent ones ignored. - */ - def uploadedFile(fieldName: String, inner: BiFunction[FileInfo, File, Route]): Route = RouteAdapter { - D.uploadedFile(fieldName) { case (info, file) ⇒ inner.apply(info, file).delegate } - } - - /** - * Collects each body part that is a multipart file as a tuple containing metadata and a `Source` - * for streaming the file contents somewhere. If there is no such field the request will be rejected, - * if there are multiple file parts with the same name, the first one will be used and the subsequent - * ones ignored. - */ - def fileUpload(fieldName: String, inner: BiFunction[FileInfo, Source[ByteString, Any], Route]): Route = RouteAdapter { - D.fileUpload(fieldName) { case (info, src) ⇒ inner.apply(info, src.asJava).delegate } - } -} - -/** - * Additional metadata about the file being uploaded/that was uploaded using the [[FileUploadDirectives]] - */ -abstract class FileInfo { - /** - * Name of the form field the file was uploaded in - */ - def getFieldName: String - - /** - * User specified name of the uploaded file - */ - def getFileName: String - - /** - * Content type of the file - */ - def getContentType: ContentType -} \ No newline at end of file diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/directives/FormFieldDirectives.scala b/akka-http/src/main/scala/akka/http/javadsl/server/directives/FormFieldDirectives.scala deleted file mode 100644 index 9fa0506950..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/directives/FormFieldDirectives.scala +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.javadsl.server.directives - -import java.util.{ Map ⇒ JMap, List ⇒ JList } -import java.util.AbstractMap.SimpleImmutableEntry -import java.util.Optional -import java.util.function.{ Function ⇒ JFunction } - -import akka.http.javadsl.unmarshalling.Unmarshaller - -import scala.collection.JavaConverters._ -import akka.http.impl.util.JavaMapping.Implicits._ - -import akka.http.javadsl.server.Route - -import akka.http.scaladsl.server.{ Directives ⇒ D } -import akka.http.scaladsl.server.directives.ParameterDirectives._ - -import scala.compat.java8.OptionConverters - -abstract class FormFieldDirectives extends FileUploadDirectives { - - def formField(name: String, inner: JFunction[String, Route]): Route = RouteAdapter( - D.formField(name) { value ⇒ - inner.apply(value).delegate - }) - - @CorrespondsTo("formField") - def formFieldOptional(name: String, inner: JFunction[Optional[String], Route]): Route = RouteAdapter( - D.formField(name.?) { value ⇒ - inner.apply(value.asJava).delegate - }) - - @CorrespondsTo("formFieldSeq") - def formFieldList(name: String, inner: JFunction[java.util.List[String], Route]): Route = RouteAdapter( - D.formField(_string2NR(name).*) { values ⇒ - inner.apply(values.toSeq.asJava).delegate - }) - - def formField[T](t: Unmarshaller[String, T], name: String, inner: JFunction[T, Route]): Route = { - import t.asScala - RouteAdapter( - D.formField(name.as[T]) { value ⇒ - inner.apply(value).delegate - }) - } - - @CorrespondsTo("formField") - def formFieldOptional[T](t: Unmarshaller[String, T], name: String, inner: JFunction[Optional[T], Route]): Route = { - import t.asScala - RouteAdapter( - D.formField(name.as[T].?) { value ⇒ - inner.apply(OptionConverters.toJava(value)).delegate - }) - } - - @CorrespondsTo("formFieldSeq") - def formFieldList[T](t: Unmarshaller[String, T], name: String, inner: JFunction[java.util.List[T], Route]): Route = { - import t.asScala - RouteAdapter( - D.formField(name.as[T].*) { values ⇒ - inner.apply(values.toSeq.asJava).delegate - }) - } - - /** - * Extracts HTTP form fields from the request as a ``Map``. - */ - def formFieldMap(inner: JFunction[JMap[String, String], Route]): Route = RouteAdapter { - D.formFieldMap { map ⇒ inner.apply(map.asJava).delegate } - } - - /** - * Extracts HTTP form fields from the request as a ``Map>``. - */ - def formFieldMultiMap(inner: JFunction[JMap[String, JList[String]], Route]): Route = RouteAdapter { - D.formFieldMultiMap { map ⇒ inner.apply(map.mapValues { l ⇒ l.asJava }.asJava).delegate } - } - - /** - * Extracts HTTP form fields from the request as a ``Map.Entry>``. - */ - @CorrespondsTo("formFieldSeq") - def formFieldList(inner: JFunction[JList[JMap.Entry[String, String]], Route]): Route = RouteAdapter { - D.formFieldSeq { list ⇒ - val entries: Seq[JMap.Entry[String, String]] = list.map { e ⇒ new SimpleImmutableEntry(e._1, e._2) } - inner.apply(entries.asJava).delegate - } - } - -} diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/directives/FramedEntityStreamingDirectives.scala b/akka-http/src/main/scala/akka/http/javadsl/server/directives/FramedEntityStreamingDirectives.scala deleted file mode 100644 index eef4a3d1f9..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/directives/FramedEntityStreamingDirectives.scala +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2009-2015 Typesafe Inc. - */ -package akka.http.javadsl.server.directives - -import java.util.function.{ Function ⇒ JFunction } -import java.util.{ List ⇒ JList, Map ⇒ JMap } - -import akka.NotUsed -import akka.http.javadsl.common.EntityStreamingSupport -import akka.http.javadsl.marshalling.Marshaller -import akka.http.javadsl.model.{ HttpEntity, _ } -import akka.http.javadsl.server.Route -import akka.http.javadsl.unmarshalling.Unmarshaller -import akka.http.scaladsl.marshalling.{ Marshalling, ToByteStringMarshaller, ToResponseMarshallable } -import akka.http.scaladsl.server.{ Directives ⇒ D } -import akka.stream.javadsl.Source -import akka.util.ByteString - -/** EXPERIMENTAL API */ -abstract class FramedEntityStreamingDirectives extends TimeoutDirectives { - - import akka.http.javadsl.server.RoutingJavaMapping._ - import akka.http.javadsl.server.RoutingJavaMapping.Implicits._ - - @CorrespondsTo("asSourceOf") - def entityAsSourceOf[T](um: Unmarshaller[ByteString, T], support: EntityStreamingSupport, - inner: java.util.function.Function[Source[T, NotUsed], Route]): Route = RouteAdapter { - val umm = D.asSourceOf(um.asScala, support.asScala) - D.entity(umm) { s: akka.stream.scaladsl.Source[T, NotUsed] ⇒ - inner(s.asJava).delegate - } - } - - // implicits and multiple parameter lists used internally, Java caller does not benefit or use it - @CorrespondsTo("complete") - def completeWithSource[T, M](source: Source[T, M])(implicit m: Marshaller[T, ByteString], support: EntityStreamingSupport): Route = RouteAdapter { - import akka.http.scaladsl.marshalling.PredefinedToResponseMarshallers._ - val mm = m.map(ByteStringAsEntityFn).asScalaCastOutput[akka.http.scaladsl.model.RequestEntity] - val mmm = fromEntityStreamingSupportAndEntityMarshaller[T, M](support.asScala, mm) - val response = ToResponseMarshallable(source.asScala)(mmm) - D.complete(response) - } - - // implicits and multiple parameter lists used internally, Java caller does not benefit or use it - @CorrespondsTo("complete") - def completeOKWithSource[T, M](source: Source[T, M])(implicit m: Marshaller[T, RequestEntity], support: EntityStreamingSupport): Route = RouteAdapter { - import akka.http.scaladsl.marshalling.PredefinedToResponseMarshallers._ - // don't try this at home: - val mm = m.asScalaCastOutput[akka.http.scaladsl.model.RequestEntity].map(_.httpEntity.asInstanceOf[akka.http.scaladsl.model.RequestEntity]) - implicit val mmm = fromEntityStreamingSupportAndEntityMarshaller[T, M](support.asScala, mm) - val response = ToResponseMarshallable(source.asScala) - D.complete(response) - } - - private[this] val ByteStringAsEntityFn = new java.util.function.Function[ByteString, HttpEntity]() { - override def apply(bs: ByteString): HttpEntity = HttpEntities.create(bs) - } -} - -object FramedEntityStreamingDirectives extends FramedEntityStreamingDirectives diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/directives/FutureDirectives.scala b/akka-http/src/main/scala/akka/http/javadsl/server/directives/FutureDirectives.scala deleted file mode 100644 index f89ac51563..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/directives/FutureDirectives.scala +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.javadsl.server.directives - -import java.util.concurrent.CompletionException -import java.util.concurrent.CompletionStage -import java.util.function.{ Function ⇒ JFunction } -import java.util.function.Supplier - -import akka.http.javadsl.marshalling.Marshaller -import akka.http.javadsl.model.RequestEntity - -import scala.compat.java8.FutureConverters._ -import scala.concurrent.ExecutionContext.Implicits.global -import scala.util.Try -import akka.http.javadsl.server.Route -import akka.http.scaladsl.server.directives.{ CompleteOrRecoverWithMagnet, FutureDirectives ⇒ D } -import akka.pattern.CircuitBreaker - -abstract class FutureDirectives extends FormFieldDirectives { - - /** - * "Unwraps" a `CompletionStage` and runs the inner route after future - * completion with the future's value as an extraction of type `Try`. - * - * @group future - */ - def onComplete[T](f: Supplier[CompletionStage[T]], inner: JFunction[Try[T], Route]) = RouteAdapter { - D.onComplete(f.get.toScala.recover(unwrapCompletionException)) { value ⇒ - inner(value).delegate - } - } - - /** - * "Unwraps" a `CompletionStage` and runs the inner route after future - * completion with the future's value as an extraction of type `Try`. - * - * @group future - */ - def onComplete[T](cs: CompletionStage[T], inner: JFunction[Try[T], Route]) = RouteAdapter { - D.onComplete(cs.toScala.recover(unwrapCompletionException)) { value ⇒ - inner(value).delegate - } - } - - /** - * "Unwraps" a `CompletionStage[T]` and runs the inner route after future - * completion with the future's value as an extraction of type `T` if - * the supplied `CircuitBreaker` is closed. - * - * If the supplied [[CircuitBreaker]] is open the request is rejected - * with a [[akka.http.javadsl.server.CircuitBreakerOpenRejection]]. - * - * @group future - */ - def onCompleteWithBreaker[T](breaker: CircuitBreaker, f: Supplier[CompletionStage[T]], inner: JFunction[Try[T], Route]) = RouteAdapter { - D.onCompleteWithBreaker(breaker)(f.get.toScala.recover(unwrapCompletionException)) { value ⇒ - inner(value).delegate - } - } - - /** - * "Unwraps" a `CompletionStage` and runs the inner route after stage - * completion with the stage's value as an extraction of type `T`. - * If the stage fails its failure Throwable is bubbled up to the nearest - * ExceptionHandler. - * - * @group future - */ - def onSuccess[T](f: Supplier[CompletionStage[T]], inner: JFunction[T, Route]) = RouteAdapter { - D.onSuccess(f.get.toScala.recover(unwrapCompletionException)) { value ⇒ - inner(value).delegate - } - } - - /** - * "Unwraps" a `CompletionStage` and runs the inner route when the stage has failed - * with the stage's failure exception as an extraction of type `Throwable`. - * If the completion stage succeeds the request is completed using the values marshaller - * (This directive therefore requires a marshaller for the completion stage value type to be - * provided.) - * - * @group future - */ - def completeOrRecoverWith[T](f: Supplier[CompletionStage[T]], marshaller: Marshaller[T, RequestEntity], inner: JFunction[Throwable, Route]): Route = RouteAdapter { - val magnet = CompleteOrRecoverWithMagnet(f.get.toScala)(Marshaller.asScalaEntityMarshaller(marshaller)) - D.completeOrRecoverWith(magnet) { ex ⇒ inner(ex).delegate } - } - - // TODO: This might need to be raised as an issue to scala-java8-compat instead. - // Right now, having this in Java: - // CompletableFuture.supplyAsync(() -> { throw new IllegalArgumentException("always failing"); }) - // will in fact fail the future with CompletionException. - private def unwrapCompletionException[T]: PartialFunction[Throwable, T] = { - case x: CompletionException if x.getCause ne null ⇒ - throw x.getCause - } - -} diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/directives/HeaderDirectives.scala b/akka-http/src/main/scala/akka/http/javadsl/server/directives/HeaderDirectives.scala deleted file mode 100644 index 5324b77a5e..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/directives/HeaderDirectives.scala +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.javadsl.server.directives - -import java.util.Optional -import java.util.{ function ⇒ jf } - -import akka.actor.ReflectiveDynamicAccess - -import scala.compat.java8.OptionConverters -import scala.compat.java8.OptionConverters._ -import akka.http.impl.util.JavaMapping.Implicits._ -import akka.http.javadsl.model.headers.{ HttpOriginRange, HttpOriginRanges } -import akka.http.javadsl.model.{ HttpHeader, StatusCodes } -import akka.http.javadsl.server.{ InvalidOriginRejection, MissingHeaderRejection, Route } -import akka.http.scaladsl.model.headers.HttpOriginRange.Default -import akka.http.scaladsl.model.headers.{ ModeledCustomHeader, ModeledCustomHeaderCompanion, Origin } -import akka.http.scaladsl.server.directives.{ HeaderMagnet, HeaderDirectives ⇒ D } - -import scala.reflect.ClassTag -import scala.util.{ Failure, Success } - -abstract class HeaderDirectives extends FutureDirectives { - - private type ScalaHeaderMagnet = HeaderMagnet[akka.http.scaladsl.model.HttpHeader] - - /** - * Checks that request comes from the same origin. Extracts the [[Origin]] header value and verifies that - * allowed range contains the obtained value. In the case of absent of the [[Origin]] header rejects - * with [[MissingHeaderRejection]]. If the origin value is not in the allowed range - * rejects with an [[InvalidOriginRejection]] and [[StatusCodes.FORBIDDEN]] status. - * - * @group header - */ - // TODO When breaking binary compatibility this should become HttpOriginRange.Default, see https://github.com/akka/akka/pull/20776/files#r70049845 - def checkSameOrigin(allowed: HttpOriginRange, inner: jf.Supplier[Route]): Route = - allowed match { - case HttpOriginRanges.ALL | HttpOriginRange.ALL | akka.http.scaladsl.model.headers.HttpOriginRange.`*` ⇒ pass(inner) - case _ ⇒ RouteAdapter { - // safe, we know it's not the `*` header - val default = allowed.asInstanceOf[akka.http.scaladsl.model.headers.HttpOriginRange.Default] - D.checkSameOrigin(default) { inner.get().delegate } - } - } - - /** - * Extracts an HTTP header value using the given function. If the function result is undefined for all headers the - * request is rejected with an empty rejection set. If the given function throws an exception the request is rejected - * with a [[akka.http.javadsl.server.MalformedHeaderRejection]]. - */ - def headerValue[T](f: jf.Function[HttpHeader, Optional[T]], inner: jf.Function[T, Route]) = RouteAdapter { - D.headerValue(h ⇒ f.apply(h).asScala) { value ⇒ - inner.apply(value).delegate - } - } - - /** - * Extracts an HTTP header value using the given partial function. If the function is undefined for all headers the - * request is rejected with an empty rejection set. - */ - def headerValuePF[T](pf: PartialFunction[HttpHeader, T], inner: jf.Function[T, Route]) = RouteAdapter { - D.headerValuePF(pf) { value ⇒ - inner.apply(value).delegate - } - } - - /** - * Extracts the value of the first HTTP request header with the given name. - * If no header with a matching name is found the request is rejected with a [[akka.http.javadsl.server.MissingHeaderRejection]]. - */ - def headerValueByName(headerName: String, inner: jf.Function[String, Route]) = RouteAdapter { - D.headerValueByName(headerName) { value ⇒ - inner.apply(value).delegate - } - } - - /** - * Extracts the first HTTP request header of the given type. - * If no header with a matching type is found the request is rejected with a [[akka.http.javadsl.server.MissingHeaderRejection]]. - */ - def headerValueByType[T <: HttpHeader](t: Class[T], inner: jf.Function[T, Route]) = RouteAdapter { - - def magnetForModeledCustomHeader(clazz: Class[T]): HeaderMagnet[T] = { - // figure out the modeled header companion and use that to parse the header - val refl = new ReflectiveDynamicAccess(getClass.getClassLoader) - refl.getObjectFor[ModeledCustomHeaderCompanion[_]](t.getName) match { - case Success(companion) ⇒ - new HeaderMagnet[T] { - override def classTag = ClassTag(t) - override def runtimeClass = t - override def extractPF = { - case h if h.is(companion.lowercaseName) ⇒ companion.apply(h.toString).asInstanceOf[T] - } - } - case Failure(ex) ⇒ throw new RuntimeException(s"Failed to find or access the ModeledCustomHeaderCompanion for [${t.getName}]", ex) - } - } - - val magnet: HeaderMagnet[T] = - if (classOf[ModeledCustomHeader[_]].isAssignableFrom(t)) magnetForModeledCustomHeader(t) - else HeaderMagnet.fromClassNormalJavaHeader(t) - - D.headerValueByType(magnet) { value ⇒ - inner.apply(value).delegate - } - - } - - /** - * Extracts an optional HTTP header value using the given function. - * If the given function throws an exception the request is rejected - * with a [[akka.http.javadsl.server.MalformedHeaderRejection]]. - */ - def optionalHeaderValue[T](f: jf.Function[HttpHeader, Optional[T]], inner: jf.Function[Optional[T], Route]) = RouteAdapter { - D.optionalHeaderValue(h ⇒ f.apply(h).asScala) { value ⇒ - inner.apply(value.asJava).delegate - } - } - - /** - * Extracts an optional HTTP header value using the given partial function. - * If the given function throws an exception the request is rejected - * with a [[akka.http.javadsl.server.MalformedHeaderRejection]]. - */ - def optionalHeaderValuePF[T](pf: PartialFunction[HttpHeader, T], inner: jf.Function[Optional[T], Route]) = RouteAdapter { - D.optionalHeaderValuePF(pf) { value ⇒ - inner.apply(value.asJava).delegate - } - } - - /** - * Extracts the value of the optional HTTP request header with the given name. - */ - def optionalHeaderValueByName(headerName: String, inner: jf.Function[Optional[String], Route]) = RouteAdapter { - D.optionalHeaderValueByName(headerName) { value ⇒ - inner.apply(value.asJava).delegate - } - } - - /** - * FIXME: WARNING: Custom headers don't work yet with this directive! - * - * Extract the header value of the optional HTTP request header with the given type. - */ - def optionalHeaderValueByType[T <: HttpHeader](t: Class[T], inner: jf.Function[Optional[T], Route]) = RouteAdapter { - // TODO custom headers don't work yet - // TODO needs instance of check if it's a modeled header and then magically locate companion - D.optionalHeaderValueByType(HeaderMagnet.fromClassNormalJavaHeader(t).asInstanceOf[ScalaHeaderMagnet]) { value ⇒ - val valueT = value.asInstanceOf[Option[T]] // we know this is safe because T <: HttpHeader - inner.apply(OptionConverters.toJava[T](valueT)).delegate - } - } - -} diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/directives/HostDirectives.scala b/akka-http/src/main/scala/akka/http/javadsl/server/directives/HostDirectives.scala deleted file mode 100644 index f8671551ac..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/directives/HostDirectives.scala +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server -package directives - -import java.lang.{ Iterable ⇒ JIterable } -import java.util.function.{ Function ⇒ JFunction } -import java.util.function.Predicate -import java.util.function.Supplier -import java.util.regex.Pattern - -import scala.collection.JavaConverters._ - -import akka.http.javadsl.common.RegexConverters.toScala -import akka.http.scaladsl.server.{ Directives ⇒ D } - -abstract class HostDirectives extends HeaderDirectives { - /** - * Extracts the hostname part of the Host request header value. - */ - def extractHost(inner: JFunction[String, Route]): Route = RouteAdapter { - D.extractHost { host ⇒ inner.apply(host).delegate } - } - - /** - * Rejects all requests with a host name different from the given ones. - */ - def host(hostNames: JIterable[String], inner: Supplier[Route]): Route = RouteAdapter { - D.host(hostNames.asScala.toSeq: _*) { inner.get().delegate } - } - - /** - * Rejects all requests with a host name different from the given one. - */ - def host(hostName: String, inner: Supplier[Route]): Route = RouteAdapter { - D.host(hostName) { inner.get().delegate } - } - - /** - * Rejects all requests for whose host name the given predicate function returns false. - */ - def host(predicate: Predicate[String], inner: Supplier[Route]): Route = RouteAdapter { - D.host(s ⇒ predicate.test(s)) { inner.get().delegate } - } - - /** - * Rejects all requests with a host name that doesn't have a prefix matching the given regular expression. - * For all matching requests the prefix string matching the regex is extracted and passed to the inner route. - * If the regex contains a capturing group only the string matched by this group is extracted. - * If the regex contains more than one capturing group an IllegalArgumentException is thrown. - */ - def host(regex: Pattern, inner: JFunction[String, Route]): Route = RouteAdapter { - D.host(toScala(regex)) { s ⇒ inner.apply(s).delegate } - } - -} diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/directives/MarshallingDirectives.scala b/akka-http/src/main/scala/akka/http/javadsl/server/directives/MarshallingDirectives.scala deleted file mode 100644 index 9abbf3c8e4..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/directives/MarshallingDirectives.scala +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.javadsl.server.directives - -import akka.http.javadsl.model.HttpRequest -import akka.http.javadsl.model.HttpEntity -import akka.http.javadsl.server.Route -import akka.http.javadsl.unmarshalling.Unmarshaller - -import akka.http.scaladsl.server.directives.{ MarshallingDirectives ⇒ D } - -abstract class MarshallingDirectives extends HostDirectives { - /** - * Unmarshalls the request using the given unmarshaller, and passes the result to [inner]. - * If there is a problem with unmarshalling the request is rejected with the [[akka.http.javadsl.server.Rejection]] - * produced by the unmarshaller. - */ - def request[T]( - unmarshaller: Unmarshaller[_ >: HttpRequest, T], - inner: java.util.function.Function[T, Route]): Route = RouteAdapter { - D.entity(unmarshaller.asScala) { value ⇒ - inner.apply(value).delegate - } - } - - /** - * Unmarshalls the requests entity using the given unmarshaller, and passes the result to [inner]. - * If there is a problem with unmarshalling the request is rejected with the [[akka.http.javadsl.server.Rejection]] - * produced by the unmarshaller. - */ - def entity[T]( - unmarshaller: Unmarshaller[_ >: HttpEntity, T], - inner: java.util.function.Function[T, Route]): Route = RouteAdapter { - D.entity(Unmarshaller.requestToEntity.flatMap(unmarshaller).asScala) { value ⇒ - inner.apply(value).delegate - } - } - - // If you want the raw entity, use BasicDirectives.extractEntity -} diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/directives/MethodDirectives.scala b/akka-http/src/main/scala/akka/http/javadsl/server/directives/MethodDirectives.scala deleted file mode 100644 index 4c4f821c9b..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/directives/MethodDirectives.scala +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server.directives - -import java.util.function - -import akka.http.javadsl.model.HttpMethod -import akka.http.javadsl.server.{ Route, RoutingJavaMapping } -import akka.http.impl.util.JavaMapping.Implicits._ -import RoutingJavaMapping._ -import akka.http.scaladsl.server.directives.{ MethodDirectives ⇒ D } - -abstract class MethodDirectives extends MarshallingDirectives { - def delete(inner: function.Supplier[Route]): Route = RouteAdapter { - D.delete { inner.get.delegate } - } - - def get(inner: function.Supplier[Route]): Route = RouteAdapter { - D.get { inner.get.delegate } - } - - def head(inner: function.Supplier[Route]): Route = RouteAdapter { - D.head { inner.get.delegate } - } - - def options(inner: function.Supplier[Route]): Route = RouteAdapter { - D.options { inner.get.delegate } - } - def patch(inner: function.Supplier[Route]): Route = RouteAdapter { - D.patch { inner.get.delegate } - } - def post(inner: function.Supplier[Route]): Route = RouteAdapter { - D.post { inner.get.delegate } - } - def put(inner: function.Supplier[Route]): Route = RouteAdapter { - D.put { inner.get.delegate } - } - - def extractMethod(inner: function.Function[HttpMethod, Route]) = RouteAdapter { - D.extractMethod { m ⇒ - inner.apply(m).delegate - } - } - - def method(method: HttpMethod, inner: function.Supplier[Route]): Route = RouteAdapter { - D.method(method.asScala) { inner.get.delegate } - } - - /** - * Changes the HTTP method of the request to the value of the specified query string parameter. If the query string - * parameter is not specified this directive has no effect. If the query string is specified as something that is not - * a HTTP method, then this directive completes the request with a `501 Not Implemented` response. - * - * This directive is useful for: - * - Use in combination with JSONP (JSONP only supports GET) - * - Supporting older browsers that lack support for certain HTTP methods. E.g. IE8 does not support PATCH - */ - def overrideMethodWithParameter(paramName: String, inner: function.Supplier[Route]): Route = RouteAdapter { - D.overrideMethodWithParameter(paramName) { inner.get.delegate } - } - -} diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/directives/MiscDirectives.scala b/akka-http/src/main/scala/akka/http/javadsl/server/directives/MiscDirectives.scala deleted file mode 100644 index 338f738ee6..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/directives/MiscDirectives.scala +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server -package directives - -import java.lang.{ Iterable ⇒ JIterable } -import java.util.function.BooleanSupplier -import java.util.function.{ Function ⇒ JFunction } -import java.util.function.Supplier - -import scala.collection.JavaConverters._ - -import akka.http.javadsl.model.RemoteAddress -import akka.http.javadsl.model.headers.Language -import akka.http.impl.util.JavaMapping.Implicits._ -import RoutingJavaMapping._ - -import akka.http.scaladsl.server.{ Directives ⇒ D } - -abstract class MiscDirectives extends MethodDirectives { - - /** - * Checks the given condition before running its inner route. - * If the condition fails the route is rejected with a [[ValidationRejection]]. - */ - def validate(check: BooleanSupplier, errorMsg: String, inner: Supplier[Route]): Route = RouteAdapter { - D.validate(check.getAsBoolean(), errorMsg) { inner.get.delegate } - } - - /** - * Extracts the client's IP from either the X-Forwarded-For, Remote-Address or X-Real-IP header - * (in that order of priority). - */ - def extractClientIP(inner: JFunction[RemoteAddress, Route]): Route = RouteAdapter { - D.extractClientIP { ip ⇒ inner.apply(ip).delegate } - } - - /** - * Rejects if the request entity is non-empty. - */ - def requestEntityEmpty(inner: Supplier[Route]): Route = RouteAdapter { - D.requestEntityEmpty { inner.get.delegate } - } - - /** - * Rejects with a [[RequestEntityExpectedRejection]] if the request entity is empty. - * Non-empty requests are passed on unchanged to the inner route. - */ - def requestEntityPresent(inner: Supplier[Route]): Route = RouteAdapter { - D.requestEntityPresent { inner.get.delegate } - } - - /** - * Converts responses with an empty entity into (empty) rejections. - * This way you can, for example, have the marshalling of a ''None'' option - * be treated as if the request could not be matched. - */ - def rejectEmptyResponse(inner: Supplier[Route]): Route = RouteAdapter { - D.rejectEmptyResponse { inner.get.delegate } - } - - /** - * Fails the stream with [[akka.http.scaladsl.model.EntityStreamSizeException]] if its request entity size exceeds - * given limit. Limit given as parameter overrides limit configured with ``akka.http.parsing.max-content-length``. - * - * Beware that request entity size check is executed when entity is consumed. - */ - def withSizeLimit(maxBytes: Long, inner: Supplier[Route]): Route = RouteAdapter { - D.withSizeLimit(maxBytes) { inner.get.delegate } - } - - /** - * Disables the size limit (configured by `akka.http.parsing.max-content-length` by default) checking on the incoming - * [[akka.http.javadsl.model.HttpRequest]] entity. - * Can be useful when handling arbitrarily large data uploads in specific parts of your routes. - */ - def withoutSizeLimit(inner: Supplier[Route]): Route = RouteAdapter { - D.withoutSizeLimit { inner.get.delegate } - } - - /** - * Inspects the request's `Accept-Language` header and determines, - * which of the given language alternatives is preferred by the client. - * (See http://tools.ietf.org/html/rfc7231#section-5.3.5 for more details on the - * negotiation logic.) - * If there are several best language alternatives that the client - * has equal preference for (even if this preference is zero!) - * the order of the arguments is used as a tie breaker (First one wins). - * - * If [languages] is empty, the route is rejected. - */ - def selectPreferredLanguage(languages: JIterable[Language], inner: JFunction[Language, Route]): Route = RouteAdapter { - languages.asScala.toList match { - case head :: tail ⇒ - D.selectPreferredLanguage(head.asScala, tail.map(_.asScala).toSeq: _*) { lang ⇒ inner.apply(lang).delegate } - case _ ⇒ - D.reject() - } - } - -} diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/directives/ParameterDirectives.scala b/akka-http/src/main/scala/akka/http/javadsl/server/directives/ParameterDirectives.scala deleted file mode 100644 index 0833c14034..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/directives/ParameterDirectives.scala +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.javadsl.server.directives - -import java.util.{ Map ⇒ JMap, List ⇒ JList } -import java.util.AbstractMap.SimpleImmutableEntry -import java.util.Optional -import java.util.function.{ Function ⇒ JFunction } - -import akka.http.javadsl.unmarshalling.Unmarshaller - -import scala.collection.JavaConverters._ -import scala.compat.java8.OptionConverters._ - -import akka.http.javadsl.server.Route -import akka.http.scaladsl.server.directives.{ ParameterDirectives ⇒ D } -import akka.http.scaladsl.server.directives.ParameterDirectives._ -import akka.http.scaladsl.unmarshalling.PredefinedFromStringUnmarshallers._ - -abstract class ParameterDirectives extends MiscDirectives { - - def parameter(name: String, inner: java.util.function.Function[String, Route]): Route = RouteAdapter( - D.parameter(name) { value ⇒ - inner.apply(value).delegate - }) - - @CorrespondsTo("parameter") - def parameterOptional(name: String, inner: java.util.function.Function[Optional[String], Route]): Route = RouteAdapter( - D.parameter(name.?) { value ⇒ - inner.apply(value.asJava).delegate - }) - - @CorrespondsTo("parameterSeq") - def parameterList(name: String, inner: java.util.function.Function[java.util.List[String], Route]): Route = RouteAdapter( - D.parameter(_string2NR(name).*) { values ⇒ - inner.apply(values.toSeq.asJava).delegate - }) - - def parameter[T](t: Unmarshaller[String, T], name: String, inner: java.util.function.Function[T, Route]): Route = { - import t.asScala - RouteAdapter( - D.parameter(name.as[T]) { value ⇒ - inner.apply(value).delegate - }) - } - - @CorrespondsTo("parameter") - def parameterOptional[T](t: Unmarshaller[String, T], name: String, inner: java.util.function.Function[Optional[T], Route]): Route = { - import t.asScala - RouteAdapter( - D.parameter(name.as[T].?) { value ⇒ - inner.apply(value.asJava).delegate - }) - } - - @CorrespondsTo("parameter") - def parameterOrDefault[T](t: Unmarshaller[String, T], defaultValue: T, name: String, inner: java.util.function.Function[T, Route]): Route = { - import t.asScala - RouteAdapter( - D.parameter(name.as[T].?(defaultValue)) { value ⇒ - inner.apply(value).delegate - }) - } - - @CorrespondsTo("parameterSeq") - def parameterList[T](t: Unmarshaller[String, T], name: String, inner: java.util.function.Function[java.util.List[T], Route]): Route = { - import t.asScala - RouteAdapter( - D.parameter(name.as[T].*) { values ⇒ - inner.apply(values.toSeq.asJava).delegate - }) - } - - def parameterMap(inner: JFunction[JMap[String, String], Route]): Route = RouteAdapter { - D.parameterMap { map ⇒ inner.apply(map.asJava).delegate } - } - - def parameterMultiMap(inner: JFunction[JMap[String, JList[String]], Route]): Route = RouteAdapter { - D.parameterMultiMap { map ⇒ inner.apply(map.mapValues { l ⇒ l.asJava }.asJava).delegate } - } - - @CorrespondsTo("parameterSeq") - def parameterList(inner: JFunction[JList[JMap.Entry[String, String]], Route]): Route = RouteAdapter { - D.parameterSeq { list ⇒ - val entries: Seq[JMap.Entry[String, String]] = list.map { e ⇒ new SimpleImmutableEntry(e._1, e._2) } - inner.apply(entries.asJava).delegate - } - } - -} diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/directives/PathDirectives.scala b/akka-http/src/main/scala/akka/http/javadsl/server/directives/PathDirectives.scala deleted file mode 100644 index c391148815..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/directives/PathDirectives.scala +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.javadsl.server.directives - -import java.util.function.BiFunction -import java.util.function.{ Function ⇒ JFunction } -import java.util.function.Supplier - -import akka.http.javadsl.unmarshalling.Unmarshaller - -import scala.util.Failure -import scala.util.Success - -import akka.http.javadsl.model.StatusCode -import akka.http.javadsl.server.PathMatcher0 -import akka.http.javadsl.server.PathMatcher1 -import akka.http.javadsl.server.PathMatcher2 -import akka.http.javadsl.server.Route -import akka.http.scaladsl.model.StatusCodes.Redirection -import akka.http.scaladsl.server.{ Directives ⇒ D } - -import akka.http.scaladsl.server.PathMatchers - -/** - * Only path prefixes are matched by these methods, since any kind of chaining path extractions - * in Java would just look cumbersome without operator overloads; hence, no PathMatcher for Java. - * - * Just nest path() directives with the required types, ending in pathEnd() if you want to fail - * further paths. - */ -abstract class PathDirectives extends ParameterDirectives { - - /** - * Rejects the request if the unmatchedPath of the [[akka.http.javadsl.server.RequestContext]] is non-empty, - * or said differently: only passes on the request to its inner route if the request path - * has been matched completely. - */ - def pathEnd(inner: Supplier[Route]): Route = RouteAdapter( - D.pathEnd { inner.get.delegate }) - - /** - * 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. - * - * Note that trailing slash and non-trailing slash URLs are '''not''' the same, although they often serve - * the same content. It is recommended to serve only one URL version and make the other redirect to it using - * [[#redirectToTrailingSlashIfMissing]] or [[#redirectToNoTrailingSlashIfPresent]] directive. - * - * For example: - * {{{ - * def route = { - * // redirect '/users/' to '/users', '/users/:userId/' to '/users/:userId' - * redirectToNoTrailingSlashIfPresent(Found) { - * pathPrefix("users") { - * pathEnd { - * // user list ... - * } ~ - * path(UUID) { userId => - * // user profile ... - * } - * } - * } - * } - * }}} - * - * For further information, refer to: http://googlewebmastercentral.blogspot.de/2010/04/to-slash-or-not-to-slash.html - */ - def pathEndOrSingleSlash(inner: Supplier[Route]): Route = RouteAdapter { - D.pathEndOrSingleSlash { inner.get.delegate } - } - - /** - * Only passes on the request to its inner route if the request path - * consists of exactly one remaining slash. - */ - def pathSingleSlash(inner: Supplier[Route]): Route = RouteAdapter { - D.pathSingleSlash { inner.get.delegate } - } - - /** - * Matches a prefix to the remaining unmatched path after consuming a leading slash. - * The matcher has to match the remaining path completely. - * If matched the value matching the prefix is extracted on the directive level. - */ - def path(segment: String, inner: Supplier[Route]): Route = RouteAdapter { - D.path(segment) { inner.get.delegate } - } - def path(inner: java.util.function.Function[String, Route]): Route = RouteAdapter { - D.path(PathMatchers.Segment) { element ⇒ inner.apply(element).delegate } - } - - /** - * Applies the given [[PathMatcher0]] to the remaining unmatched path after consuming a leading slash. - * The matcher has to match the remaining path completely. - * If matched the value extracted by the [[PathMatcher0]] is extracted on the directive level. - */ - def path(p: PathMatcher0, inner: Supplier[Route]): Route = RouteAdapter { - D.path(p.toScala) { inner.get.delegate } - } - def path[T](p: PathMatcher1[T], inner: JFunction[T, Route]): Route = RouteAdapter { - D.path(p.toScala) { t1 ⇒ inner.apply(t1).delegate } - } - def path[T1, T2](p: PathMatcher2[T1, T2], inner: BiFunction[T1, T2, Route]): Route = RouteAdapter { - D.path(p.toScala) { (t1, t2) ⇒ inner.apply(t1, t2).delegate } - } - - /** - * Matches a prefix to the remaining unmatched path after consuming a leading slash. - * The matcher has to match a prefix of the remaining path. - * If matched the value matching the prefix is extracted on the directive level. - */ - def pathPrefix(segment: String, inner: Supplier[Route]): Route = RouteAdapter { - D.pathPrefix(segment) { inner.get.delegate } - } - def pathPrefix(inner: java.util.function.Function[String, Route]): Route = RouteAdapter { - D.pathPrefix(PathMatchers.Segment) { element ⇒ inner.apply(element).delegate } - } - - /** - * Applies the given [[PathMatcher0]] to the remaining unmatched path after consuming a leading slash. - * The matcher has to match a prefix of the remaining path. - * If matched the value extracted by the PathMatcher is extracted on the directive level. - */ - def pathPrefix(p: PathMatcher0, inner: Supplier[Route]): Route = RouteAdapter { - D.pathPrefix(p.toScala) { inner.get.delegate } - } - def pathPrefix[T](p: PathMatcher1[T], inner: JFunction[T, Route]): Route = RouteAdapter { - D.pathPrefix(p.toScala) { t1 ⇒ inner.apply(t1).delegate } - } - def pathPrefix[T1, T2](p: PathMatcher2[T1, T2], inner: BiFunction[T1, T2, Route]): Route = RouteAdapter { - D.pathPrefix(p.toScala) { (t1, t2) ⇒ inner.apply(t1, t2).delegate } - } - - /** - * Applies the given matcher directly to a prefix of the unmatched path of the - * [[akka.http.javadsl.server.RequestContext]] (i.e. without implicitly consuming a leading slash). - * The matcher has to match a prefix of the remaining path. - * If matched the value extracted by the PathMatcher is extracted on the directive level. - */ - def rawPathPrefix(segment: String, inner: Supplier[Route]): Route = RouteAdapter { - D.rawPathPrefix(segment) { inner.get().delegate } - } - def rawPathPrefix(pm: PathMatcher0, inner: Supplier[Route]): Route = RouteAdapter { - D.rawPathPrefix(pm.toScala) { inner.get().delegate } - } - def rawPathPrefix[T1](pm: PathMatcher1[T1], inner: Function[T1, Route]): Route = RouteAdapter { - D.rawPathPrefix(pm.toScala) { t1 ⇒ inner.apply(t1).delegate } - } - def rawPathPrefix[T1, T2](pm: PathMatcher2[T1, T2], inner: BiFunction[T1, T2, Route]): Route = RouteAdapter { - D.rawPathPrefix(pm.toScala) { case (t1, t2) ⇒ inner.apply(t1, t2).delegate } - } - - /** - * Checks whether the unmatchedPath of the [[akka.http.javadsl.server.RequestContext]] has a prefix matched by the - * given PathMatcher. In analogy to the `pathPrefix` directive a leading slash is implied. - */ - def pathPrefixTest(segment: String, inner: Supplier[Route]): Route = RouteAdapter { - D.pathPrefixTest(segment) { inner.get().delegate } - } - def pathPrefixTest(pm: PathMatcher0, inner: Supplier[Route]): Route = RouteAdapter { - D.pathPrefixTest(pm.toScala) { inner.get().delegate } - } - def pathPrefixTest[T1](pm: PathMatcher1[T1], inner: Function[T1, Route]): Route = RouteAdapter { - D.pathPrefixTest(pm.toScala) { t1 ⇒ inner.apply(t1).delegate } - } - def pathPrefixTest[T1, T2](pm: PathMatcher2[T1, T2], inner: BiFunction[T1, T2, Route]): Route = RouteAdapter { - D.pathPrefixTest(pm.toScala) { case (t1, t2) ⇒ inner.apply(t1, t2).delegate } - } - - /** - * Checks whether the unmatchedPath of the [[akka.http.javadsl.server.RequestContext]] has a prefix matched by the - * given PathMatcher. However, as opposed to the `pathPrefix` directive the matched path is not - * actually "consumed". - */ - def rawPathPrefixTest(segment: String, inner: Supplier[Route]): Route = RouteAdapter { - D.rawPathPrefixTest(segment) { inner.get().delegate } - } - def rawPathPrefixTest(pm: PathMatcher0, inner: Supplier[Route]): Route = RouteAdapter { - D.rawPathPrefixTest(pm.toScala) { inner.get().delegate } - } - def rawPathPrefixTest[T1](pm: PathMatcher1[T1], inner: Function[T1, Route]): Route = RouteAdapter { - D.rawPathPrefixTest(pm.toScala) { t1 ⇒ inner.apply(t1).delegate } - } - def rawPathPrefixTest[T1, T2](pm: PathMatcher2[T1, T2], inner: BiFunction[T1, T2, Route]): Route = RouteAdapter { - D.rawPathPrefixTest(pm.toScala) { case (t1, t2) ⇒ inner.apply(t1, t2).delegate } - } - - /** - * Applies the given [[akka.http.scaladsl.server.PathMatcher]] to a suffix of the remaining unmatchedPath of the [[akka.http.javadsl.server.RequestContext]]. - * If matched the value extracted by the [[akka.http.javadsl.server.PathMatcher0]] is extracted and the matched parts of the path are consumed. - * Note that, for efficiency reasons, the given [[akka.http.javadsl.server.PathMatcher0]] must match the desired suffix in reversed-segment - * order, i.e. `pathSuffix("baz" / "bar")` would match `/foo/bar/baz`! - */ - def pathSuffix(segment: String, inner: Supplier[Route]): Route = RouteAdapter { - D.pathSuffix(segment) { inner.get().delegate } - } - def pathSuffix(pm: PathMatcher0, inner: Supplier[Route]): Route = RouteAdapter { - D.pathSuffix(pm.toScala) { inner.get().delegate } - } - def pathSuffix[T1](pm: PathMatcher1[T1], inner: Function[T1, Route]): Route = RouteAdapter { - D.pathSuffix(pm.toScala) { t1 ⇒ inner.apply(t1).delegate } - } - def pathSuffix[T1, T2](pm: PathMatcher2[T1, T2], inner: BiFunction[T1, T2, Route]): Route = RouteAdapter { - D.pathSuffix(pm.toScala) { case (t1, t2) ⇒ inner.apply(t1, t2).delegate } - } - - /** - * Checks whether the unmatchedPath of the [[akka.http.javadsl.server.RequestContext]] has a suffix matched by the - * given PathMatcher. However, as opposed to the pathSuffix directive the matched path is not - * actually "consumed". - * Note that, for efficiency reasons, the given PathMatcher must match the desired suffix in reversed-segment - * order, i.e. `pathSuffixTest("baz" / "bar")` would match `/foo/bar/baz`! - */ - def pathSuffixTest(segment: String, inner: Supplier[Route]): Route = RouteAdapter { - D.pathSuffixTest(segment) { inner.get().delegate } - } - def pathSuffixTest(pm: PathMatcher0, inner: Supplier[Route]): Route = RouteAdapter { - D.pathSuffixTest(pm.toScala) { inner.get().delegate } - } - def pathSuffixTest[T1](pm: PathMatcher1[T1], inner: Function[T1, Route]): Route = RouteAdapter { - D.pathSuffixTest(pm.toScala) { t1 ⇒ inner.apply(t1).delegate } - } - def pathSuffixTest[T1, T2](pm: PathMatcher2[T1, T2], inner: BiFunction[T1, T2, Route]): Route = RouteAdapter { - D.pathSuffixTest(pm.toScala) { case (t1, t2) ⇒ inner.apply(t1, t2).delegate } - } - - /** - * If the request path doesn't end with a slash, redirect to the same uri with trailing slash in the path. - * - * '''Caveat''': [[#path]] without trailing slash and [[#pathEnd]] directives will not match inside of this directive. - * - * @param redirectionType A status code from StatusCodes, which must be a redirection type. - */ - def redirectToTrailingSlashIfMissing(redirectionType: StatusCode, inner: Supplier[Route]): Route = RouteAdapter { - redirectionType match { - case r: Redirection ⇒ D.redirectToTrailingSlashIfMissing(r) { inner.get().delegate } - case _ ⇒ throw new IllegalArgumentException("Not a valid redirection status code: " + redirectionType) - } - } - - /** - * If the request path ends with a slash, redirect to the same uri without trailing slash in the path. - * - * '''Caveat''': [[#pathSingleSlash]] directive will not match inside of this directive. - * - * @param redirectionType A status code from StatusCodes, which must be a redirection type. - */ - def redirectToNoTrailingSlashIfPresent(redirectionType: StatusCode, inner: Supplier[Route]): Route = RouteAdapter { - redirectionType match { - case r: Redirection ⇒ D.redirectToNoTrailingSlashIfPresent(r) { inner.get().delegate } - case _ ⇒ throw new IllegalArgumentException("Not a valid redirection status code: " + redirectionType) - } - } - - //------ extra java-specific methods - - // Java-specific since there's no Java API to create custom PathMatchers. And that's because there's no Path model in Java. - /** - * Consumes a leading slash and extracts the next path segment, unmarshalling it and passing the result to the inner function. - */ - def pathPrefix[T](t: Unmarshaller[String, T], inner: java.util.function.Function[T, Route]): Route = RouteAdapter { - D.pathPrefix(PathMatchers.Segment)(unmarshal(t, inner)) - } - - /** - * Consumes a leading slash and extracts the next path segment, unmarshalling it and passing the result to the inner function, - * expecting the full path to have been consumed then. - */ - def path[T](t: Unmarshaller[String, T], inner: java.util.function.Function[T, Route]): Route = RouteAdapter { - D.path(PathMatchers.Segment)(unmarshal(t, inner)) - } - - private def unmarshal[T](t: Unmarshaller[String, T], inner: java.util.function.Function[T, Route]) = { element: String ⇒ - D.extractRequestContext { ctx ⇒ - import ctx.executionContext - import ctx.materializer - - D.onComplete(t.asScala.apply(element)) { - case Success(value) ⇒ - inner.apply(value).delegate - case Failure(x: IllegalArgumentException) ⇒ - D.reject() - case Failure(x) ⇒ - D.failWith(x) - } - } - } -} - diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/directives/RangeDirectives.scala b/akka-http/src/main/scala/akka/http/javadsl/server/directives/RangeDirectives.scala deleted file mode 100644 index 59d65c0bac..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/directives/RangeDirectives.scala +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server -package directives - -import java.util.function.Supplier -import akka.http.scaladsl.server.{ Directives ⇒ D } - -abstract class RangeDirectives extends PathDirectives { - /** - * Answers GET requests with an `Accept-Ranges: bytes` header and converts HttpResponses coming back from its inner - * route into partial responses if the initial request contained a valid `Range` request header. The requested - * byte-ranges may be coalesced. - * This directive is transparent to non-GET requests - * Rejects requests with unsatisfiable ranges `UnsatisfiableRangeRejection`. - * Rejects requests with too many expected ranges. - * - * Note: if you want to combine this directive with `conditional(...)` you need to put - * it on the *inside* of the `conditional(...)` directive, i.e. `conditional(...)` must be - * on a higher level in your route structure in order to function correctly. - * - * For more information, see: https://tools.ietf.org/html/rfc7233 - */ - def withRangeSupport(inner: Supplier[Route]): Route = RouteAdapter { - D.withRangeSupport { inner.get.delegate } - } -} diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/directives/RespondWithDirectives.scala b/akka-http/src/main/scala/akka/http/javadsl/server/directives/RespondWithDirectives.scala deleted file mode 100644 index 90809d489c..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/directives/RespondWithDirectives.scala +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.javadsl.server.directives - -import java.lang.{ Iterable ⇒ JIterable } -import java.util.function.Supplier -import java.util.{ List ⇒ JList } - -import akka.http.javadsl.model.HttpHeader -import akka.http.javadsl.server.Route -import akka.http.scaladsl.server.{ Directives ⇒ D } - -abstract class RespondWithDirectives extends RangeDirectives { - import akka.http.impl.util.JavaMapping.Implicits._ - - /** - * Unconditionally adds the given response header to all HTTP responses of its inner Route. - */ - def respondWithHeader(responseHeader: HttpHeader, inner: Supplier[Route]): Route = RouteAdapter { - D.respondWithHeader(responseHeader.asScala) { inner.get.delegate } - } - - /** - * Adds the given response header to all HTTP responses of its inner Route, - * if the response from the inner Route doesn't already contain a header with the same name. - */ - def respondWithDefaultHeader(responseHeader: HttpHeader, inner: Supplier[Route]): Route = RouteAdapter { - D.respondWithDefaultHeader(responseHeader.asScala) { inner.get.delegate } - } - - /** - * Unconditionally adds the given response headers to all HTTP responses of its inner Route. - */ - def respondWithHeaders(responseHeaders: JIterable[HttpHeader], inner: Supplier[Route]): Route = RouteAdapter { - D.respondWithHeaders(responseHeaders.asScala) { inner.get.delegate } - } - - /** - * Adds the given response headers to all HTTP responses of its inner Route, - * if a header already exists it is not added again. - */ - def respondWithDefaultHeaders(responseHeaders: JIterable[HttpHeader], inner: Supplier[Route]): Route = RouteAdapter { - D.respondWithDefaultHeaders(responseHeaders.asScala.toVector) { inner.get.delegate } - } - -} \ No newline at end of file diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/directives/RouteAdapter.scala b/akka-http/src/main/scala/akka/http/javadsl/server/directives/RouteAdapter.scala deleted file mode 100644 index 1d36f41f9b..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/directives/RouteAdapter.scala +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.javadsl.server.directives - -import akka.NotUsed -import akka.actor.ActorSystem -import akka.http.javadsl.model.HttpRequest -import akka.http.javadsl.model.HttpResponse -import akka.http.impl.util.JavaMapping.Implicits._ -import akka.http.javadsl.server.{ ExceptionHandler, RejectionHandler, Route, RoutingJavaMapping } -import RoutingJavaMapping._ -import akka.http.javadsl.settings.{ ParserSettings, RoutingSettings } -import akka.http.scaladsl -import akka.http.scaladsl.server.RouteConcatenation._ -import akka.stream.{ Materializer, javadsl } -import akka.stream.scaladsl.Flow - -/** INTERNAL API */ -final class RouteAdapter(val delegate: akka.http.scaladsl.server.Route) extends Route { - import RouteAdapter._ - - override def flow(system: ActorSystem, materializer: Materializer): javadsl.Flow[HttpRequest, HttpResponse, NotUsed] = - scalaFlow(system, materializer).asJava - - private def scalaFlow(system: ActorSystem, materializer: Materializer): Flow[HttpRequest, HttpResponse, NotUsed] = { - implicit val s = system - implicit val m = materializer - Flow[HttpRequest].map(_.asScala).via(delegate).map(_.asJava) - } - - override def orElse(alternative: Route): Route = - alternative match { - case adapt: RouteAdapter ⇒ - RouteAdapter(delegate ~ adapt.delegate) - } - - override def seal(system: ActorSystem, materializer: Materializer): Route = { - implicit val s = system - implicit val m = materializer - - RouteAdapter(scaladsl.server.Route.seal(delegate)) - } - - override def seal(routingSettings: RoutingSettings, parserSettings: ParserSettings, rejectionHandler: RejectionHandler, exceptionHandler: ExceptionHandler, system: ActorSystem, materializer: Materializer): Route = { - implicit val s = system - implicit val m = materializer - - RouteAdapter(scaladsl.server.Route.seal(delegate)( - routingSettings.asScala, - parserSettings.asScala, - rejectionHandler.asScala, - exceptionHandler.asScala)) - } - - override def toString = s"akka.http.javadsl.server.Route($delegate)" - -} - -object RouteAdapter { - def apply(delegate: akka.http.scaladsl.server.Route) = new RouteAdapter(delegate) -} \ No newline at end of file diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/directives/RouteDirectives.scala b/akka-http/src/main/scala/akka/http/javadsl/server/directives/RouteDirectives.scala deleted file mode 100644 index 68eef49db8..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/directives/RouteDirectives.scala +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.javadsl.server.directives - -import java.util.concurrent.CompletionStage - -import akka.dispatch.ExecutionContexts -import akka.http.javadsl.marshalling.Marshaller -import akka.http.scaladsl.server._ -import akka.japi.Util - -import scala.collection.immutable.Seq -import scala.annotation.varargs -import scala.collection.JavaConverters._ -import akka.http.impl.model.JavaUri -import akka.http.javadsl.model.HttpHeader -import akka.http.javadsl.model.HttpResponse -import akka.http.javadsl.model.RequestEntity -import akka.http.javadsl.model.StatusCode -import akka.http.javadsl.model.Uri -import akka.http.javadsl.server.{ RoutingJavaMapping, Rejection, Route } -import akka.http.scaladsl -import akka.http.scaladsl.marshalling.Marshaller._ -import akka.http.scaladsl.marshalling.ToResponseMarshallable -import akka.http.scaladsl.model.StatusCodes.Redirection -import akka.http.javadsl.server.RoutingJavaMapping._ -import akka.http.scaladsl.server.directives.{ RouteDirectives ⇒ D } -import akka.http.scaladsl.util.FastFuture._ - -abstract class RouteDirectives extends RespondWithDirectives { - import RoutingJavaMapping.Implicits._ - - // Don't try this at home – we only use it here for the java -> scala conversions - private implicit val conversionExecutionContext = ExecutionContexts.sameThreadExecutionContext - - /** - * Java-specific call added so you can chain together multiple alternate routes using comma, - * rather than having to explicitly call route1.orElse(route2).orElse(route3). - */ - @CorrespondsTo("concat") - @varargs def route(alternatives: Route*): Route = RouteAdapter { - import akka.http.scaladsl.server.Directives._ - - alternatives.map(_.delegate).reduce(_ ~ _) - } - - /** - * Rejects the request with the given rejections, or with an empty set of rejections if no rejections are given. - */ - @varargs def reject(rejection: Rejection, rejections: Rejection*): Route = RouteAdapter { - D.reject((rejection +: rejections).map(_.asScala): _*) - } - - /** - * Rejects the request with an empty rejection (usualy used for "no directive matched"). - */ - def reject(): Route = RouteAdapter { - D.reject() - } - - /** - * Completes the request with redirection response of the given type to the given URI. - * - * @param redirectionType A status code from StatusCodes, which must be a redirection type. - */ - def redirect(uri: Uri, redirectionType: StatusCode): Route = RouteAdapter { - redirectionType match { - case r: Redirection ⇒ D.redirect(uri.asInstanceOf[JavaUri].uri, r) - case _ ⇒ throw new IllegalArgumentException("Not a valid redirection status code: " + redirectionType) - } - } - - /** - * Bubbles the given error up the response chain, where it is dealt with by the closest `handleExceptions` - * directive and its ExceptionHandler. - */ - def failWith(error: Throwable): Route = RouteAdapter(D.failWith(error)) - - /** - * Completes the request using an HTTP 200 OK status code and the given body as UTF-8 entity. - */ - def complete(body: String): Route = RouteAdapter( - D.complete(body)) - - /** - * Completes the request using the given http response. - */ - def complete(response: HttpResponse): Route = RouteAdapter( - D.complete(response.asScala)) - - /** - * Completes the request using the given status code. - */ - def complete(status: StatusCode): Route = RouteAdapter( - D.complete(status.asScala)) - - /** - * Completes the request by marshalling the given value into an http response. - */ - def complete[T](value: T, marshaller: Marshaller[T, HttpResponse]) = RouteAdapter { - D.complete(ToResponseMarshallable(value)(marshaller)) - } - - /** - * Completes the request using the given status code and headers, marshalling the given value as response entity. - */ - def complete[T](status: StatusCode, headers: java.lang.Iterable[HttpHeader], value: T, marshaller: Marshaller[T, RequestEntity]) = RouteAdapter { - D.complete(ToResponseMarshallable(value)(fromToEntityMarshaller(status.asScala, Util.immutableSeq(headers).map(_.asScala))(marshaller))) // TODO avoid the map() - } - - /** - * Completes the request using the given status code, headers, and response entity. - */ - def complete(status: StatusCode, headers: java.lang.Iterable[HttpHeader], entity: RequestEntity) = RouteAdapter { - D.complete(scaladsl.model.HttpResponse(status = status.asScala, entity = entity.asScala, headers = Util.immutableSeq(headers).map(_.asScala))) // TODO avoid the map() - } - - /** - * Completes the request using the given status code, marshalling the given value as response entity. - */ - def complete[T](status: StatusCode, value: T, marshaller: Marshaller[T, RequestEntity]) = RouteAdapter { - D.complete(ToResponseMarshallable(value)(fromToEntityMarshaller(status.asScala)(marshaller))) - } - - /** - * Completes the request using the given status code and response entity. - */ - def complete(status: StatusCode, entity: RequestEntity) = RouteAdapter { - D.complete(scaladsl.model.HttpResponse(status = status.asScala, entity = entity.asScala)) - } - - /** - * Completes the request using the given status code and the given body as UTF-8. - */ - def complete(status: StatusCode, entity: String) = RouteAdapter { - D.complete(scaladsl.model.HttpResponse(status = status.asScala, entity = entity)) - } - - /** - * Completes the request as HTTP 200 OK, adding the given headers, and marshalling the given value as response entity. - */ - def complete[T](headers: java.lang.Iterable[HttpHeader], value: T, marshaller: Marshaller[T, RequestEntity]) = RouteAdapter { - D.complete(ToResponseMarshallable(value)(fromToEntityMarshaller(headers = Util.immutableSeq(headers).map(_.asScala))(marshaller))) // TODO can we avoid the map() ? - } - - /** - * Completes the request as HTTP 200 OK, adding the given headers and response entity. - */ - def complete(headers: java.lang.Iterable[HttpHeader], entity: RequestEntity) = RouteAdapter { - D.complete(scaladsl.model.HttpResponse(headers = headers.asScala.toVector.map(_.asScala), entity = entity.asScala)) // TODO can we avoid the map() ? - } - - /** - * Completes the request as HTTP 200 OK, marshalling the given value as response entity. - */ - @CorrespondsTo("complete") - def completeOK[T](value: T, marshaller: Marshaller[T, RequestEntity]) = RouteAdapter { - D.complete(ToResponseMarshallable(value)(fromToEntityMarshaller()(marshaller))) - } - - /** - * Completes the request as HTTP 200 OK with the given value as response entity. - */ - def complete(entity: RequestEntity) = RouteAdapter { - D.complete(scaladsl.model.HttpResponse(entity = entity.asScala)) - } - - // --- manual "magnet" for Scala Future --- - - /** - * Completes the request by marshalling the given future value into an http response. - */ - @CorrespondsTo("complete") - def completeWithFutureResponse(value: scala.concurrent.Future[HttpResponse]) = RouteAdapter { - D.complete(value.fast.map(_.asScala)) - } - - /** - * Completes the request by marshalling the given future value into an http response. - */ - @CorrespondsTo("complete") - def completeOKWithFutureString(value: scala.concurrent.Future[String]) = RouteAdapter { - D.complete(value) - } - - /** - * Completes the request using the given future status code. - */ - @CorrespondsTo("complete") - def completeWithFutureStatus(status: scala.concurrent.Future[StatusCode]): Route = RouteAdapter { - D.complete(status.fast.map(_.asScala)) - } - - /** - * Completes the request by marshalling the given value into an http response. - */ - @CorrespondsTo("complete") - def completeOKWithFuture[T](value: scala.concurrent.Future[T], marshaller: Marshaller[T, RequestEntity]) = RouteAdapter { - D.complete(value.fast.map(v ⇒ ToResponseMarshallable(v)(fromToEntityMarshaller()(marshaller)))) - } - - /** - * Completes the request by marshalling the given value into an http response. - */ - @CorrespondsTo("complete") - def completeWithFuture[T](value: scala.concurrent.Future[T], marshaller: Marshaller[T, HttpResponse]) = RouteAdapter { - D.complete(value.fast.map(v ⇒ ToResponseMarshallable(v)(marshaller))) - } - - // --- manual "magnet" for CompletionStage --- - - /** - * Completes the request by marshalling the given future value into an http response. - */ - @CorrespondsTo("complete") - def completeWithFuture(value: CompletionStage[HttpResponse]) = RouteAdapter { - D.complete(value.asScala.fast.map(_.asScala)) - } - - /** - * Completes the request by marshalling the given future value into an http response. - */ - @CorrespondsTo("complete") - def completeOKWithFuture(value: CompletionStage[RequestEntity]) = RouteAdapter { - D.complete(value.asScala.fast.map(_.asScala)) - } - - /** - * Completes the request by marshalling the given future value into an http response. - */ - @CorrespondsTo("complete") - def completeOKWithFutureString(value: CompletionStage[String]) = RouteAdapter { - D.complete(value.asScala) - } - - /** - * Completes the request using the given future status code. - */ - @CorrespondsTo("complete") - def completeWithFutureStatus(status: CompletionStage[StatusCode]): Route = RouteAdapter { - D.complete(status.asScala.fast.map(_.asScala)) - } - - /** - * Completes the request with an `OK` status code by marshalling the given value into an http response. - */ - @CorrespondsTo("complete") - def completeOKWithFuture[T](value: CompletionStage[T], marshaller: Marshaller[T, RequestEntity]) = RouteAdapter { - D.complete(value.asScala.fast.map(v ⇒ ToResponseMarshallable(v)(fromToEntityMarshaller()(marshaller)))) - } - - /** - * Completes the request by marshalling the given value into an http response. - */ - @CorrespondsTo("complete") - def completeWithFuture[T](value: CompletionStage[T], marshaller: Marshaller[T, HttpResponse]) = RouteAdapter { - D.complete(value.asScala.fast.map(v ⇒ ToResponseMarshallable(v)(marshaller))) - } - -} diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/directives/SchemeDirectives.scala b/akka-http/src/main/scala/akka/http/javadsl/server/directives/SchemeDirectives.scala deleted file mode 100644 index 97ff656177..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/directives/SchemeDirectives.scala +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server.directives - -import java.util.function.{ Function ⇒ JFunction } -import java.util.function.Supplier - -import akka.http.javadsl.server.Route -import akka.http.scaladsl.server.{ Directives ⇒ D } - -abstract class SchemeDirectives extends RouteDirectives { - /** - * Extracts the Uri scheme from the request. - */ - def extractScheme(inner: JFunction[String, Route]): Route = RouteAdapter { - D.extractScheme { s ⇒ inner.apply(s).delegate } - } - - /** - * Rejects all requests whose Uri scheme does not match the given one. - */ - def scheme(name: String, inner: Supplier[Route]): Route = RouteAdapter { - D.scheme(name) { inner.get().delegate } - } -} diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/directives/SecurityDirectives.scala b/akka-http/src/main/scala/akka/http/javadsl/server/directives/SecurityDirectives.scala deleted file mode 100644 index 9ed7f9a2fb..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/directives/SecurityDirectives.scala +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ -package akka.http.javadsl.server.directives - -import java.util.Optional -import java.util.concurrent.CompletionStage -import java.util.function.{ Function ⇒ JFunction } -import java.util.function.Supplier - -import scala.compat.java8.FutureConverters._ -import scala.compat.java8.OptionConverters._ -import akka.http.javadsl.model.headers.HttpChallenge -import akka.http.javadsl.model.headers.HttpCredentials -import akka.http.javadsl.server.{ Route, RequestContext } -import akka.http.scaladsl -import akka.http.scaladsl.server.{ Directives ⇒ D } - -import scala.concurrent.{ ExecutionContextExecutor, Future } - -object SecurityDirectives { - /** - * Represents HTTP Basic or OAuth2 authentication credentials supplied with a request. - */ - case class ProvidedCredentials(private val asScala: scaladsl.server.directives.Credentials.Provided) { - /** - * The username or token provided with the credentials - */ - def identifier: String = asScala.identifier - - /** - * Safely compares the passed in `secret` with the received secret part of the Credentials. - * Use of this method instead of manual String equality testing is recommended in order to guard against timing attacks. - * - * See also [[akka.http.impl.util.EnhancedString#secure_==]], for more information. - */ - def verify(secret: String): Boolean = asScala.verify(secret) - } - - private def toJava(cred: scaladsl.server.directives.Credentials): Optional[ProvidedCredentials] = cred match { - case provided: scaladsl.server.directives.Credentials.Provided ⇒ Optional.of(ProvidedCredentials(provided)) - case _ ⇒ Optional.empty() - } -} - -abstract class SecurityDirectives extends SchemeDirectives { - import SecurityDirectives._ - import akka.http.impl.util.JavaMapping.Implicits._ - - /** - * Extracts the potentially present [[HttpCredentials]] provided with the request's [[akka.http.javadsl.model.headers.Authorization]] header. - */ - def extractCredentials(inner: JFunction[Optional[HttpCredentials], Route]): Route = RouteAdapter { - D.extractCredentials { cred ⇒ - inner.apply(cred.map(_.asJava).asJava).delegate // TODO attempt to not need map() - } - } - - /** - * Wraps the inner route with Http Basic authentication support using a given `Authenticator[T]`. - * The given authenticator determines whether the credentials in the request are valid - * and, if so, which user object to supply to the inner route. - * - * Authentication is required in this variant, i.e. the request is rejected if [authenticator] returns Optional.empty. - */ - def authenticateBasic[T](realm: String, authenticator: JFunction[Optional[ProvidedCredentials], Optional[T]], - inner: JFunction[T, Route]): Route = RouteAdapter { - D.authenticateBasic(realm, c ⇒ authenticator.apply(toJava(c)).asScala) { t ⇒ - inner.apply(t).delegate - } - } - - /** - * Wraps the inner route with Http Basic authentication support. - * The given authenticator determines whether the credentials in the request are valid - * and, if so, which user object to supply to the inner route. - * - * Authentication is required in this variant, i.e. the request is rejected if [authenticator] returns Optional.empty. - */ - def authenticateBasicPF[T](realm: String, authenticator: PartialFunction[Optional[ProvidedCredentials], T], - inner: JFunction[T, Route]): Route = RouteAdapter { - def pf: PartialFunction[scaladsl.server.directives.Credentials, Option[T]] = { - case c ⇒ Option(authenticator.applyOrElse(toJava(c), (_: Any) ⇒ null.asInstanceOf[T])) - } - - D.authenticateBasic(realm, pf) { t ⇒ - inner.apply(t).delegate - } - } - - /** - * Wraps the inner route with Http Basic authentication support. - * The given authenticator determines whether the credentials in the request are valid - * and, if so, which user object to supply to the inner route. - * - * Authentication is required in this variant, i.e. the request is rejected if [authenticator] returns Optional.empty. - */ - def authenticateBasicPFAsync[T](realm: String, authenticator: PartialFunction[Optional[ProvidedCredentials], CompletionStage[T]], - inner: JFunction[T, Route]): Route = RouteAdapter { - def pf(implicit ec: ExecutionContextExecutor): PartialFunction[scaladsl.server.directives.Credentials, Future[Option[T]]] = { - case credentials ⇒ - val jCredentials = toJava(credentials) - if (authenticator isDefinedAt jCredentials) { - authenticator(jCredentials).toScala.map(Some(_)) - } else { - Future.successful(None) - } - } - - D.extractExecutionContext { implicit ec ⇒ - D.authenticateBasicAsync(realm, pf) { t ⇒ - inner.apply(t).delegate - } - } - } - - /** - * Wraps the inner route with Http Basic authentication support using a given `Authenticator[T]`. - * The given authenticator determines whether the credentials in the request are valid - * and, if so, which user object to supply to the inner route. - * - * Authentication is optional in this variant. - */ - @CorrespondsTo("authenticateBasic") - def authenticateBasicOptional[T](realm: String, authenticator: JFunction[Optional[ProvidedCredentials], Optional[T]], - inner: JFunction[Optional[T], Route]): Route = RouteAdapter { - D.authenticateBasic(realm, c ⇒ authenticator.apply(toJava(c)).asScala).optional { t ⇒ - inner.apply(t.asJava).delegate - } - } - - /** - * Wraps the inner route with Http Basic authentication support. - * The given authenticator determines whether the credentials in the request are valid - * and, if so, which user object to supply to the inner route. - * - * Authentication is required in this variant, i.e. the request is rejected if [authenticator] returns Optional.empty. - */ - def authenticateBasicAsync[T](realm: String, authenticator: JFunction[Optional[ProvidedCredentials], CompletionStage[Optional[T]]], - inner: JFunction[T, Route]): Route = RouteAdapter { - D.extractExecutionContext { implicit ctx ⇒ - D.authenticateBasicAsync(realm, c ⇒ authenticator.apply(toJava(c)).toScala.map(_.asScala)) { t ⇒ - inner.apply(t).delegate - } - } - } - - /** - * Wraps the inner route with Http Basic authentication support. - * The given authenticator determines whether the credentials in the request are valid - * and, if so, which user object to supply to the inner route. - * - * Authentication is optional in this variant. - */ - @CorrespondsTo("authenticateBasicAsync") - def authenticateBasicAsyncOptional[T](realm: String, authenticator: JFunction[Optional[ProvidedCredentials], CompletionStage[Optional[T]]], - inner: JFunction[Optional[T], Route]): Route = RouteAdapter { - D.extractExecutionContext { implicit ctx ⇒ - D.authenticateBasicAsync(realm, c ⇒ authenticator.apply(toJava(c)).toScala.map(_.asScala)).optional { t ⇒ - inner.apply(t.asJava).delegate - } - } - } - - /** - * A directive that wraps the inner route with OAuth2 Bearer Token authentication support. - * The given authenticator determines whether the credentials in the request are valid - * and, if so, which user object to supply to the inner route. - * - * Authentication is required in this variant, i.e. the request is rejected if [authenticator] returns Optional.empty. - */ - def authenticateOAuth2[T](realm: String, authenticator: JFunction[Optional[ProvidedCredentials], Optional[T]], - inner: JFunction[T, Route]): Route = RouteAdapter { - D.authenticateOAuth2(realm, c ⇒ authenticator.apply(toJava(c)).asScala) { t ⇒ - inner.apply(t).delegate - } - } - - /** - * A directive that wraps the inner route with OAuth2 Bearer Token authentication support. - * The given authenticator determines whether the credentials in the request are valid - * and, if so, which user object to supply to the inner route. - * - * Authentication is optional in this variant. - */ - @CorrespondsTo("authenticateOAuth2") - def authenticateOAuth2Optional[T](realm: String, authenticator: JFunction[Optional[ProvidedCredentials], Optional[T]], - inner: JFunction[Optional[T], Route]): Route = RouteAdapter { - D.authenticateOAuth2(realm, c ⇒ authenticator.apply(toJava(c)).asScala).optional { t ⇒ - inner.apply(t.asJava).delegate - } - } - - /** - * A directive that wraps the inner route with OAuth2 Bearer Token authentication support. - * The given authenticator determines whether the credentials in the request are valid - * and, if so, which user object to supply to the inner route. - * - * Authentication is required in this variant, i.e. the request is rejected if [authenticator] returns Optional.empty. - */ - def authenticateOAuth2Async[T](realm: String, authenticator: JFunction[Optional[ProvidedCredentials], CompletionStage[Optional[T]]], - inner: JFunction[T, Route]): Route = RouteAdapter { - D.extractExecutionContext { implicit ctx ⇒ - D.authenticateOAuth2Async(realm, c ⇒ authenticator.apply(toJava(c)).toScala.map(_.asScala)) { t ⇒ - inner.apply(t).delegate - } - } - } - - /** - * A directive that wraps the inner route with OAuth2 Bearer Token authentication support. - * The given authenticator determines whether the credentials in the request are valid - * and, if so, which user object to supply to the inner route. - * - * Authentication is optional in this variant. - */ - @CorrespondsTo("authenticateOAuth2Async") - def authenticateOAuth2AsyncOptional[T](realm: String, authenticator: JFunction[Optional[ProvidedCredentials], CompletionStage[Optional[T]]], - inner: JFunction[Optional[T], Route]): Route = RouteAdapter { - D.extractExecutionContext { implicit ctx ⇒ - D.authenticateOAuth2Async(realm, c ⇒ authenticator.apply(toJava(c)).toScala.map(_.asScala)).optional { t ⇒ - inner.apply(t.asJava).delegate - } - } - } - - /** - * Lifts an authenticator function into a directive. The authenticator function gets passed in credentials from the - * [[akka.http.javadsl.model.headers.Authorization]] header of the request. If the function returns `Right(user)` the user object is provided - * to the inner route. If the function returns `Left(challenge)` the request is rejected with an - * [[akka.http.javadsl.server.AuthenticationFailedRejection]] that contains this challenge to be added to the response. - */ - def authenticateOrRejectWithChallenge[T]( - authenticator: JFunction[Optional[HttpCredentials], CompletionStage[Either[HttpChallenge, T]]], - inner: JFunction[T, Route]): Route = RouteAdapter { - D.extractExecutionContext { implicit ctx ⇒ - val scalaAuthenticator = { cred: Option[scaladsl.model.headers.HttpCredentials] ⇒ - authenticator.apply(cred.map(_.asJava).asJava).toScala.map(_.left.map(_.asScala)) - } - - D.authenticateOrRejectWithChallenge(scalaAuthenticator) { t ⇒ - inner.apply(t).delegate - } - } - } - - /** - * Lifts an authenticator function into a directive. Same as `authenticateOrRejectWithChallenge` - * but only applies the authenticator function with a certain type of credentials. - */ - def authenticateOrRejectWithChallenge[C <: HttpCredentials, T]( - c: Class[C], - authenticator: JFunction[Optional[C], CompletionStage[Either[HttpChallenge, T]]], - inner: JFunction[T, Route]): Route = RouteAdapter { - D.extractExecutionContext { implicit ctx ⇒ - val scalaAuthenticator = { cred: Option[scaladsl.model.headers.HttpCredentials] ⇒ - authenticator.apply(cred.filter(c.isInstance).map(_.asJava).asJava.asInstanceOf[Optional[C]]).toScala.map(_.left.map(_.asScala)) // TODO make sure cast is safe - } - - D.authenticateOrRejectWithChallenge(scalaAuthenticator) { t ⇒ - inner.apply(t).delegate - } - } - } - - /** - * Applies the given authorization check to the request. - * If the check fails the route is rejected with an [[akka.http.javadsl.server.AuthorizationFailedRejection]]. - */ - def authorize(check: Supplier[Boolean], inner: Supplier[Route]): Route = RouteAdapter { - D.authorize(check.get()) { - inner.get().delegate - } - } - - /** - * Applies the given authorization check to the request. - * If the check fails the route is rejected with an [[akka.http.javadsl.server.AuthorizationFailedRejection]]. - */ - @CorrespondsTo("authorize") - def authorizeWithRequestContext(check: akka.japi.function.Function[RequestContext, Boolean], inner: Supplier[Route]): Route = RouteAdapter { - D.authorize(rc ⇒ check(RequestContext.wrap(rc)))(inner.get().delegate) - } - - /** - * Applies the given authorization check to the request. - * If the check fails the route is rejected with an [[akka.http.javadsl.server.AuthorizationFailedRejection]]. - */ - def authorizeAsync(check: Supplier[CompletionStage[Boolean]], inner: Supplier[Route]): Route = RouteAdapter { - D.authorizeAsync(check.get().toScala) { - inner.get().delegate - } - } - - /** - * Asynchronous version of [[authorize]]. - * If the [[CompletionStage]] fails or is completed with `false` - * authorization fails and the route is rejected with an [[akka.http.javadsl.server.AuthorizationFailedRejection]]. - */ - @CorrespondsTo("authorizeAsync") - def authorizeAsyncWithRequestContext(check: akka.japi.function.Function[RequestContext, CompletionStage[Boolean]], inner: Supplier[Route]): Route = RouteAdapter { - D.authorizeAsync(rc ⇒ check(RequestContext.wrap(rc)).toScala)(inner.get().delegate) - } -} diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/directives/TimeoutDirectives.scala b/akka-http/src/main/scala/akka/http/javadsl/server/directives/TimeoutDirectives.scala deleted file mode 100644 index b558630b30..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/directives/TimeoutDirectives.scala +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server.directives - -import java.lang.{ Iterable ⇒ JIterable } -import java.util.function.{ BooleanSupplier, Function ⇒ JFunction, Supplier } - -import akka.http.javadsl.model.{ HttpRequest, HttpResponse, RemoteAddress } -import akka.http.javadsl.model.headers.Language -import akka.http.javadsl.server.Route -import akka.http.scaladsl.server.{ Directives ⇒ D } - -import akka.http.impl.util.JavaMapping -import akka.http.impl.util.JavaMapping.Implicits._ -import scala.collection.JavaConverters._ - -abstract class TimeoutDirectives extends WebSocketDirectives { - - /** - * Tries to set a new request timeout and handler (if provided) at the same time. - * - * Due to the inherent raciness it is not guaranteed that the update will be applied before - * the previously set timeout has expired! - */ - def withRequestTimeout(timeout: scala.concurrent.duration.Duration, inner: Supplier[Route]): RouteAdapter = RouteAdapter { - D.withRequestTimeout(timeout) { inner.get.delegate } - } - - /** - * Tries to set a new request timeout and handler (if provided) at the same time. - * - * Due to the inherent raciness it is not guaranteed that the update will be applied before - * the previously set timeout has expired! - */ - def withRequestTimeout(timeout: scala.concurrent.duration.Duration, timeoutHandler: JFunction[HttpRequest, HttpResponse], - inner: Supplier[Route]): RouteAdapter = RouteAdapter { - D.withRequestTimeout(timeout, in ⇒ timeoutHandler(in.asJava).asScala) { inner.get.delegate } - } - - def withoutRequestTimeout(inner: Supplier[Route]): RouteAdapter = RouteAdapter { - D.withoutRequestTimeout { inner.get.delegate } - } - - /** - * Tries to set a new request timeout handler, which produces the timeout response for a - * given request. Note that the handler must produce the response synchronously and shouldn't block! - * - * Due to the inherent raciness it is not guaranteed that the update will be applied before - * the previously set timeout has expired! - */ - def withRequestTimeoutResponse(timeoutHandler: JFunction[HttpRequest, HttpResponse], inner: Supplier[Route]): RouteAdapter = RouteAdapter { - D.withRequestTimeoutResponse(in ⇒ timeoutHandler(in.asJava).asScala) { inner.get.delegate } - } - -} diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/directives/WebSocketDirectives.scala b/akka-http/src/main/scala/akka/http/javadsl/server/directives/WebSocketDirectives.scala deleted file mode 100644 index c807744f0f..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/server/directives/WebSocketDirectives.scala +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.server -package directives - -import java.util.{ List ⇒ JList } -import java.util.Optional -import java.util.function.{ Function ⇒ JFunction } - -import akka.NotUsed -import scala.collection.JavaConverters._ -import akka.http.scaladsl.model.{ ws ⇒ s } -import akka.http.javadsl.model.ws.Message -import akka.http.javadsl.model.ws.UpgradeToWebSocket -import akka.http.scaladsl.server.{ Directives ⇒ D } -import akka.stream.javadsl.Flow -import akka.stream.scaladsl - -abstract class WebSocketDirectives extends SecurityDirectives { - import akka.http.impl.util.JavaMapping.Implicits._ - - /** - * Extract the [[UpgradeToWebSocket]] header if existent. Rejects with an [[ExpectedWebSocketRequestRejection]], otherwise. - */ - def extractUpgradeToWebSocket(inner: JFunction[UpgradeToWebSocket, Route]): Route = RouteAdapter { - D.extractUpgradeToWebSocket { header ⇒ - inner.apply(header).delegate - } - } - - /** - * Extract the list of WebSocket subprotocols as offered by the client in the [[Sec-WebSocket-Protocol]] header if - * this is a WebSocket request. Rejects with an [[ExpectedWebSocketRequestRejection]], otherwise. - */ - def extractOfferedWsProtocols(inner: JFunction[JList[String], Route]): Route = RouteAdapter { - D.extractOfferedWsProtocols { list ⇒ - inner.apply(list.asJava).delegate - } - } - - /** - * Handles WebSocket requests with the given handler and rejects other requests with an - * [[ExpectedWebSocketRequestRejection]]. - */ - def handleWebSocketMessages[T](handler: Flow[Message, Message, T]): Route = RouteAdapter { - D.handleWebSocketMessages(adapt(handler)) - } - - /** - * Handles WebSocket requests with the given handler if the given subprotocol is offered in the request and - * rejects other requests with an [[ExpectedWebSocketRequestRejection]] or an [[UnsupportedWebSocketSubprotocolRejection]]. - */ - def handleWebSocketMessagesForProtocol[T](handler: Flow[Message, Message, T], subprotocol: String): Route = RouteAdapter { - D.handleWebSocketMessagesForProtocol(adapt(handler), subprotocol) - } - - /** - * Handles WebSocket requests with the given handler and rejects other requests with an - * [[ExpectedWebSocketRequestRejection]]. - * - * If the `subprotocol` parameter is None any WebSocket request is accepted. If the `subprotocol` parameter is - * `Some(protocol)` a WebSocket request is only accepted if the list of subprotocols supported by the client (as - * announced in the WebSocket request) contains `protocol`. If the client did not offer the protocol in question - * the request is rejected with an [[UnsupportedWebSocketSubprotocolRejection]] rejection. - * - * To support several subprotocols you may chain several `handleWebSocketMessage` Routes. - */ - def handleWebSocketMessagesForOptionalProtocol[T](handler: Flow[Message, Message, T], subprotocol: Optional[String]): Route = RouteAdapter { - D.handleWebSocketMessagesForOptionalProtocol(adapt(handler), subprotocol.asScala) - } - - private def adapt[T](handler: Flow[Message, Message, T]): scaladsl.Flow[s.Message, s.Message, NotUsed] = { - scaladsl.Flow[s.Message].map(_.asJava).via(handler).map(_.asScala) - } -} diff --git a/akka-http/src/main/scala/akka/http/javadsl/unmarshalling/StringUnmarshaller.scala b/akka-http/src/main/scala/akka/http/javadsl/unmarshalling/StringUnmarshaller.scala deleted file mode 100644 index 1a85b25697..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/unmarshalling/StringUnmarshaller.scala +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.unmarshalling - -import java.util.concurrent.CompletionStage - -object StringUnmarshaller { - /** - * Turns the given asynchronous function into an unmarshaller from String to B. - */ - def async[B](f: java.util.function.Function[String, CompletionStage[B]]): Unmarshaller[String, B] = Unmarshaller.async(f) - - /** - * Turns the given function into an unmarshaller from String to B. - */ - def sync[B](f: java.util.function.Function[String, B]): Unmarshaller[String, B] = Unmarshaller.sync(f) -} - -/** - * INTERNAL API - */ -private[unmarshalling] object StringUnmarshallerPredef extends akka.http.scaladsl.unmarshalling.PredefinedFromStringUnmarshallers { - -} - diff --git a/akka-http/src/main/scala/akka/http/javadsl/unmarshalling/Unmarshaller.scala b/akka-http/src/main/scala/akka/http/javadsl/unmarshalling/Unmarshaller.scala deleted file mode 100644 index 368ebe68c3..0000000000 --- a/akka-http/src/main/scala/akka/http/javadsl/unmarshalling/Unmarshaller.scala +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.javadsl.unmarshalling - -import java.util.concurrent.CompletionStage - -import akka.http.impl.util.JavaMapping -import akka.http.impl.util.JavaMapping.Implicits._ -import akka.http.javadsl.model.{ HttpEntity, HttpRequest, MediaType, RequestEntity } -import akka.http.scaladsl.model.{ ContentTypeRange, ContentTypes, FormData, Multipart } -import akka.http.scaladsl.unmarshalling -import akka.http.scaladsl.unmarshalling.FromEntityUnmarshaller -import akka.http.scaladsl.unmarshalling.Unmarshaller.{ EnhancedFromEntityUnmarshaller, UnsupportedContentTypeException } -import akka.http.scaladsl.util.FastFuture -import akka.stream.Materializer -import akka.util.ByteString - -import scala.collection.JavaConverters._ -import scala.compat.java8.FutureConverters._ -import scala.concurrent.ExecutionContext -import scala.language.implicitConversions - -object Unmarshaller { - implicit def fromScala[A, B](scalaUnmarshaller: unmarshalling.Unmarshaller[A, B]): Unmarshaller[A, B] = - scalaUnmarshaller - - /** - * Creates an unmarshaller from an asynchronous Java function. - */ - def async[A, B](f: java.util.function.Function[A, CompletionStage[B]]): Unmarshaller[A, B] = - unmarshalling.Unmarshaller[A, B] { ctx ⇒ a ⇒ f(a).toScala - } - - /** - * Creates an unmarshaller from a Java function. - */ - def sync[A, B](f: java.util.function.Function[A, B]): Unmarshaller[A, B] = - unmarshalling.Unmarshaller[A, B] { ctx ⇒ a ⇒ scala.concurrent.Future.successful(f.apply(a)) - } - - // format: OFF - def entityToByteString: Unmarshaller[HttpEntity, ByteString] = unmarshalling.Unmarshaller.byteStringUnmarshaller - def entityToByteArray: Unmarshaller[HttpEntity, Array[Byte]] = unmarshalling.Unmarshaller.byteArrayUnmarshaller - def entityToCharArray: Unmarshaller[HttpEntity, Array[Char]] = unmarshalling.Unmarshaller.charArrayUnmarshaller - def entityToString: Unmarshaller[HttpEntity, String] = unmarshalling.Unmarshaller.stringUnmarshaller - def entityToUrlEncodedFormData: Unmarshaller[HttpEntity, FormData] = unmarshalling.Unmarshaller.defaultUrlEncodedFormDataUnmarshaller - def entityToMultipartByteRanges: Unmarshaller[HttpEntity, Multipart.ByteRanges] = unmarshalling.MultipartUnmarshallers.defaultMultipartByteRangesUnmarshaller - // format: ON - - val requestToEntity: Unmarshaller[HttpRequest, RequestEntity] = - unmarshalling.Unmarshaller.strict[HttpRequest, RequestEntity](_.entity) - - def forMediaType[B](t: MediaType, um: Unmarshaller[HttpEntity, B]): Unmarshaller[HttpEntity, B] = { - unmarshalling.Unmarshaller.withMaterializer[HttpEntity, B] { implicit ex ⇒ implicit mat ⇒ jEntity ⇒ { - val entity = jEntity.asScala - val mediaType = t.asScala - if (entity.contentType == ContentTypes.NoContentType || mediaType.matches(entity.contentType.mediaType)) { - um.asScala(entity) - } else FastFuture.failed(UnsupportedContentTypeException(ContentTypeRange(t.toRange.asScala))) - } - } - } - - def forMediaTypes[B](types: java.lang.Iterable[MediaType], um: Unmarshaller[HttpEntity, B]): Unmarshaller[HttpEntity, B] = { - val u: FromEntityUnmarshaller[B] = um.asScala - val theTypes: Seq[akka.http.scaladsl.model.ContentTypeRange] = types.asScala.toSeq.map { media ⇒ - akka.http.scaladsl.model.ContentTypeRange(media.asScala) - } - u.forContentTypes(theTypes: _*) - } - - def firstOf[A, B](u1: Unmarshaller[A, B], u2: Unmarshaller[A, B]): Unmarshaller[A, B] = { - unmarshalling.Unmarshaller.firstOf(u1.asScala, u2.asScala) - } - - def firstOf[A, B](u1: Unmarshaller[A, B], u2: Unmarshaller[A, B], u3: Unmarshaller[A, B]): Unmarshaller[A, B] = { - unmarshalling.Unmarshaller.firstOf(u1.asScala, u2.asScala, u3.asScala) - } - - def firstOf[A, B](u1: Unmarshaller[A, B], u2: Unmarshaller[A, B], u3: Unmarshaller[A, B], u4: Unmarshaller[A, B]): Unmarshaller[A, B] = { - unmarshalling.Unmarshaller.firstOf(u1.asScala, u2.asScala, u3.asScala, u4.asScala) - } - - def firstOf[A, B](u1: Unmarshaller[A, B], u2: Unmarshaller[A, B], u3: Unmarshaller[A, B], u4: Unmarshaller[A, B], u5: Unmarshaller[A, B]): Unmarshaller[A, B] = { - unmarshalling.Unmarshaller.firstOf(u1.asScala, u2.asScala, u3.asScala, u4.asScala, u5.asScala) - } - - private implicit def adaptInputToJava[JI, SI, O](um: unmarshalling.Unmarshaller[SI, O])(implicit mi: JavaMapping[JI, SI]): unmarshalling.Unmarshaller[JI, O] = - um.asInstanceOf[unmarshalling.Unmarshaller[JI, O]] // since guarantee provided by existence of `mi` - -} - -trait UnmarshallerBase[-A, B] - -/** - * An unmarshaller transforms values of type A into type B. - */ -abstract class Unmarshaller[-A, B] extends UnmarshallerBase[A, B] { - - implicit def asScala: akka.http.scaladsl.unmarshalling.Unmarshaller[A, B] - - /** INTERNAL API */ - private[akka] def asScalaCastInput[I]: unmarshalling.Unmarshaller[I, B] = asScala.asInstanceOf[unmarshalling.Unmarshaller[I, B]] - - def unmarshall(a: A, ec: ExecutionContext, mat: Materializer): CompletionStage[B] = asScala.apply(a)(ec, mat).toJava - - /** - * Transform the result `B` of this unmarshaller to a `C` producing a marshaller that turns `A`s into `C`s - * - * @return A new marshaller that can unmarshall instances of `A` into instances of `C` - */ - def thenApply[C](f: java.util.function.Function[B, C]): Unmarshaller[A, C] = asScala.map(f.apply) - - def flatMap[C](f: java.util.function.Function[B, CompletionStage[C]]): Unmarshaller[A, C] = - asScala.flatMap { ctx ⇒ mat ⇒ b ⇒ f.apply(b).toScala } - - def flatMap[C](u: Unmarshaller[_ >: B, C]): Unmarshaller[A, C] = - asScala.flatMap { ctx ⇒ mat ⇒ b ⇒ u.asScala.apply(b)(ctx, mat) } - - // TODO not exposed for Java yet - // def mapWithInput[C](f: java.util.function.BiFunction[A, B, C]): Unmarshaller[A, C] = - // asScala.mapWithInput { case (a, b) ⇒ f.apply(a, b) } - // - // def flatMapWithInput[C](f: java.util.function.BiFunction[A, B, CompletionStage[C]]): Unmarshaller[A, C] = - // asScala.flatMapWithInput { case (a, b) ⇒ f.apply(a, b).toScala } -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/client/RequestBuilding.scala b/akka-http/src/main/scala/akka/http/scaladsl/client/RequestBuilding.scala deleted file mode 100644 index e07f4f25d6..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/client/RequestBuilding.scala +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.client - -import scala.collection.immutable -import scala.concurrent.{ Await, ExecutionContext } -import scala.concurrent.duration._ -import scala.reflect.ClassTag -import akka.util.Timeout -import akka.event.{ Logging, LoggingAdapter } -import akka.http.scaladsl.marshalling._ -import akka.http.scaladsl.model._ -import headers.HttpCredentials -import HttpMethods._ - -trait RequestBuilding extends TransformerPipelineSupport { - type RequestTransformer = HttpRequest ⇒ HttpRequest - - class RequestBuilder(val method: HttpMethod) { - def apply(): HttpRequest = - apply("/") - - def apply(uri: String): HttpRequest = - apply(uri, HttpEntity.Empty) - - def apply[T](uri: String, content: T)(implicit m: ToEntityMarshaller[T], ec: ExecutionContext): HttpRequest = - apply(uri, Some(content)) - - def apply[T](uri: String, content: Option[T])(implicit m: ToEntityMarshaller[T], ec: ExecutionContext): HttpRequest = - apply(Uri(uri), content) - - def apply(uri: String, entity: RequestEntity): HttpRequest = - apply(Uri(uri), entity) - - def apply(uri: Uri): HttpRequest = - apply(uri, HttpEntity.Empty) - - def apply[T](uri: Uri, content: T)(implicit m: ToEntityMarshaller[T], ec: ExecutionContext): HttpRequest = - apply(uri, Some(content)) - - def apply[T](uri: Uri, content: Option[T])(implicit m: ToEntityMarshaller[T], timeout: Timeout = Timeout(1.second), ec: ExecutionContext): HttpRequest = - content match { - case None ⇒ apply(uri, HttpEntity.Empty) - case Some(value) ⇒ - val entity = Await.result(Marshal(value).to[RequestEntity], timeout.duration) - apply(uri, entity) - } - - def apply(uri: Uri, entity: RequestEntity): HttpRequest = - HttpRequest(method, uri, Nil, entity) - } - - val Get = new RequestBuilder(GET) - val Post = new RequestBuilder(POST) - val Put = new RequestBuilder(PUT) - val Patch = new RequestBuilder(PATCH) - val Delete = new RequestBuilder(DELETE) - val Options = new RequestBuilder(OPTIONS) - val Head = new RequestBuilder(HEAD) - - // TODO: reactivate after HTTP message encoding has been ported - //def encode(encoder: Encoder): RequestTransformer = encoder.encode(_, flow) - - def addHeader(header: HttpHeader): RequestTransformer = _.mapHeaders(header +: _) - - def addHeader(headerName: String, headerValue: String): RequestTransformer = - HttpHeader.parse(headerName, headerValue) match { - case HttpHeader.ParsingResult.Ok(h, Nil) ⇒ addHeader(h) - case result ⇒ throw new IllegalArgumentException(result.errors.head.formatPretty) - } - - def addHeaders(first: HttpHeader, more: HttpHeader*): RequestTransformer = _.mapHeaders(_ ++ (first +: more)) - - def mapHeaders(f: immutable.Seq[HttpHeader] ⇒ immutable.Seq[HttpHeader]): RequestTransformer = _.mapHeaders(f) - - def removeHeader(headerName: String): RequestTransformer = - _ mapHeaders (_ filterNot (_.name equalsIgnoreCase headerName)) - - def removeHeader[T <: HttpHeader: ClassTag]: RequestTransformer = - removeHeader(implicitly[ClassTag[T]].runtimeClass) - - def removeHeader(clazz: Class[_]): RequestTransformer = - _ mapHeaders (_ filterNot clazz.isInstance) - - def removeHeaders(names: String*): RequestTransformer = - _ mapHeaders (_ filterNot (header ⇒ names exists (_ equalsIgnoreCase header.name))) - - def addCredentials(credentials: HttpCredentials) = addHeader(headers.Authorization(credentials)) - - def logRequest(log: LoggingAdapter, level: Logging.LogLevel = Logging.DebugLevel) = logValue[HttpRequest](log, level) - - def logRequest(logFun: HttpRequest ⇒ Unit) = logValue[HttpRequest](logFun) - - implicit def header2AddHeader(header: HttpHeader): RequestTransformer = addHeader(header) -} - -object RequestBuilding extends RequestBuilding \ No newline at end of file diff --git a/akka-http/src/main/scala/akka/http/scaladsl/client/TransformerPipelineSupport.scala b/akka-http/src/main/scala/akka/http/scaladsl/client/TransformerPipelineSupport.scala deleted file mode 100644 index 585cbacea3..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/client/TransformerPipelineSupport.scala +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.client - -import scala.concurrent.{ Future, ExecutionContext } -import akka.event.{ Logging, LoggingAdapter } - -trait TransformerPipelineSupport { - - def logValue[T](log: LoggingAdapter, level: Logging.LogLevel = Logging.DebugLevel): T ⇒ T = - logValue { value ⇒ log.log(level, value.toString) } - - def logValue[T](logFun: T ⇒ Unit): T ⇒ T = { response ⇒ - logFun(response) - response - } - - implicit class WithTransformation[A](value: A) { - def ~>[B](f: A ⇒ B): B = f(value) - } - - implicit class WithTransformerConcatenation[A, B](f: A ⇒ B) extends (A ⇒ B) { - def apply(input: A) = f(input) - def ~>[AA, BB, R](g: AA ⇒ BB)(implicit aux: TransformerAux[A, B, AA, BB, R]) = - new WithTransformerConcatenation[A, R](aux(f, g)) - } -} - -object TransformerPipelineSupport extends TransformerPipelineSupport - -trait TransformerAux[A, B, AA, BB, R] { - def apply(f: A ⇒ B, g: AA ⇒ BB): A ⇒ R -} - -object TransformerAux { - implicit def aux1[A, B, C]: TransformerAux[A, B, B, C, C] = new TransformerAux[A, B, B, C, C] { - def apply(f: A ⇒ B, g: B ⇒ C): A ⇒ C = f andThen g - } - implicit def aux2[A, B, C](implicit ec: ExecutionContext): TransformerAux[A, Future[B], B, C, Future[C]] = - new TransformerAux[A, Future[B], B, C, Future[C]] { - def apply(f: A ⇒ Future[B], g: B ⇒ C): A ⇒ Future[C] = f(_).map(g) - } - implicit def aux3[A, B, C](implicit ec: ExecutionContext): TransformerAux[A, Future[B], B, Future[C], Future[C]] = - new TransformerAux[A, Future[B], B, Future[C], Future[C]] { - def apply(f: A ⇒ Future[B], g: B ⇒ Future[C]): A ⇒ Future[C] = f(_).flatMap(g) - } -} \ No newline at end of file diff --git a/akka-http/src/main/scala/akka/http/scaladsl/coding/Coder.scala b/akka-http/src/main/scala/akka/http/scaladsl/coding/Coder.scala deleted file mode 100644 index b1186a2e30..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/coding/Coder.scala +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.coding - -/** Marker trait for A combined Encoder and Decoder */ -trait Coder extends Encoder with Decoder diff --git a/akka-http/src/main/scala/akka/http/scaladsl/coding/DataMapper.scala b/akka-http/src/main/scala/akka/http/scaladsl/coding/DataMapper.scala deleted file mode 100644 index 6a9bf60d42..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/coding/DataMapper.scala +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.coding - -import akka.http.scaladsl.model.{ HttpRequest, HttpResponse, ResponseEntity, RequestEntity } -import akka.util.ByteString -import akka.stream.scaladsl.Flow - -/** An abstraction to transform data bytes of HttpMessages or HttpEntities */ -sealed trait DataMapper[T] { - def transformDataBytes(t: T, transformer: Flow[ByteString, ByteString, _]): T -} -object DataMapper { - implicit val mapRequestEntity: DataMapper[RequestEntity] = - new DataMapper[RequestEntity] { - def transformDataBytes(t: RequestEntity, transformer: Flow[ByteString, ByteString, _]): RequestEntity = - t.transformDataBytes(transformer) - } - implicit val mapResponseEntity: DataMapper[ResponseEntity] = - new DataMapper[ResponseEntity] { - def transformDataBytes(t: ResponseEntity, transformer: Flow[ByteString, ByteString, _]): ResponseEntity = - t.transformDataBytes(transformer) - } - - implicit val mapRequest: DataMapper[HttpRequest] = mapMessage(mapRequestEntity)((m, f) ⇒ m.withEntity(f(m.entity))) - implicit val mapResponse: DataMapper[HttpResponse] = mapMessage(mapResponseEntity)((m, f) ⇒ m.withEntity(f(m.entity))) - - def mapMessage[T, E](entityMapper: DataMapper[E])(mapEntity: (T, E ⇒ E) ⇒ T): DataMapper[T] = - new DataMapper[T] { - def transformDataBytes(t: T, transformer: Flow[ByteString, ByteString, _]): T = - mapEntity(t, entityMapper.transformDataBytes(_, transformer)) - } -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/coding/Decoder.scala b/akka-http/src/main/scala/akka/http/scaladsl/coding/Decoder.scala deleted file mode 100644 index d91ecdc2c6..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/coding/Decoder.scala +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.coding - -import akka.NotUsed -import akka.http.scaladsl.model._ -import akka.stream.{ FlowShape, Materializer } -import akka.stream.stage.{ GraphStage } -import akka.util.ByteString -import headers.HttpEncoding -import akka.stream.scaladsl.{ Sink, Source, Flow } - -import scala.concurrent.Future - -trait Decoder { - def encoding: HttpEncoding - - def decode[T <: HttpMessage](message: T)(implicit mapper: DataMapper[T]): T#Self = - if (message.headers exists Encoder.isContentEncodingHeader) - decodeData(message).withHeaders(message.headers filterNot Encoder.isContentEncodingHeader) - else message.self - - def decodeData[T](t: T)(implicit mapper: DataMapper[T]): T = mapper.transformDataBytes(t, decoderFlow) - - def maxBytesPerChunk: Int - def withMaxBytesPerChunk(maxBytesPerChunk: Int): Decoder - - def decoderFlow: Flow[ByteString, ByteString, NotUsed] - def decode(input: ByteString)(implicit mat: Materializer): Future[ByteString] = - Source.single(input).via(decoderFlow).runWith(Sink.fold(ByteString.empty)(_ ++ _)) -} -object Decoder { - val MaxBytesPerChunkDefault: Int = 65536 -} - -/** A decoder that is implemented in terms of a [[Stage]] */ -trait StreamDecoder extends Decoder { outer ⇒ - protected def newDecompressorStage(maxBytesPerChunk: Int): () ⇒ GraphStage[FlowShape[ByteString, ByteString]] - - def maxBytesPerChunk: Int = Decoder.MaxBytesPerChunkDefault - def withMaxBytesPerChunk(newMaxBytesPerChunk: Int): Decoder = - new StreamDecoder { - def encoding: HttpEncoding = outer.encoding - override def maxBytesPerChunk: Int = newMaxBytesPerChunk - - def newDecompressorStage(maxBytesPerChunk: Int): () ⇒ GraphStage[FlowShape[ByteString, ByteString]] = - outer.newDecompressorStage(maxBytesPerChunk) - } - - def decoderFlow: Flow[ByteString, ByteString, NotUsed] = - Flow.fromGraph(newDecompressorStage(maxBytesPerChunk)()) - -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/coding/Deflate.scala b/akka-http/src/main/scala/akka/http/scaladsl/coding/Deflate.scala deleted file mode 100644 index 348266692a..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/coding/Deflate.scala +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.coding - -import java.util.zip.{ Inflater, Deflater } -import akka.stream.Attributes -import akka.stream.impl.io.ByteStringParser -import ByteStringParser.{ ParseResult, ParseStep } -import akka.util.{ ByteStringBuilder, ByteString } - -import scala.annotation.tailrec -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers.HttpEncodings - -class Deflate(val messageFilter: HttpMessage ⇒ Boolean) extends Coder with StreamDecoder { - val encoding = HttpEncodings.deflate - def newCompressor = new DeflateCompressor - def newDecompressorStage(maxBytesPerChunk: Int) = () ⇒ new DeflateDecompressor(maxBytesPerChunk) -} -object Deflate extends Deflate(Encoder.DefaultFilter) - -class DeflateCompressor extends Compressor { - import DeflateCompressor._ - - protected lazy val deflater = new Deflater(Deflater.BEST_COMPRESSION, false) - - override final def compressAndFlush(input: ByteString): ByteString = { - val buffer = newTempBuffer(input.size) - - compressWithBuffer(input, buffer) ++ flushWithBuffer(buffer) - } - override final def compressAndFinish(input: ByteString): ByteString = { - val buffer = newTempBuffer(input.size) - - compressWithBuffer(input, buffer) ++ finishWithBuffer(buffer) - } - override final def compress(input: ByteString): ByteString = compressWithBuffer(input, newTempBuffer()) - override final def flush(): ByteString = flushWithBuffer(newTempBuffer()) - override final def finish(): ByteString = finishWithBuffer(newTempBuffer()) - - protected def compressWithBuffer(input: ByteString, buffer: Array[Byte]): ByteString = { - require(deflater.needsInput()) - deflater.setInput(input.toArray) - drainDeflater(deflater, buffer) - } - protected def flushWithBuffer(buffer: Array[Byte]): ByteString = { - val written = deflater.deflate(buffer, 0, buffer.length, Deflater.SYNC_FLUSH) - ByteString.fromArray(buffer, 0, written) - } - protected def finishWithBuffer(buffer: Array[Byte]): ByteString = { - deflater.finish() - val res = drainDeflater(deflater, buffer) - deflater.end() - res - } - - private def newTempBuffer(size: Int = 65536): Array[Byte] = { - // The default size is somewhat arbitrary, we'd like to guess a better value but Deflater/zlib - // is buffering in an unpredictable manner. - // `compress` will only return any data if the buffered compressed data has some size in - // the region of 10000-50000 bytes. - // `flush` and `finish` will return any size depending on the previous input. - // This value will hopefully provide a good compromise between memory churn and - // excessive fragmentation of ByteStrings. - // We also make sure that buffer size stays within a reasonable range, to avoid - // draining deflator with too small buffer. - new Array[Byte](math.max(size, MinBufferSize)) - } -} - -private[http] object DeflateCompressor { - val MinBufferSize = 1024 - - @tailrec - def drainDeflater(deflater: Deflater, buffer: Array[Byte], result: ByteStringBuilder = new ByteStringBuilder()): ByteString = { - val len = deflater.deflate(buffer) - if (len > 0) { - result ++= ByteString.fromArray(buffer, 0, len) - drainDeflater(deflater, buffer, result) - } else { - require(deflater.needsInput()) - result.result() - } - } -} - -class DeflateDecompressor(maxBytesPerChunk: Int = Decoder.MaxBytesPerChunkDefault) extends DeflateDecompressorBase(maxBytesPerChunk) { - - override def createLogic(attr: Attributes) = new DecompressorParsingLogic { - override val inflater: Inflater = new Inflater() - - override val inflateState = new Inflate(true) { - override def onTruncation(): Unit = completeStage() - } - - override def afterInflate = inflateState - override def afterBytesRead(buffer: Array[Byte], offset: Int, length: Int): Unit = {} - - startWith(inflateState) - } -} - -abstract class DeflateDecompressorBase(maxBytesPerChunk: Int = Decoder.MaxBytesPerChunkDefault) - extends ByteStringParser[ByteString] { - - abstract class DecompressorParsingLogic extends ParsingLogic { - val inflater: Inflater - def afterInflate: ParseStep[ByteString] - def afterBytesRead(buffer: Array[Byte], offset: Int, length: Int): Unit - val inflateState: Inflate - - abstract class Inflate(noPostProcessing: Boolean) extends ParseStep[ByteString] { - override def canWorkWithPartialData = true - override def parse(reader: ByteStringParser.ByteReader): ParseResult[ByteString] = { - inflater.setInput(reader.remainingData.toArray) - - val buffer = new Array[Byte](maxBytesPerChunk) - val read = inflater.inflate(buffer) - - reader.skip(reader.remainingSize - inflater.getRemaining) - - if (read > 0) { - afterBytesRead(buffer, 0, read) - val next = if (inflater.finished()) afterInflate else this - ParseResult(Some(ByteString.fromArray(buffer, 0, read)), next, noPostProcessing) - } else { - if (inflater.finished()) ParseResult(None, afterInflate, noPostProcessing) - else throw ByteStringParser.NeedMoreData - } - } - } - } -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/coding/Encoder.scala b/akka-http/src/main/scala/akka/http/scaladsl/coding/Encoder.scala deleted file mode 100644 index 0f6a0e2af1..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/coding/Encoder.scala +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.coding - -import akka.NotUsed -import akka.http.scaladsl.model._ -import akka.http.impl.util.StreamUtils -import akka.stream.FlowShape -import akka.stream.stage.GraphStage -import akka.util.ByteString -import headers._ -import akka.stream.scaladsl.Flow - -trait Encoder { - def encoding: HttpEncoding - - def messageFilter: HttpMessage ⇒ Boolean - - def encode[T <: HttpMessage](message: T)(implicit mapper: DataMapper[T]): T#Self = - if (messageFilter(message) && !message.headers.exists(Encoder.isContentEncodingHeader)) - encodeData(message).withHeaders(`Content-Encoding`(encoding) +: message.headers) - else message.self - - def encodeData[T](t: T)(implicit mapper: DataMapper[T]): T = - mapper.transformDataBytes(t, Flow[ByteString].via(newEncodeTransformer)) - - def encode(input: ByteString): ByteString = newCompressor.compressAndFinish(input) - - def encoderFlow: Flow[ByteString, ByteString, NotUsed] = Flow[ByteString].via(newEncodeTransformer) - - def newCompressor: Compressor - - def newEncodeTransformer(): GraphStage[FlowShape[ByteString, ByteString]] = { - val compressor = newCompressor - - def encodeChunk(bytes: ByteString): ByteString = compressor.compressAndFlush(bytes) - def finish(): ByteString = compressor.finish() - - StreamUtils.byteStringTransformer(encodeChunk, finish) - } -} - -object Encoder { - val DefaultFilter: HttpMessage ⇒ Boolean = { - case req: HttpRequest ⇒ isCompressible(req) - case res @ HttpResponse(status, _, _, _) ⇒ isCompressible(res) && status.allowsEntity - } - private[coding] def isCompressible(msg: HttpMessage): Boolean = - msg.entity.contentType.mediaType.isCompressible - - private[coding] val isContentEncodingHeader: HttpHeader ⇒ Boolean = _.isInstanceOf[`Content-Encoding`] -} - -/** A stateful object representing ongoing compression. */ -abstract class Compressor { - /** - * Compresses the given input and returns compressed data. The implementation - * can and will choose to buffer output data to improve compression. Use - * `flush` or `compressAndFlush` to make sure that all input data has been - * compressed and pending output data has been returned. - */ - def compress(input: ByteString): ByteString - - /** - * Flushes any output data and returns the currently remaining compressed data. - */ - def flush(): ByteString - - /** - * Closes this compressed stream and return the remaining compressed data. After - * calling this method, this Compressor cannot be used any further. - */ - def finish(): ByteString - - /** Combines `compress` + `flush` */ - def compressAndFlush(input: ByteString): ByteString - /** Combines `compress` + `finish` */ - def compressAndFinish(input: ByteString): ByteString -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/coding/Gzip.scala b/akka-http/src/main/scala/akka/http/scaladsl/coding/Gzip.scala deleted file mode 100644 index 59c4dd90c6..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/coding/Gzip.scala +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.coding - -import java.util.zip.{ CRC32, Deflater, Inflater, ZipException } - -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers.HttpEncodings -import akka.stream.Attributes -import akka.stream.impl.io.ByteStringParser -import ByteStringParser.{ ParseResult, ParseStep } -import akka.util.ByteString - -class Gzip(val messageFilter: HttpMessage ⇒ Boolean) extends Coder with StreamDecoder { - val encoding = HttpEncodings.gzip - def newCompressor = new GzipCompressor - def newDecompressorStage(maxBytesPerChunk: Int) = () ⇒ new GzipDecompressor(maxBytesPerChunk) -} - -/** - * An encoder and decoder for the HTTP 'gzip' encoding. - */ -object Gzip extends Gzip(Encoder.DefaultFilter) { - def apply(messageFilter: HttpMessage ⇒ Boolean) = new Gzip(messageFilter) -} - -class GzipCompressor extends DeflateCompressor { - override protected lazy val deflater = new Deflater(Deflater.BEST_COMPRESSION, true) - private val checkSum = new CRC32 // CRC32 of uncompressed data - private var headerSent = false - private var bytesRead = 0L - - override protected def compressWithBuffer(input: ByteString, buffer: Array[Byte]): ByteString = { - updateCrc(input) - header() ++ super.compressWithBuffer(input, buffer) - } - override protected def flushWithBuffer(buffer: Array[Byte]): ByteString = header() ++ super.flushWithBuffer(buffer) - override protected def finishWithBuffer(buffer: Array[Byte]): ByteString = header() ++ super.finishWithBuffer(buffer) ++ trailer() - - private def updateCrc(input: ByteString): Unit = { - checkSum.update(input.toArray) - bytesRead += input.length - } - private def header(): ByteString = - if (!headerSent) { - headerSent = true - GzipDecompressor.Header - } else ByteString.empty - - private def trailer(): ByteString = { - def int32(i: Int): ByteString = ByteString(i, i >> 8, i >> 16, i >> 24) - val crc = checkSum.getValue.toInt - val tot = bytesRead.toInt // truncated to 32bit as specified in https://tools.ietf.org/html/rfc1952#section-2 - val trailer = int32(crc) ++ int32(tot) - - trailer - } -} - -class GzipDecompressor(maxBytesPerChunk: Int = Decoder.MaxBytesPerChunkDefault) extends DeflateDecompressorBase(maxBytesPerChunk) { - override def createLogic(attr: Attributes) = new DecompressorParsingLogic { - override val inflater: Inflater = new Inflater(true) - override def afterInflate: ParseStep[ByteString] = ReadTrailer - override def afterBytesRead(buffer: Array[Byte], offset: Int, length: Int): Unit = - crc32.update(buffer, offset, length) - - trait Step extends ParseStep[ByteString] { - override def onTruncation(): Unit = failStage(new ZipException("Truncated GZIP stream")) - } - override val inflateState = new Inflate(false) with Step - startWith(ReadHeaders) - - /** Reading the header bytes */ - case object ReadHeaders extends Step { - override def parse(reader: ByteStringParser.ByteReader): ParseResult[ByteString] = { - import reader._ - if (readByte() != 0x1F || readByte() != 0x8B) fail("Not in GZIP format") // check magic header - if (readByte() != 8) fail("Unsupported GZIP compression method") // check compression method - val flags = readByte() - skip(6) // skip MTIME, XFL and OS fields - if ((flags & 4) > 0) skip(readShortLE()) // skip optional extra fields - if ((flags & 8) > 0) skipZeroTerminatedString() // skip optional file name - if ((flags & 16) > 0) skipZeroTerminatedString() // skip optional file comment - if ((flags & 2) > 0 && crc16(fromStartToHere) != readShortLE()) fail("Corrupt GZIP header") - - inflater.reset() - crc32.reset() - ParseResult(None, inflateState, false) - } - } - var crc32: CRC32 = new CRC32 - private def fail(msg: String) = throw new ZipException(msg) - - /** Reading the trailer */ - case object ReadTrailer extends Step { - override def parse(reader: ByteStringParser.ByteReader): ParseResult[ByteString] = { - import reader._ - if (readIntLE() != crc32.getValue.toInt) fail("Corrupt data (CRC32 checksum error)") - if (readIntLE() != inflater.getBytesWritten.toInt /* truncated to 32bit */ ) - fail("Corrupt GZIP trailer ISIZE") - ParseResult(None, ReadHeaders, true) - } - } - } - private def crc16(data: ByteString) = { - val crc = new CRC32 - crc.update(data.toArray) - crc.getValue.toInt & 0xFFFF - } -} - -/** INTERNAL API */ -private[http] object GzipDecompressor { - // RFC 1952: http://tools.ietf.org/html/rfc1952 section 2.2 - val Header = ByteString( - 0x1F, // ID1 - 0x8B, // ID2 - 8, // CM = Deflate - 0, // FLG - 0, // MTIME 1 - 0, // MTIME 2 - 0, // MTIME 3 - 0, // MTIME 4 - 0, // XFL - 0 // OS - ) -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/coding/NoCoding.scala b/akka-http/src/main/scala/akka/http/scaladsl/coding/NoCoding.scala deleted file mode 100644 index 6e74ad9ae4..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/coding/NoCoding.scala +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.coding - -import akka.http.scaladsl.model._ -import akka.http.impl.util.StreamUtils -import akka.stream.FlowShape -import akka.stream.stage.{ GraphStage } -import akka.util.ByteString -import headers.HttpEncodings - -/** - * An encoder and decoder for the HTTP 'identity' encoding. - */ -object NoCoding extends Coder with StreamDecoder { - val encoding = HttpEncodings.identity - - override def encode[T <: HttpMessage](message: T)(implicit mapper: DataMapper[T]): T#Self = message.self - override def encodeData[T](t: T)(implicit mapper: DataMapper[T]): T = t - override def decode[T <: HttpMessage](message: T)(implicit mapper: DataMapper[T]): T#Self = message.self - override def decodeData[T](t: T)(implicit mapper: DataMapper[T]): T = t - - val messageFilter: HttpMessage ⇒ Boolean = _ ⇒ false - - def newCompressor = NoCodingCompressor - - def newDecompressorStage(maxBytesPerChunk: Int): () ⇒ GraphStage[FlowShape[ByteString, ByteString]] = - () ⇒ StreamUtils.limitByteChunksStage(maxBytesPerChunk) -} - -object NoCodingCompressor extends Compressor { - def compress(input: ByteString): ByteString = input - def flush() = ByteString.empty - def finish() = ByteString.empty - - def compressAndFlush(input: ByteString): ByteString = input - def compressAndFinish(input: ByteString): ByteString = input -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/common/CsvEntityStreamingSupport.scala b/akka-http/src/main/scala/akka/http/scaladsl/common/CsvEntityStreamingSupport.scala deleted file mode 100644 index 536a86f003..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/common/CsvEntityStreamingSupport.scala +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2016 Lightbend Inc. - */ - -package akka.http.scaladsl.common - -import akka.NotUsed -import akka.event.Logging -import akka.http.javadsl.{ common, model ⇒ jm } -import akka.http.scaladsl.model.{ ContentType, ContentTypeRange, ContentTypes } -import akka.stream.scaladsl.{ Flow, Framing } -import akka.util.ByteString - -final class CsvEntityStreamingSupport private[akka] ( - maxLineLength: Int, - val supported: ContentTypeRange, - val contentType: ContentType, - val framingRenderer: Flow[ByteString, ByteString, NotUsed], - val parallelism: Int, - val unordered: Boolean -) extends common.CsvEntityStreamingSupport { - import akka.http.impl.util.JavaMapping.Implicits._ - - def this(maxObjectSize: Int) = - this( - maxObjectSize, - ContentTypeRange(ContentTypes.`text/csv(UTF-8)`), - ContentTypes.`text/csv(UTF-8)`, - Flow[ByteString].intersperse(ByteString("\n")), - 1, false) - - override val framingDecoder: Flow[ByteString, ByteString, NotUsed] = - Framing.delimiter(ByteString("\n"), maxLineLength) - - override def withFramingRendererFlow(framingRendererFlow: akka.stream.javadsl.Flow[ByteString, ByteString, NotUsed]): CsvEntityStreamingSupport = - withFramingRenderer(framingRendererFlow.asScala) - def withFramingRenderer(framingRendererFlow: Flow[ByteString, ByteString, NotUsed]): CsvEntityStreamingSupport = - new CsvEntityStreamingSupport(maxLineLength, supported, contentType, framingRendererFlow, parallelism, unordered) - - override def withContentType(ct: jm.ContentType): CsvEntityStreamingSupport = - new CsvEntityStreamingSupport(maxLineLength, supported, ct.asScala, framingRenderer, parallelism, unordered) - override def withSupported(range: jm.ContentTypeRange): CsvEntityStreamingSupport = - new CsvEntityStreamingSupport(maxLineLength, range.asScala, contentType, framingRenderer, parallelism, unordered) - override def withParallelMarshalling(parallelism: Int, unordered: Boolean): CsvEntityStreamingSupport = - new CsvEntityStreamingSupport(maxLineLength, supported, contentType, framingRenderer, parallelism, unordered) - - override def toString = s"""${Logging.simpleName(getClass)}($maxLineLength, $supported, $contentType)""" -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/common/EntityStreamingSupport.scala b/akka-http/src/main/scala/akka/http/scaladsl/common/EntityStreamingSupport.scala deleted file mode 100644 index aea219666f..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/common/EntityStreamingSupport.scala +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) 2016 Lightbend Inc. - */ -package akka.http.scaladsl.common - -import akka.NotUsed -import akka.http.javadsl.{ common, model ⇒ jm } -import akka.http.scaladsl.model._ -import akka.stream.scaladsl.Flow -import akka.util.ByteString - -/** - * Entity streaming support trait allowing rendering and receiving incoming ``Source[T, _]`` from HTTP entities. - * - * See [[JsonEntityStreamingSupport]] or [[CsvEntityStreamingSupport]] for default implementations. - */ -abstract class EntityStreamingSupport extends common.EntityStreamingSupport { - /** Read-side, what content types it is able to frame and unmarshall. */ - def supported: ContentTypeRange - /** Write-side, defines what Content-Type the Marshaller should offer and the final Content-Type of the response. */ - def contentType: ContentType - - /** - * Read-side, decode incoming framed entity. - * For example with an incoming JSON array, chunk it up into JSON objects contained within that array. - */ - def framingDecoder: Flow[ByteString, ByteString, NotUsed] - override final def getFramingDecoder = framingDecoder.asJava - - /** - * Write-side, apply framing to outgoing entity stream. - * - * Most typical usage will be a variant of `Flow[ByteString].intersperse`. - * - * For example for rendering a JSON array one would return - * `Flow[ByteString].intersperse(ByteString("["), ByteString(","), ByteString("]"))` - * and for rendering a new-line separated CSV simply `Flow[ByteString].intersperse(ByteString("\n"))`. - */ - def framingRenderer: Flow[ByteString, ByteString, NotUsed] - override final def getFramingRenderer = framingRenderer.asJava - - /** - * Read-side, allows changing what content types are accepted by this framing. - * - * EntityStreamingSupport traits MUST support re-configuring the accepted [[ContentTypeRange]]. - * - * This is in order to support a-typical APIs which users still want to communicate with using - * the provided support trait. Typical examples include APIs which return valid `application/json` - * however advertise the content type as being `application/javascript` or vendor specific content types, - * which still parse correctly as JSON, CSV or something else that a provided support trait is built for. - * - * NOTE: Implementations should specialize the return type to their own Type! - */ - override def withSupported(range: jm.ContentTypeRange): EntityStreamingSupport - - /** - * Write-side, defines what Content-Type the Marshaller should offer and the final Content-Type of the response. - * - * EntityStreamingSupport traits MUST support re-configuring the offered [[ContentType]]. - * This is due to the need integrating with existing systems which sometimes excpect custom Content-Types, - * however really are just plain JSON or something else internally (perhaps with slight extensions). - * - * NOTE: Implementations should specialize the return type to their own Type! - */ - override def withContentType(range: jm.ContentType): EntityStreamingSupport - - /** - * Write-side / read-side, defines if (un)marshalling should be done in parallel. - * - * This may be beneficial marshalling the bottleneck in the pipeline. - * - * See also [[parallelism]] and [[withParallelMarshalling]]. - */ - def parallelism: Int - - /** - * Write-side / read-side, defines if (un)marshalling of incoming stream elements should be perserved or not. - * - * Allowing for parallel and unordered (un)marshalling often yields higher throughput and also allows avoiding - * head-of-line blocking if some elements are much larger than others. - * - * See also [[parallelism]] and [[withParallelMarshalling]]. - */ - def unordered: Boolean - - /** - * Write-side / read-side, defines parallelism and if ordering should be preserved or not of Source element marshalling. - * - * Sometimes marshalling multiple elements at once (esp. when elements are not evenly sized, and ordering is not enforced) - * may yield in higher throughput. - * - * NOTE: Implementations should specialize the return type to their own Type! - */ - def withParallelMarshalling(parallelism: Int, unordered: Boolean): EntityStreamingSupport - -} - -/** - * Entity streaming support, independent of used Json parsing library etc. - */ -object EntityStreamingSupport { - - /** - * Default `application/json` entity streaming support. - * - * Provides framing (based on scanning the incoming dataBytes for valid JSON objects, so for example uploads using arrays or - * new-line separated JSON objects are all parsed correctly) and rendering of Sources as JSON Arrays. - * A different very popular style of returning streaming JSON is to separate JSON objects on a line-by-line basis, - * you can configure the support trait to do so by calling `withFramingRendererFlow`. - * - * Limits the maximum JSON object length to 8KB, if you want to increase this limit provide a value explicitly. - * - * See also https://en.wikipedia.org/wiki/JSON_Streaming - */ - def json(): JsonEntityStreamingSupport = json(8 * 1024) - /** - * Default `application/json` entity streaming support. - * - * Provides framing (based on scanning the incoming dataBytes for valid JSON objects, so for example uploads using arrays or - * new-line separated JSON objects are all parsed correctly) and rendering of Sources as JSON Arrays. - * A different very popular style of returning streaming JSON is to separate JSON objects on a line-by-line basis, - * you can configure the support trait to do so by calling `withFramingRendererFlow`. - * - * See also https://en.wikipedia.org/wiki/JSON_Streaming - */ - def json(maxObjectLength: Int): JsonEntityStreamingSupport = new JsonEntityStreamingSupport(maxObjectLength) - - /** - * Default `text/csv(UTF-8)` entity streaming support. - * Provides framing and rendering of `\n` separated lines and marshalling Sources into such values. - * - * Limits the maximum line-length to 8KB, if you want to increase this limit provide a value explicitly. - */ - def csv(): CsvEntityStreamingSupport = csv(8 * 1024) - /** - * Default `text/csv(UTF-8)` entity streaming support. - * Provides framing and rendering of `\n` separated lines and marshalling Sources into such values. - */ - def csv(maxLineLength: Int): CsvEntityStreamingSupport = new CsvEntityStreamingSupport(maxLineLength) -} - diff --git a/akka-http/src/main/scala/akka/http/scaladsl/common/JsonEntityStreamingSupport.scala b/akka-http/src/main/scala/akka/http/scaladsl/common/JsonEntityStreamingSupport.scala deleted file mode 100644 index 743f5fd8aa..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/common/JsonEntityStreamingSupport.scala +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2016 Lightbend Inc. - */ - -package akka.http.scaladsl.common - -import akka.NotUsed -import akka.event.Logging -import akka.http.javadsl.{ common, model ⇒ jm } -import akka.http.scaladsl.model.{ ContentType, ContentTypeRange, ContentTypes } -import akka.stream.scaladsl.Flow -import akka.util.ByteString - -final class JsonEntityStreamingSupport private[akka] ( - maxObjectSize: Int, - val supported: ContentTypeRange, - val contentType: ContentType, - val framingRenderer: Flow[ByteString, ByteString, NotUsed], - val parallelism: Int, - val unordered: Boolean -) extends common.JsonEntityStreamingSupport { - import akka.http.impl.util.JavaMapping.Implicits._ - - def this(maxObjectSize: Int) = - this( - maxObjectSize, - ContentTypeRange(ContentTypes.`application/json`), - ContentTypes.`application/json`, - Flow[ByteString].intersperse(ByteString("["), ByteString(","), ByteString("]")), - 1, false) - - override val framingDecoder: Flow[ByteString, ByteString, NotUsed] = - akka.stream.scaladsl.JsonFraming.objectScanner(maxObjectSize) - - override def withFramingRendererFlow(framingRendererFlow: akka.stream.javadsl.Flow[ByteString, ByteString, NotUsed]): JsonEntityStreamingSupport = - withFramingRenderer(framingRendererFlow.asScala) - def withFramingRenderer(framingRendererFlow: Flow[ByteString, ByteString, NotUsed]): JsonEntityStreamingSupport = - new JsonEntityStreamingSupport(maxObjectSize, supported, contentType, framingRendererFlow, parallelism, unordered) - - override def withContentType(ct: jm.ContentType): JsonEntityStreamingSupport = - new JsonEntityStreamingSupport(maxObjectSize, supported, ct.asScala, framingRenderer, parallelism, unordered) - override def withSupported(range: jm.ContentTypeRange): JsonEntityStreamingSupport = - new JsonEntityStreamingSupport(maxObjectSize, range.asScala, contentType, framingRenderer, parallelism, unordered) - override def withParallelMarshalling(parallelism: Int, unordered: Boolean): JsonEntityStreamingSupport = - new JsonEntityStreamingSupport(maxObjectSize, supported, contentType, framingRenderer, parallelism, unordered) - - override def toString = s"""${Logging.simpleName(getClass)}($maxObjectSize, $supported, $contentType)""" - -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/common/NameReceptacle.scala b/akka-http/src/main/scala/akka/http/scaladsl/common/NameReceptacle.scala deleted file mode 100644 index 65b0bfaa27..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/common/NameReceptacle.scala +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.common - -import akka.http.scaladsl.unmarshalling.{ Unmarshaller, FromStringUnmarshaller ⇒ FSU } - -private[http] trait ToNameReceptacleEnhancements { - implicit def _symbol2NR(symbol: Symbol): NameReceptacle[String] = new NameReceptacle[String](symbol.name) - implicit def _string2NR(string: String): NameReceptacle[String] = new NameReceptacle[String](string) -} -object ToNameReceptacleEnhancements extends ToNameReceptacleEnhancements - -class NameReceptacle[T](val name: String) { - def as[B] = new NameReceptacle[B](name) - def as[B](unmarshaller: Unmarshaller[T, B])(implicit fsu: FSU[T]) = - new NameUnmarshallerReceptacle(name, fsu.transform[B](implicit ec ⇒ implicit mat ⇒ { _ flatMap unmarshaller.apply })) - def ? = new NameOptionReceptacle[T](name) - def ?[B](default: B) = new NameDefaultReceptacle(name, default) - def ![B](requiredValue: B) = new RequiredValueReceptacle(name, requiredValue) - def * = new RepeatedValueReceptacle[T](name) -} - -class NameUnmarshallerReceptacle[T](val name: String, val um: FSU[T]) { - def as[B](implicit unmarshaller: Unmarshaller[T, B]) = - new NameUnmarshallerReceptacle(name, um.transform[B](implicit ec ⇒ implicit mat ⇒ { _ flatMap unmarshaller.apply })) - def ? = new NameOptionUnmarshallerReceptacle[T](name, um) - def ?(default: T) = new NameDefaultUnmarshallerReceptacle(name, default, um) - def !(requiredValue: T) = new RequiredValueUnmarshallerReceptacle(name, requiredValue, um) - def * = new RepeatedValueUnmarshallerReceptacle[T](name, um) -} - -class NameOptionReceptacle[T](val name: String) - -class NameDefaultReceptacle[T](val name: String, val default: T) - -class RequiredValueReceptacle[T](val name: String, val requiredValue: T) - -class RepeatedValueReceptacle[T](val name: String) - -class NameOptionUnmarshallerReceptacle[T](val name: String, val um: FSU[T]) - -class NameDefaultUnmarshallerReceptacle[T](val name: String, val default: T, val um: FSU[T]) - -class RequiredValueUnmarshallerReceptacle[T](val name: String, val requiredValue: T, val um: FSU[T]) - -class RepeatedValueUnmarshallerReceptacle[T](val name: String, val um: FSU[T]) diff --git a/akka-http/src/main/scala/akka/http/scaladsl/common/StrictForm.scala b/akka-http/src/main/scala/akka/http/scaladsl/common/StrictForm.scala deleted file mode 100644 index f6a5d35206..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/common/StrictForm.scala +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.common - -import scala.annotation.implicitNotFound -import scala.collection.immutable -import scala.concurrent.{ ExecutionContext, Future } -import scala.concurrent.duration._ -import akka.stream.Materializer -import akka.http.scaladsl.unmarshalling._ -import akka.http.scaladsl.model._ -import akka.http.scaladsl.util.FastFuture -import FastFuture._ - -/** - * Read-only abstraction on top of `application/x-www-form-urlencoded` and multipart form data, - * allowing joint unmarshalling access to either kind, **if** you supply both, a [[akka.http.scaladsl.unmarshalling.FromStringUnmarshaller]] - * as well as a [[akka.http.scaladsl.unmarshalling.FromEntityUnmarshaller]] for the target type `T`. - * Note: In order to allow for random access to the field values streamed multipart form data are strictified! - * Don't use this abstraction on potentially unbounded forms (e.g. large file uploads). - * - * If you only need to consume one type of form (`application/x-www-form-urlencoded` *or* multipart) then - * simply unmarshal directly to the respective form abstraction ([[akka.http.scaladsl.model.FormData]] or [[akka.http.scaladsl.model.Multipart.FormData]]) - * rather than going through [[StrictForm]]. - * - * Simple usage example: - * {{{ - * val strictFormFuture = Unmarshal(entity).to[StrictForm] - * val fooFieldUnmarshalled: Future[T] = - * strictFormFuture flatMap { form => - * Unmarshal(form field "foo").to[T] - * } - * }}} - */ -sealed abstract class StrictForm { - def fields: immutable.Seq[(String, StrictForm.Field)] - def field(name: String): Option[StrictForm.Field] = fields collectFirst { case (`name`, field) ⇒ field } -} - -object StrictForm { - sealed trait Field - object Field { - private[http] def fromString(value: String): Field = FromString(value) - - private[StrictForm] final case class FromString(value: String) extends Field - private[StrictForm] final case class FromPart(value: Multipart.FormData.BodyPart.Strict) extends Field - - implicit def unmarshaller[T](implicit um: FieldUnmarshaller[T]): FromStrictFormFieldUnmarshaller[T] = - Unmarshaller.withMaterializer(implicit ec ⇒ implicit mat ⇒ { - case FromString(value) ⇒ um.unmarshalString(value) - case FromPart(value) ⇒ um.unmarshalPart(value) - }) - - def unmarshallerFromFSU[T](fsu: FromStringUnmarshaller[T]): FromStrictFormFieldUnmarshaller[T] = - Unmarshaller.withMaterializer(implicit ec ⇒ implicit mat ⇒ { - case FromString(value) ⇒ fsu(value) - case FromPart(value) ⇒ - val charsetName = value.entity.contentType.asInstanceOf[ContentType.NonBinary].charset.nioCharset.name - fsu(value.entity.data.decodeString(charsetName)) - }) - - @implicitNotFound(msg = - s"In order to unmarshal a `StrictForm.Field` to type `$${T}` you need to supply a " + - s"`FromStringUnmarshaller[$${T}]` and/or a `FromEntityUnmarshaller[$${T}]`") - sealed trait FieldUnmarshaller[T] { - def unmarshalString(value: String)(implicit ec: ExecutionContext, mat: Materializer): Future[T] - def unmarshalPart(value: Multipart.FormData.BodyPart.Strict)(implicit ec: ExecutionContext, mat: Materializer): Future[T] - } - object FieldUnmarshaller extends LowPrioImplicits { - implicit def fromBoth[T](implicit fsu: FromStringUnmarshaller[T], feu: FromEntityUnmarshaller[T]): FieldUnmarshaller[T] = - new FieldUnmarshaller[T] { - def unmarshalString(value: String)(implicit ec: ExecutionContext, mat: Materializer) = fsu(value) - def unmarshalPart(value: Multipart.FormData.BodyPart.Strict)(implicit ec: ExecutionContext, mat: Materializer) = feu(value.entity) - } - } - sealed abstract class LowPrioImplicits { - implicit def fromFSU[T](implicit fsu: FromStringUnmarshaller[T]): FieldUnmarshaller[T] = - new FieldUnmarshaller[T] { - def unmarshalString(value: String)(implicit ec: ExecutionContext, mat: Materializer) = fsu(value) - def unmarshalPart(value: Multipart.FormData.BodyPart.Strict)(implicit ec: ExecutionContext, mat: Materializer) = { - val charsetName = value.entity.contentType.asInstanceOf[ContentType.NonBinary].charset.nioCharset.name - fsu(value.entity.data.decodeString(charsetName)) - } - } - implicit def fromFEU[T](implicit feu: FromEntityUnmarshaller[T]): FieldUnmarshaller[T] = - new FieldUnmarshaller[T] { - def unmarshalString(value: String)(implicit ec: ExecutionContext, mat: Materializer) = feu(HttpEntity(value)) - def unmarshalPart(value: Multipart.FormData.BodyPart.Strict)(implicit ec: ExecutionContext, mat: Materializer) = feu(value.entity) - } - } - } - - implicit def unmarshaller(implicit - formDataUM: FromEntityUnmarshaller[FormData], - multipartUM: FromEntityUnmarshaller[Multipart.FormData]): FromEntityUnmarshaller[StrictForm] = - Unmarshaller.withMaterializer { implicit ec ⇒ implicit fm ⇒ - entity ⇒ - - def tryUnmarshalToQueryForm: Future[StrictForm] = - for (formData ← formDataUM(entity).fast) yield { - new StrictForm { - val fields = formData.fields.map { case (name, value) ⇒ name → Field.FromString(value) }(collection.breakOut) - } - } - - def tryUnmarshalToMultipartForm: Future[StrictForm] = - for { - multiPartFD ← multipartUM(entity).fast - strictMultiPartFD ← multiPartFD.toStrict(10.seconds).fast // TODO: make timeout configurable - } yield { - new StrictForm { - val fields = strictMultiPartFD.strictParts.map { - case x: Multipart.FormData.BodyPart.Strict ⇒ x.name → Field.FromPart(x) - }(collection.breakOut) - } - } - - tryUnmarshalToQueryForm.fast.recoverWith { - case Unmarshaller.UnsupportedContentTypeException(supported1) ⇒ - tryUnmarshalToMultipartForm.fast.recoverWith { - case Unmarshaller.UnsupportedContentTypeException(supported2) ⇒ - FastFuture.failed(Unmarshaller.UnsupportedContentTypeException(supported1 ++ supported2)) - } - } - } - - /** - * Simple model for strict file content in a multipart form data part. - */ - final case class FileData(filename: Option[String], entity: HttpEntity.Strict) - - object FileData { - implicit val unmarshaller: FromStrictFormFieldUnmarshaller[FileData] = - Unmarshaller strict { - case Field.FromString(_) ⇒ throw Unmarshaller.UnsupportedContentTypeException(MediaTypes.`application/x-www-form-urlencoded`) - case Field.FromPart(part) ⇒ FileData(part.filename, part.entity) - } - } -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/marshalling/ContentTypeOverrider.scala b/akka-http/src/main/scala/akka/http/scaladsl/marshalling/ContentTypeOverrider.scala deleted file mode 100644 index b7b74df025..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/marshalling/ContentTypeOverrider.scala +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.marshalling - -import scala.collection.immutable -import akka.http.scaladsl.model._ - -sealed trait ContentTypeOverrider[T] { - def apply(value: T, newContentType: ContentType): T -} - -object ContentTypeOverrider { - - implicit def forEntity[T <: HttpEntity]: ContentTypeOverrider[T] = new ContentTypeOverrider[T] { - def apply(value: T, newContentType: ContentType) = - value.withContentType(newContentType).asInstanceOf[T] // can't be expressed in types - } - - implicit def forHeadersAndEntity[T <: HttpEntity]: ContentTypeOverrider[(immutable.Seq[HttpHeader], T)] = - new ContentTypeOverrider[(immutable.Seq[HttpHeader], T)] { - def apply(value: (immutable.Seq[HttpHeader], T), newContentType: ContentType) = - value._1 → value._2.withContentType(newContentType).asInstanceOf[T] - } - - implicit val forResponse: ContentTypeOverrider[HttpResponse] = - new ContentTypeOverrider[HttpResponse] { - def apply(value: HttpResponse, newContentType: ContentType) = - value.mapEntity(forEntity(_: ResponseEntity, newContentType)) - } - - implicit val forRequest: ContentTypeOverrider[HttpRequest] = - new ContentTypeOverrider[HttpRequest] { - def apply(value: HttpRequest, newContentType: ContentType) = - value.mapEntity(forEntity(_: RequestEntity, newContentType)) - } -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/marshalling/EmptyValue.scala b/akka-http/src/main/scala/akka/http/scaladsl/marshalling/EmptyValue.scala deleted file mode 100644 index 28dd4fc8b8..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/marshalling/EmptyValue.scala +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.marshalling - -import scala.collection.immutable -import akka.http.scaladsl.model._ - -class EmptyValue[+T] private (val emptyValue: T) - -object EmptyValue { - implicit def emptyEntity: EmptyValue[UniversalEntity] = - new EmptyValue[UniversalEntity](HttpEntity.Empty) - - implicit val emptyHeadersAndEntity: EmptyValue[(immutable.Seq[HttpHeader], UniversalEntity)] = - new EmptyValue[(immutable.Seq[HttpHeader], UniversalEntity)](Nil → HttpEntity.Empty) - - implicit val emptyResponse: EmptyValue[HttpResponse] = - new EmptyValue[HttpResponse](HttpResponse(entity = emptyEntity.emptyValue)) -} \ No newline at end of file diff --git a/akka-http/src/main/scala/akka/http/scaladsl/marshalling/GenericMarshallers.scala b/akka-http/src/main/scala/akka/http/scaladsl/marshalling/GenericMarshallers.scala deleted file mode 100644 index 2509993add..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/marshalling/GenericMarshallers.scala +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.marshalling - -import scala.concurrent.Future -import scala.util.{ Try, Failure, Success } -import akka.http.scaladsl.util.FastFuture -import FastFuture._ - -trait GenericMarshallers extends LowPriorityToResponseMarshallerImplicits { - - implicit def throwableMarshaller[T]: Marshaller[Throwable, T] = Marshaller(_ ⇒ FastFuture.failed) - - implicit def optionMarshaller[A, B](implicit m: Marshaller[A, B], empty: EmptyValue[B]): Marshaller[Option[A], B] = - Marshaller { implicit ec ⇒ - { - case Some(value) ⇒ m(value) - case None ⇒ FastFuture.successful(Marshalling.Opaque(() ⇒ empty.emptyValue) :: Nil) - } - } - - implicit def eitherMarshaller[A1, A2, B](implicit m1: Marshaller[A1, B], m2: Marshaller[A2, B]): Marshaller[Either[A1, A2], B] = - Marshaller { implicit ec ⇒ - { - case Left(a1) ⇒ m1(a1) - case Right(a2) ⇒ m2(a2) - } - } - - implicit def futureMarshaller[A, B](implicit m: Marshaller[A, B]): Marshaller[Future[A], B] = - Marshaller(implicit ec ⇒ _.fast.flatMap(m(_))) - - implicit def tryMarshaller[A, B](implicit m: Marshaller[A, B]): Marshaller[Try[A], B] = - Marshaller { implicit ec ⇒ - { - case Success(value) ⇒ m(value) - case Failure(error) ⇒ FastFuture.failed(error) - } - } -} - -object GenericMarshallers extends GenericMarshallers \ No newline at end of file diff --git a/akka-http/src/main/scala/akka/http/scaladsl/marshalling/Marshal.scala b/akka-http/src/main/scala/akka/http/scaladsl/marshalling/Marshal.scala deleted file mode 100644 index 1762d59944..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/marshalling/Marshal.scala +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.marshalling - -import scala.concurrent.{ ExecutionContext, Future } -import akka.http.scaladsl.server.ContentNegotiator -import akka.http.scaladsl.model._ -import akka.http.scaladsl.util.FastFuture._ - -import scala.util.control.NoStackTrace - -object Marshal { - def apply[T](value: T): Marshal[T] = new Marshal(value) - - final case class UnacceptableResponseContentTypeException(supported: Set[ContentNegotiator.Alternative]) - extends RuntimeException with NoStackTrace -} - -class Marshal[A](val value: A) { - /** - * Marshals `value` using the first available [[Marshalling]] for `A` and `B` provided by the given [[Marshaller]]. - * If the marshalling is flexible with regard to the used charset `UTF-8` is chosen. - */ - def to[B](implicit m: Marshaller[A, B], ec: ExecutionContext): Future[B] = - m(value).fast.map { - _.head match { - case Marshalling.WithFixedContentType(_, marshal) ⇒ marshal() - case Marshalling.WithOpenCharset(_, marshal) ⇒ marshal(HttpCharsets.`UTF-8`) - case Marshalling.Opaque(marshal) ⇒ marshal() - } - } - - /** - * Marshals `value` to an `HttpResponse` for the given `HttpRequest` with full content-negotiation. - */ - def toResponseFor(request: HttpRequest)(implicit m: ToResponseMarshaller[A], ec: ExecutionContext): Future[HttpResponse] = { - import akka.http.scaladsl.marshalling.Marshal._ - val ctn = ContentNegotiator(request.headers) - - m(value).fast.map { marshallings ⇒ - val supportedAlternatives: List[ContentNegotiator.Alternative] = - marshallings.collect { - case Marshalling.WithFixedContentType(ct, _) ⇒ ContentNegotiator.Alternative(ct) - case Marshalling.WithOpenCharset(mt, _) ⇒ ContentNegotiator.Alternative(mt) - }(collection.breakOut) - val bestMarshal = { - if (supportedAlternatives.nonEmpty) { - ctn.pickContentType(supportedAlternatives).flatMap { - case best @ (_: ContentType.Binary | _: ContentType.WithFixedCharset) ⇒ - marshallings collectFirst { case Marshalling.WithFixedContentType(`best`, marshal) ⇒ marshal } - case best @ ContentType.WithCharset(bestMT, bestCS) ⇒ - marshallings collectFirst { - case Marshalling.WithFixedContentType(`best`, marshal) ⇒ marshal - case Marshalling.WithOpenCharset(`bestMT`, marshal) ⇒ () ⇒ marshal(bestCS) - } - } - } else None - } orElse { - marshallings collectFirst { case Marshalling.Opaque(marshal) ⇒ marshal } - } getOrElse { - throw UnacceptableResponseContentTypeException(supportedAlternatives.toSet) - } - bestMarshal() - } - } -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/marshalling/Marshaller.scala b/akka-http/src/main/scala/akka/http/scaladsl/marshalling/Marshaller.scala deleted file mode 100644 index 6606ff9f37..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/marshalling/Marshaller.scala +++ /dev/null @@ -1,179 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.marshalling - -import akka.http.scaladsl.marshalling.Marshalling.Opaque - -import scala.concurrent.{ ExecutionContext, Future } -import scala.util.control.NonFatal -import akka.http.scaladsl.model._ -import akka.http.scaladsl.util.FastFuture -import akka.http.scaladsl.util.FastFuture._ - -// TODO make it extend JavaDSL -sealed abstract class Marshaller[-A, +B] { - - def apply(value: A)(implicit ec: ExecutionContext): Future[List[Marshalling[B]]] - - def map[C](f: B ⇒ C): Marshaller[A, C] = - Marshaller(implicit ec ⇒ value ⇒ this(value).fast map (_ map (_ map f))) - - /** - * Reuses this Marshaller's logic to produce a new Marshaller from another type `C` which overrides - * the [[akka.http.scaladsl.model.MediaType]] of the marshalling result with the given one. - * Note that not all wrappings are legal. f the underlying [[akka.http.scaladsl.model.MediaType]] has constraints with regard to the - * charsets it allows the new [[akka.http.scaladsl.model.MediaType]] must be compatible, since akka-http will never recode entities. - * If the wrapping is illegal the [[scala.concurrent.Future]] produced by the resulting marshaller will contain a [[RuntimeException]]. - */ - def wrap[C, D >: B](newMediaType: MediaType)(f: C ⇒ A)(implicit mto: ContentTypeOverrider[D]): Marshaller[C, D] = - wrapWithEC[C, D](newMediaType)(_ ⇒ f) - - /** - * Reuses this Marshaller's logic to produce a new Marshaller from another type `C` which overrides - * the [[akka.http.scaladsl.model.MediaType]] of the marshalling result with the given one. - * Note that not all wrappings are legal. f the underlying [[akka.http.scaladsl.model.MediaType]] has constraints with regard to the - * charsets it allows the new [[akka.http.scaladsl.model.MediaType]] must be compatible, since akka-http will never recode entities. - * If the wrapping is illegal the [[scala.concurrent.Future]] produced by the resulting marshaller will contain a [[RuntimeException]]. - */ - def wrapWithEC[C, D >: B](newMediaType: MediaType)(f: ExecutionContext ⇒ C ⇒ A)(implicit cto: ContentTypeOverrider[D]): Marshaller[C, D] = - Marshaller { implicit ec ⇒ value ⇒ - import Marshalling._ - this(f(ec)(value)).fast map { - _ map { - (_, newMediaType) match { - case (WithFixedContentType(_, marshal), newMT: MediaType.Binary) ⇒ - WithFixedContentType(newMT, () ⇒ cto(marshal(), newMT)) - case (WithFixedContentType(oldCT: ContentType.Binary, marshal), newMT: MediaType.WithFixedCharset) ⇒ - WithFixedContentType(newMT, () ⇒ cto(marshal(), newMT)) - case (WithFixedContentType(oldCT: ContentType.NonBinary, marshal), newMT: MediaType.WithFixedCharset) if oldCT.charset == newMT.charset ⇒ - WithFixedContentType(newMT, () ⇒ cto(marshal(), newMT)) - case (WithFixedContentType(oldCT: ContentType.NonBinary, marshal), newMT: MediaType.WithOpenCharset) ⇒ - val newCT = newMT withCharset oldCT.charset - WithFixedContentType(newCT, () ⇒ cto(marshal(), newCT)) - - case (WithOpenCharset(oldMT, marshal), newMT: MediaType.WithOpenCharset) ⇒ - WithOpenCharset(newMT, cs ⇒ cto(marshal(cs), newMT withCharset cs)) - case (WithOpenCharset(oldMT, marshal), newMT: MediaType.WithFixedCharset) ⇒ - WithFixedContentType(newMT, () ⇒ cto(marshal(newMT.charset), newMT)) - - case (Opaque(marshal), newMT: MediaType.Binary) ⇒ - WithFixedContentType(newMT, () ⇒ cto(marshal(), newMT)) - case (Opaque(marshal), newMT: MediaType.WithFixedCharset) ⇒ - WithFixedContentType(newMT, () ⇒ cto(marshal(), newMT)) - - case x ⇒ sys.error(s"Illegal marshaller wrapping. Marshalling `$x` cannot be wrapped with MediaType `$newMediaType`") - } - } - } - } - - def compose[C](f: C ⇒ A): Marshaller[C, B] = - Marshaller(implicit ec ⇒ c ⇒ apply(f(c))) - - def composeWithEC[C](f: ExecutionContext ⇒ C ⇒ A): Marshaller[C, B] = - Marshaller(implicit ec ⇒ c ⇒ apply(f(ec)(c))) -} - -//# marshaller-creation -object Marshaller - extends GenericMarshallers - with PredefinedToEntityMarshallers - with PredefinedToResponseMarshallers - with PredefinedToRequestMarshallers { - - /** - * Creates a [[Marshaller]] from the given function. - */ - def apply[A, B](f: ExecutionContext ⇒ A ⇒ Future[List[Marshalling[B]]]): Marshaller[A, B] = - new Marshaller[A, B] { - def apply(value: A)(implicit ec: ExecutionContext) = - try f(ec)(value) - catch { case NonFatal(e) ⇒ FastFuture.failed(e) } - } - - /** - * Helper for creating a [[Marshaller]] using the given function. - */ - def strict[A, B](f: A ⇒ Marshalling[B]): Marshaller[A, B] = - Marshaller { _ ⇒ a ⇒ FastFuture.successful(f(a) :: Nil) } - - /** - * Helper for creating a "super-marshaller" from a number of "sub-marshallers". - * Content-negotiation determines, which "sub-marshaller" eventually gets to do the job. - */ - def oneOf[A, B](marshallers: Marshaller[A, B]*): Marshaller[A, B] = - Marshaller { implicit ec ⇒ a ⇒ FastFuture.sequence(marshallers.map(_(a))).fast.map(_.flatten.toList) } - - /** - * Helper for creating a "super-marshaller" from a number of values and a function producing "sub-marshallers" - * from these values. Content-negotiation determines, which "sub-marshaller" eventually gets to do the job. - */ - def oneOf[T, A, B](values: T*)(f: T ⇒ Marshaller[A, B]): Marshaller[A, B] = - oneOf(values map f: _*) - - /** - * Helper for creating a synchronous [[Marshaller]] to content with a fixed charset from the given function. - */ - def withFixedContentType[A, B](contentType: ContentType)(marshal: A ⇒ B): Marshaller[A, B] = - strict { value ⇒ Marshalling.WithFixedContentType(contentType, () ⇒ marshal(value)) } - - /** - * Helper for creating a synchronous [[Marshaller]] to content with a negotiable charset from the given function. - */ - def withOpenCharset[A, B](mediaType: MediaType.WithOpenCharset)(marshal: (A, HttpCharset) ⇒ B): Marshaller[A, B] = - strict { value ⇒ Marshalling.WithOpenCharset(mediaType, charset ⇒ marshal(value, charset)) } - - /** - * Helper for creating a synchronous [[Marshaller]] to non-negotiable content from the given function. - */ - def opaque[A, B](marshal: A ⇒ B): Marshaller[A, B] = - strict { value ⇒ Marshalling.Opaque(() ⇒ marshal(value)) } - - /** - * Helper for creating a [[Marshaller]] combined of the provided `marshal` function - * and an implicit Marshaller which is able to produce the required final type. - */ - def combined[A, B, C](marshal: A ⇒ B)(implicit m2: Marshaller[B, C]): Marshaller[A, C] = - Marshaller[A, C] { ec ⇒ a ⇒ m2.compose(marshal).apply(a)(ec) } -} -//# - -//#marshalling -/** - * Describes one possible option for marshalling a given value. - */ -sealed trait Marshalling[+A] { - def map[B](f: A ⇒ B): Marshalling[B] -} - -object Marshalling { - - /** - * A Marshalling to a specific [[akka.http.scaladsl.model.ContentType]]. - */ - final case class WithFixedContentType[A]( - contentType: ContentType, - marshal: () ⇒ A) extends Marshalling[A] { - def map[B](f: A ⇒ B): WithFixedContentType[B] = copy(marshal = () ⇒ f(marshal())) - } - - /** - * A Marshalling to a specific [[akka.http.scaladsl.model.MediaType]] with a flexible charset. - */ - final case class WithOpenCharset[A]( - mediaType: MediaType.WithOpenCharset, - marshal: HttpCharset ⇒ A) extends Marshalling[A] { - def map[B](f: A ⇒ B): WithOpenCharset[B] = copy(marshal = cs ⇒ f(marshal(cs))) - } - - /** - * A Marshalling to an unknown MediaType and charset. - * Circumvents content negotiation. - */ - final case class Opaque[A](marshal: () ⇒ A) extends Marshalling[A] { - def map[B](f: A ⇒ B): Opaque[B] = copy(marshal = () ⇒ f(marshal())) - } -} -//# diff --git a/akka-http/src/main/scala/akka/http/scaladsl/marshalling/MultipartMarshallers.scala b/akka-http/src/main/scala/akka/http/scaladsl/marshalling/MultipartMarshallers.scala deleted file mode 100644 index 5ccfde904e..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/marshalling/MultipartMarshallers.scala +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.marshalling - -import java.util.concurrent.ThreadLocalRandom -import akka.event.{ NoLogging, LoggingAdapter } -import akka.http.impl.engine.rendering.BodyPartRenderer -import akka.http.scaladsl.model._ - -trait MultipartMarshallers { - implicit def multipartMarshaller[T <: Multipart](implicit log: LoggingAdapter = NoLogging): ToEntityMarshaller[T] = - Marshaller strict { value ⇒ - val boundary = randomBoundary() - val mediaType = value.mediaType withBoundary boundary - Marshalling.WithOpenCharset(mediaType, { charset ⇒ - value.toEntity(charset, boundary)(log) - }) - } - - /** - * The random instance that is used to create multipart boundaries. This can be overriden (e.g. in tests) to - * choose how a boundary is created. - */ - protected def multipartBoundaryRandom: java.util.Random = ThreadLocalRandom.current() - - /** - * The length of randomly generated multipart boundaries (before base64 encoding). Can be overridden - * to configure. - */ - protected def multipartBoundaryLength: Int = 18 - - /** - * The method used to create boundaries in `multipartMarshaller`. Can be overridden to create custom boundaries. - */ - protected def randomBoundary(): String = - BodyPartRenderer.randomBoundary(length = multipartBoundaryLength, random = multipartBoundaryRandom) -} - -object MultipartMarshallers extends MultipartMarshallers diff --git a/akka-http/src/main/scala/akka/http/scaladsl/marshalling/PredefinedToEntityMarshallers.scala b/akka-http/src/main/scala/akka/http/scaladsl/marshalling/PredefinedToEntityMarshallers.scala deleted file mode 100644 index 2df34953c2..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/marshalling/PredefinedToEntityMarshallers.scala +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.marshalling - -import java.nio.CharBuffer - -import akka.http.scaladsl.model.MediaTypes._ -import akka.http.scaladsl.model.ContentTypes.`text/plain(UTF-8)` -import akka.http.scaladsl.model._ -import akka.util.ByteString - -trait PredefinedToEntityMarshallers extends MultipartMarshallers { - - implicit val ByteArrayMarshaller: ToEntityMarshaller[Array[Byte]] = byteArrayMarshaller(`application/octet-stream`) - def byteArrayMarshaller(contentType: ContentType): ToEntityMarshaller[Array[Byte]] = - Marshaller.withFixedContentType(contentType) { bytes ⇒ HttpEntity(contentType, bytes) } - - implicit val ByteStringMarshaller: ToEntityMarshaller[ByteString] = byteStringMarshaller(`application/octet-stream`) - def byteStringMarshaller(contentType: ContentType): ToEntityMarshaller[ByteString] = - Marshaller.withFixedContentType(contentType) { bytes ⇒ HttpEntity(contentType, bytes) } - - implicit val CharArrayMarshaller: ToEntityMarshaller[Array[Char]] = charArrayMarshaller(`text/plain`) - def charArrayMarshaller(mediaType: MediaType.WithOpenCharset): ToEntityMarshaller[Array[Char]] = - Marshaller.withOpenCharset(mediaType) { (value, charset) ⇒ marshalCharArray(value, mediaType withCharset charset) } - def charArrayMarshaller(mediaType: MediaType.WithFixedCharset): ToEntityMarshaller[Array[Char]] = - Marshaller.withFixedContentType(mediaType) { value ⇒ marshalCharArray(value, mediaType) } - - private def marshalCharArray(value: Array[Char], contentType: ContentType.NonBinary): HttpEntity.Strict = - if (value.length > 0) { - val charBuffer = CharBuffer.wrap(value) - val byteBuffer = contentType.charset.nioCharset.encode(charBuffer) - val array = new Array[Byte](byteBuffer.remaining()) - byteBuffer.get(array) - HttpEntity(contentType, array) - } else HttpEntity.Empty - - implicit val DoneMarshaller: ToEntityMarshaller[akka.Done] = - Marshaller.withFixedContentType(`text/plain(UTF-8)`) { done ⇒ - HttpEntity(`text/plain(UTF-8)`, "") - } - - implicit val StringMarshaller: ToEntityMarshaller[String] = stringMarshaller(`text/plain`) - def stringMarshaller(mediaType: MediaType.WithOpenCharset): ToEntityMarshaller[String] = - Marshaller.withOpenCharset(mediaType) { (s, cs) ⇒ HttpEntity(mediaType withCharset cs, s) } - def stringMarshaller(mediaType: MediaType.WithFixedCharset): ToEntityMarshaller[String] = - Marshaller.withFixedContentType(mediaType) { s ⇒ HttpEntity(mediaType, s) } - - implicit val FormDataMarshaller: ToEntityMarshaller[FormData] = - Marshaller.withOpenCharset(`application/x-www-form-urlencoded`) { _ toEntity _ } - - implicit val MessageEntityMarshaller: ToEntityMarshaller[MessageEntity] = - Marshaller strict { value ⇒ Marshalling.WithFixedContentType(value.contentType, () ⇒ value) } -} - -object PredefinedToEntityMarshallers extends PredefinedToEntityMarshallers - diff --git a/akka-http/src/main/scala/akka/http/scaladsl/marshalling/PredefinedToRequestMarshallers.scala b/akka-http/src/main/scala/akka/http/scaladsl/marshalling/PredefinedToRequestMarshallers.scala deleted file mode 100644 index 2756781477..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/marshalling/PredefinedToRequestMarshallers.scala +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.marshalling - -import scala.collection.immutable -import akka.http.scaladsl.model._ -import akka.http.scaladsl.util.FastFuture._ - -trait PredefinedToRequestMarshallers { - private type TRM[T] = ToRequestMarshaller[T] // brevity alias - - implicit val fromRequest: TRM[HttpRequest] = Marshaller.opaque(conforms) - - implicit def fromUri: TRM[Uri] = - Marshaller strict { uri ⇒ Marshalling.Opaque(() ⇒ HttpRequest(uri = uri)) } - - implicit def fromMethodAndUriAndValue[S, T](implicit mt: ToEntityMarshaller[T]): TRM[(HttpMethod, Uri, T)] = - fromMethodAndUriAndHeadersAndValue[T] compose { case (m, u, v) ⇒ (m, u, Nil, v) } - - implicit def fromMethodAndUriAndHeadersAndValue[T](implicit mt: ToEntityMarshaller[T]): TRM[(HttpMethod, Uri, immutable.Seq[HttpHeader], T)] = - Marshaller(implicit ec ⇒ { - case (m, u, h, v) ⇒ mt(v).fast map (_ map (_ map (HttpRequest(m, u, h, _)))) - }) -} - -object PredefinedToRequestMarshallers extends PredefinedToRequestMarshallers diff --git a/akka-http/src/main/scala/akka/http/scaladsl/marshalling/PredefinedToResponseMarshallers.scala b/akka-http/src/main/scala/akka/http/scaladsl/marshalling/PredefinedToResponseMarshallers.scala deleted file mode 100644 index d587566a20..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/marshalling/PredefinedToResponseMarshallers.scala +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.marshalling - -import akka.http.scaladsl.common.EntityStreamingSupport -import akka.http.scaladsl.model.MediaTypes._ -import akka.http.scaladsl.model._ -import akka.http.scaladsl.util.FastFuture -import akka.http.scaladsl.util.FastFuture._ -import akka.stream.impl.ConstantFun -import akka.stream.scaladsl.Source -import akka.util.ByteString - -import scala.collection.immutable -import scala.language.higherKinds - -trait PredefinedToResponseMarshallers extends LowPriorityToResponseMarshallerImplicits { - - private type TRM[T] = ToResponseMarshaller[T] // brevity alias - - def fromToEntityMarshaller[T]( - status: StatusCode = StatusCodes.OK, - headers: immutable.Seq[HttpHeader] = Nil)( - implicit - m: ToEntityMarshaller[T]): ToResponseMarshaller[T] = - fromStatusCodeAndHeadersAndValue compose (t ⇒ (status, headers, t)) - - implicit val fromResponse: TRM[HttpResponse] = Marshaller.opaque(ConstantFun.scalaIdentityFunction) - - implicit val fromStatusCode: TRM[StatusCode] = - Marshaller.withOpenCharset(`text/plain`) { (status, charset) ⇒ - val responseEntity = - if (status.allowsEntity) HttpEntity(status.defaultMessage) - else HttpEntity.Empty - HttpResponse(status, entity = responseEntity) - } - - implicit val fromStatusCodeAndHeaders: TRM[(StatusCode, immutable.Seq[HttpHeader])] = - Marshaller.withOpenCharset(`text/plain`) { (statusAndHeaders, charset) ⇒ - val status = statusAndHeaders._1 - val headers = statusAndHeaders._2 - val responseEntity = - if (status.allowsEntity) HttpEntity(status.defaultMessage) - else HttpEntity.Empty - HttpResponse(status, headers, entity = responseEntity) - } - - implicit def fromStatusCodeAndValue[S, T](implicit sConv: S ⇒ StatusCode, mt: ToEntityMarshaller[T]): TRM[(S, T)] = - fromStatusCodeAndHeadersAndValue[T] compose { case (status, value) ⇒ (sConv(status), Nil, value) } - - implicit def fromStatusCodeConvertibleAndHeadersAndT[S, T](implicit - sConv: S ⇒ StatusCode, - mt: ToEntityMarshaller[T]): TRM[(S, immutable.Seq[HttpHeader], T)] = - fromStatusCodeAndHeadersAndValue[T] compose { case (status, headers, value) ⇒ (sConv(status), headers, value) } - - implicit def fromStatusCodeAndHeadersAndValue[T](implicit mt: ToEntityMarshaller[T]): TRM[(StatusCode, immutable.Seq[HttpHeader], T)] = - Marshaller(implicit ec ⇒ { - case (status, headers, value) if (status.allowsEntity) ⇒ mt(value).fast map (_ map (_ map (HttpResponse(status, headers, _)))) - case (status, headers, _) ⇒ fromStatusCodeAndHeaders((status, headers)) - }) - - implicit def fromEntityStreamingSupportAndByteStringMarshaller[T, M](implicit s: EntityStreamingSupport, m: ToByteStringMarshaller[T]): ToResponseMarshaller[Source[T, M]] = { - Marshaller[Source[T, M], HttpResponse] { implicit ec ⇒ source ⇒ - FastFuture successful { - Marshalling.WithFixedContentType(s.contentType, () ⇒ { - val availableMarshallingsPerElement = source.mapAsync(1) { t ⇒ m(t)(ec) } - - // TODO optimise such that we pick the optimal marshalling only once (headAndTail needed?) - // TODO, NOTE: this is somewhat duplicated from Marshal.scala it could be made DRYer - val bestMarshallingPerElement = availableMarshallingsPerElement mapConcat { marshallings ⇒ - // pick the Marshalling that matches our EntityStreamingSupport - (s.contentType match { - case best @ (_: ContentType.Binary | _: ContentType.WithFixedCharset) ⇒ - marshallings collectFirst { case Marshalling.WithFixedContentType(`best`, marshal) ⇒ marshal } - - case best @ ContentType.WithCharset(bestMT, bestCS) ⇒ - marshallings collectFirst { - case Marshalling.WithFixedContentType(`best`, marshal) ⇒ marshal - case Marshalling.WithOpenCharset(`bestMT`, marshal) ⇒ () ⇒ marshal(bestCS) - } - }).toList - } - val marshalledElements: Source[ByteString, M] = - bestMarshallingPerElement.map(_.apply()) // marshal! - .via(s.framingRenderer) - - HttpResponse(entity = HttpEntity(s.contentType, marshalledElements)) - }) :: Nil - } - } - } -} - -trait LowPriorityToResponseMarshallerImplicits { - implicit def liftMarshallerConversion[T](m: ToEntityMarshaller[T]): ToResponseMarshaller[T] = - liftMarshaller(m) - implicit def liftMarshaller[T](implicit m: ToEntityMarshaller[T]): ToResponseMarshaller[T] = - PredefinedToResponseMarshallers.fromToEntityMarshaller() - - // FIXME deduplicate this!!! - implicit def fromEntityStreamingSupportAndEntityMarshaller[T, M](implicit s: EntityStreamingSupport, m: ToEntityMarshaller[T]): ToResponseMarshaller[Source[T, M]] = { - Marshaller[Source[T, M], HttpResponse] { implicit ec ⇒ source ⇒ - FastFuture successful { - Marshalling.WithFixedContentType(s.contentType, () ⇒ { - val availableMarshallingsPerElement = source.mapAsync(1) { t ⇒ m(t)(ec) } - - // TODO optimise such that we pick the optimal marshalling only once (headAndTail needed?) - // TODO, NOTE: this is somewhat duplicated from Marshal.scala it could be made DRYer - val bestMarshallingPerElement = availableMarshallingsPerElement mapConcat { marshallings ⇒ - // pick the Marshalling that matches our EntityStreamingSupport - (s.contentType match { - case best @ (_: ContentType.Binary | _: ContentType.WithFixedCharset) ⇒ - marshallings collectFirst { case Marshalling.WithFixedContentType(`best`, marshal) ⇒ marshal } - - case best @ ContentType.WithCharset(bestMT, bestCS) ⇒ - marshallings collectFirst { - case Marshalling.WithFixedContentType(`best`, marshal) ⇒ marshal - case Marshalling.WithOpenCharset(`bestMT`, marshal) ⇒ () ⇒ marshal(bestCS) - } - }).toList - } - val marshalledElements: Source[ByteString, M] = - bestMarshallingPerElement.map(_.apply()) // marshal! - .flatMapConcat(_.dataBytes) // extract raw dataBytes - .via(s.framingRenderer) - - HttpResponse(entity = HttpEntity(s.contentType, marshalledElements)) - }) :: Nil - } - } - } - -} - -object PredefinedToResponseMarshallers extends PredefinedToResponseMarshallers diff --git a/akka-http/src/main/scala/akka/http/scaladsl/marshalling/ToResponseMarshallable.scala b/akka-http/src/main/scala/akka/http/scaladsl/marshalling/ToResponseMarshallable.scala deleted file mode 100644 index ee94f2cb36..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/marshalling/ToResponseMarshallable.scala +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.marshalling - -import scala.concurrent.{ Future, ExecutionContext } -import akka.http.scaladsl.model._ - -/** Something that can later be marshalled into a response */ -trait ToResponseMarshallable { - type T - def value: T - implicit def marshaller: ToResponseMarshaller[T] - - def apply(request: HttpRequest)(implicit ec: ExecutionContext): Future[HttpResponse] = - Marshal(value).toResponseFor(request) -} - -object ToResponseMarshallable { - implicit def apply[A](_value: A)(implicit _marshaller: ToResponseMarshaller[A]): ToResponseMarshallable = - new ToResponseMarshallable { - type T = A - def value: T = _value - def marshaller: ToResponseMarshaller[T] = _marshaller - } - - implicit val marshaller: ToResponseMarshaller[ToResponseMarshallable] = - Marshaller { implicit ec ⇒ marshallable ⇒ marshallable.marshaller(marshallable.value) } -} \ No newline at end of file diff --git a/akka-http/src/main/scala/akka/http/scaladsl/marshalling/package.scala b/akka-http/src/main/scala/akka/http/scaladsl/marshalling/package.scala deleted file mode 100644 index c1935887e0..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/marshalling/package.scala +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl - -import scala.collection.immutable -import akka.http.scaladsl.model._ -import akka.util.ByteString - -package object marshalling { - //# marshaller-aliases - type ToEntityMarshaller[T] = Marshaller[T, MessageEntity] - type ToByteStringMarshaller[T] = Marshaller[T, ByteString] - type ToHeadersAndEntityMarshaller[T] = Marshaller[T, (immutable.Seq[HttpHeader], MessageEntity)] - type ToResponseMarshaller[T] = Marshaller[T, HttpResponse] - type ToRequestMarshaller[T] = Marshaller[T, HttpRequest] - //# -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/ContentNegotation.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/ContentNegotation.scala deleted file mode 100644 index ceec0ffe47..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/ContentNegotation.scala +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server - -import akka.http.scaladsl.model -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers._ -import HttpCharsets.`UTF-8` - -final class MediaTypeNegotiator(requestHeaders: Seq[HttpHeader]) { - - /** - * The media-ranges accepted by the client according to the given request headers, sorted by - * 1. increasing generality (i.e. most specific first) - * 2. decreasing q-value (only for ranges targeting a single MediaType) - * 3. order of appearance in the `Accept` header(s) - */ - val acceptedMediaRanges: List[MediaRange] = - (for { - Accept(mediaRanges) ← requestHeaders - range ← mediaRanges - } yield range).sortBy { // `sortBy` is stable, i.e. upholds the original order on identical keys - case x if x.isWildcard ⇒ 2f // most general, needs to come last - case MediaRange.One(_, qv) ⇒ -qv // most specific, needs to come first - case _ ⇒ 1f // simple range like `image/*` - }.toList - - /** - * Returns the q-value that the client (implicitly or explicitly) attaches to the given media-type. - * See http://tools.ietf.org/html/rfc7231#section-5.3.1 for details. - */ - def qValueFor(mediaType: MediaType): Float = - acceptedMediaRanges match { - case Nil ⇒ 1.0f - case x ⇒ x collectFirst { case r if r matches mediaType ⇒ r.qValue } getOrElse 0f - } - - /** - * Determines whether the given [[akka.http.scaladsl.model.MediaType]] is accepted by the client. - */ - def isAccepted(mediaType: MediaType): Boolean = qValueFor(mediaType) > 0f -} - -final class CharsetNegotiator(requestHeaders: Seq[HttpHeader]) { - - /** - * The charset-ranges accepted by the client according to given request headers, sorted by - * 1. increasing generality (i.e. most specific first) - * 2. decreasing q-value (only for ranges targeting a single HttpCharset) - * 3. order of appearance in the `Accept-Charset` header(s) - */ - val acceptedCharsetRanges: List[HttpCharsetRange] = - (for { - `Accept-Charset`(charsetRanges) ← requestHeaders - range ← charsetRanges - } yield range).sortBy { // `sortBy` is stable, i.e. upholds the original order on identical keys - case _: HttpCharsetRange.`*` ⇒ 1f // most general, needs to come last - case x ⇒ -x.qValue // all others come first - }.toList - - /** - * Returns the q-value that the client (implicitly or explicitly) attaches to the given charset. - * See http://tools.ietf.org/html/rfc7231#section-5.3.1 for details. - */ - def qValueFor(charset: HttpCharset): Float = - acceptedCharsetRanges match { - case Nil ⇒ 1.0f - case x ⇒ x collectFirst { case r if r matches charset ⇒ r.qValue } getOrElse 0f - } - - /** - * Determines whether the given charset is accepted by the client. - */ - def isAccepted(charset: HttpCharset): Boolean = qValueFor(charset) > 0f - - /** - * Picks the charset that is most preferred by the client with a bias towards UTF-8, - * i.e. if the client accepts all charsets with equal preference then UTF-8 is picked. - * If the client doesn't accept any charsets the method returns `None`. - * - * See also: http://tools.ietf.org/html/rfc7231#section-5.3.3 - */ - def pickBest: Option[HttpCharset] = - acceptedCharsetRanges match { - case Nil ⇒ Some(`UTF-8`) - case HttpCharsetRange.One(cs, _) :: _ ⇒ Some(cs) - case HttpCharsetRange.`*`(qv) :: _ if qv > 0f ⇒ Some(`UTF-8`) - case _ ⇒ None - } -} - -final class ContentNegotiator(requestHeaders: Seq[HttpHeader]) { - import ContentNegotiator.Alternative - - val mtn = new MediaTypeNegotiator(requestHeaders) - val csn = new CharsetNegotiator(requestHeaders) - - def qValueFor(alternative: Alternative): Float = - alternative match { - case Alternative.ContentType(ct: ContentType.NonBinary) ⇒ - math.min(mtn.qValueFor(ct.mediaType), csn.qValueFor(ct.charset)) - case x ⇒ mtn.qValueFor(x.mediaType) - } - - /** - * Picks the best of the given content alternatives given the preferences - * the client indicated in the request's `Accept` and `Accept-Charset` headers. - * See http://tools.ietf.org/html/rfc7231#section-5.3.2 ff for details on the negotiation logic. - * - * If there are several best alternatives that the client has equal preference for - * the order of the given alternatives is used as a tie breaker (first one wins). - * - * If none of the given alternatives is acceptable to the client the methods return `None`. - */ - def pickContentType(alternatives: List[Alternative]): Option[ContentType] = - alternatives - .map(alt ⇒ alt → qValueFor(alt)) - .sortBy(-_._2) - .collectFirst { case (alt, q) if q > 0f ⇒ alt } - .flatMap { - case Alternative.ContentType(ct) ⇒ Some(ct) - case Alternative.MediaType(mt) ⇒ csn.pickBest.map(mt.withCharset) - } -} - -object ContentNegotiator { - sealed trait Alternative { - def mediaType: MediaType - def format: String - } - object Alternative { - implicit def apply(contentType: model.ContentType): ContentType = ContentType(contentType) - implicit def apply(mediaType: model.MediaType): Alternative = - mediaType match { - case x: model.MediaType.Binary ⇒ ContentType(x) - case x: model.MediaType.WithFixedCharset ⇒ ContentType(x) - case x: model.MediaType.WithOpenCharset ⇒ MediaType(x) - } - - final case class ContentType(contentType: model.ContentType) extends Alternative { - def mediaType = contentType.mediaType - def format = contentType.toString - } - final case class MediaType(mediaType: model.MediaType.WithOpenCharset) extends Alternative { - def format = mediaType.toString - } - } - - def apply(requestHeaders: Seq[HttpHeader]) = new ContentNegotiator(requestHeaders) -} - -final class EncodingNegotiator(requestHeaders: Seq[HttpHeader]) { - - /** - * The encoding-ranges accepted by the client according to given request headers, sorted by - * 1. increasing generality (i.e. most specific first) - * 2. decreasing q-value (only for ranges targeting a single HttpEncoding) - * 3. order of appearance in the `Accept-Encoding` header(s) - */ - val acceptedEncodingRanges: List[HttpEncodingRange] = - (for { - `Accept-Encoding`(encodingRanges) ← requestHeaders - range ← encodingRanges - } yield range).sortBy { // `sortBy` is stable, i.e. upholds the original order on identical keys - case _: HttpEncodingRange.`*` ⇒ 1f // most general, needs to come last - case x ⇒ -x.qValue // all others come first - }.toList - - /** - * Returns the q-value that the client (implicitly or explicitly) attaches to the given encoding. - * See http://tools.ietf.org/html/rfc7231#section-5.3.1 for details. - */ - def qValueFor(encoding: HttpEncoding): Float = - acceptedEncodingRanges match { - case Nil ⇒ 1.0f - case x ⇒ x collectFirst { case r if r matches encoding ⇒ r.qValue } getOrElse 0f - } - - /** - * Determines whether the given encoding is accepted by the client. - */ - def isAccepted(encoding: HttpEncoding): Boolean = qValueFor(encoding) > 0f - - /** - * Determines whether the request has an `Accept-Encoding` clause matching the given encoding. - */ - def hasMatchingFor(encoding: HttpEncoding): Boolean = - acceptedEncodingRanges.exists(_ matches encoding) - - /** - * Picks the best of the given encoding alternatives given the preferences - * the client indicated in the request's `Accept-Encoding` headers. - * See http://tools.ietf.org/html/rfc7231#section-5.3.4 for details on the negotiation logic. - * - * If there are several best encoding alternatives that the client has equal preference for - * the order of the given alternatives is used as a tie breaker (first one wins). - * - * If none of the given alternatives is acceptable to the client the methods return `None`. - */ - def pickEncoding(alternatives: List[HttpEncoding]): Option[HttpEncoding] = - alternatives - .map(alt ⇒ alt → qValueFor(alt)) - .sortBy(-_._2) - .collectFirst { case (alt, q) if q > 0f ⇒ alt } -} - -object EncodingNegotiator { - def apply(requestHeaders: Seq[HttpHeader]) = new EncodingNegotiator(requestHeaders) -} - -final class LanguageNegotiator(requestHeaders: Seq[HttpHeader]) { - - /** - * The language-ranges accepted by the client according to given request headers, sorted by - * 1. increasing generality (i.e. most specific first) - * 2. decreasing q-value (only for ranges targeting a single Language) - * 3. order of appearance in the `Accept-Language` header(s) - */ - val acceptedLanguageRanges: List[LanguageRange] = - (for { - `Accept-Language`(languageRanges) ← requestHeaders - range ← languageRanges - } yield range).sortBy { // `sortBy` is stable, i.e. upholds the original order on identical keys - case _: LanguageRange.`*` ⇒ 1f // most general, needs to come last - case x ⇒ -(2 * x.subTags.size + x.qValue) // more subtags -> more specific -> go first - }.toList - - /** - * Returns the q-value that the client (implicitly or explicitly) attaches to the given language. - * See http://tools.ietf.org/html/rfc7231#section-5.3.1 for details. - */ - def qValueFor(language: Language): Float = - acceptedLanguageRanges match { - case Nil ⇒ 1.0f - case x ⇒ x collectFirst { case r if r matches language ⇒ r.qValue } getOrElse 0f - } - - /** - * Determines whether the given language is accepted by the client. - */ - def isAccepted(language: Language): Boolean = qValueFor(language) > 0f - - /** - * Picks the best of the given language alternatives given the preferences - * the client indicated in the request's `Accept-Language` headers. - * See http://tools.ietf.org/html/rfc7231#section-5.3.5 for details on the negotiation logic. - * - * If there are several best language alternatives that the client has equal preference for - * the order of the given alternatives is used as a tie breaker (first one wins). - * - * If none of the given alternatives is acceptable to the client the methods return `None`. - */ - def pickLanguage(alternatives: List[Language]): Option[Language] = - alternatives - .map(alt ⇒ alt → qValueFor(alt)) - .sortBy(-_._2) - .collectFirst { case (alt, q) if q > 0f ⇒ alt } -} - -object LanguageNegotiator { - def apply(requestHeaders: Seq[HttpHeader]) = new LanguageNegotiator(requestHeaders) -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/Directive.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/Directive.scala deleted file mode 100644 index a9d3125391..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/Directive.scala +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server - -import scala.collection.immutable -import akka.http.scaladsl.server.directives.RouteDirectives -import akka.http.scaladsl.server.util._ -import akka.http.scaladsl.util.FastFuture -import akka.http.scaladsl.util.FastFuture._ -import akka.http.impl.util._ - -/** - * A directive that provides a tuple of values of type `L` to create an inner route. - */ -//#basic -abstract class Directive[L](implicit val ev: Tuple[L]) { - - /** - * Calls the inner route with a tuple of extracted values of type `L`. - * - * `tapply` is short for "tuple-apply". Usually, you will use the regular `apply` method instead, - * which is added by an implicit conversion (see `Directive.addDirectiveApply`). - */ - def tapply(f: L ⇒ Route): Route - //# - /** - * Joins two directives into one which runs the second directive if the first one rejects. - */ - def |[R >: L](that: Directive[R]): Directive[R] = - recover(rejections ⇒ directives.BasicDirectives.mapRejections(rejections ++ _) & that)(that.ev) - - /** - * Joins two directives into one which extracts the concatenation of its base directive extractions. - * NOTE: Extraction joining is an O(N) operation with N being the number of extractions on the right-side. - */ - def &(magnet: ConjunctionMagnet[L]): magnet.Out = magnet(this) - - /** - * Converts this directive into one which, instead of a tuple of type `L`, creates an - * instance of type `A` (which is usually a case class). - */ - def as[A](constructor: ConstructFromTuple[L, A]): Directive1[A] = { - def validatedMap[R](f: L ⇒ R)(implicit tupler: Tupler[R]): Directive[tupler.Out] = - Directive[tupler.Out] { inner ⇒ - tapply { values ⇒ ctx ⇒ - try inner(tupler(f(values)))(ctx) - catch { - case e: IllegalArgumentException ⇒ ctx.reject(ValidationRejection(e.getMessage.nullAsEmpty, Some(e))) - } - } - }(tupler.OutIsTuple) - - validatedMap(constructor) - } - - /** - * Maps over this directive using the given function, which can produce either a tuple or any other value - * (which will then we wrapped into a [[scala.Tuple1]]). - */ - def tmap[R](f: L ⇒ R)(implicit tupler: Tupler[R]): Directive[tupler.Out] = - Directive[tupler.Out] { inner ⇒ tapply { values ⇒ inner(tupler(f(values))) } }(tupler.OutIsTuple) - - /** - * Flatmaps this directive using the given function. - */ - def tflatMap[R: Tuple](f: L ⇒ Directive[R]): Directive[R] = - Directive[R] { inner ⇒ tapply { values ⇒ f(values) tapply inner } } - - /** - * Creates a new [[akka.http.scaladsl.server.Directive0]], which passes if the given predicate matches the current - * extractions or rejects with the given rejections. - */ - def trequire(predicate: L ⇒ Boolean, rejections: Rejection*): Directive0 = - tfilter(predicate, rejections: _*).tflatMap(_ ⇒ Directive.Empty) - - /** - * Creates a new directive of the same type, which passes if the given predicate matches the current - * extractions or rejects with the given rejections. - */ - def tfilter(predicate: L ⇒ Boolean, rejections: Rejection*): Directive[L] = - Directive[L] { inner ⇒ tapply { values ⇒ ctx ⇒ if (predicate(values)) inner(values)(ctx) else ctx.reject(rejections: _*) } } - - /** - * Creates a new directive that is able to recover from rejections that were produced by `this` Directive - * **before the inner route was applied**. - */ - def recover[R >: L: Tuple](recovery: immutable.Seq[Rejection] ⇒ Directive[R]): Directive[R] = - Directive[R] { inner ⇒ ctx ⇒ - import ctx.executionContext - @volatile var rejectedFromInnerRoute = false - tapply({ list ⇒ c ⇒ rejectedFromInnerRoute = true; inner(list)(c) })(ctx).fast.flatMap { - case RouteResult.Rejected(rejections) if !rejectedFromInnerRoute ⇒ recovery(rejections).tapply(inner)(ctx) - case x ⇒ FastFuture.successful(x) - } - } - - /** - * Variant of `recover` that only recovers from rejections handled by the given PartialFunction. - */ - def recoverPF[R >: L: Tuple](recovery: PartialFunction[immutable.Seq[Rejection], Directive[R]]): Directive[R] = - recover { rejections ⇒ recovery.applyOrElse(rejections, (rejs: Seq[Rejection]) ⇒ RouteDirectives.reject(rejs: _*)) } - - //#basic -} -//# - -object Directive { - - /** - * Constructs a directive from a function literal. - */ - def apply[T: Tuple](f: (T ⇒ Route) ⇒ Route): Directive[T] = - new Directive[T] { def tapply(inner: T ⇒ Route) = f(inner) } - - /** - * A Directive that always passes the request on to its inner route (i.e. does nothing). - */ - val Empty: Directive0 = Directive(_(())) - - /** - * Adds `apply` to all Directives with 1 or more extractions, - * which allows specifying an n-ary function to receive the extractions instead of a Function1[TupleX, Route]. - */ - implicit def addDirectiveApply[L](directive: Directive[L])(implicit hac: ApplyConverter[L]): hac.In ⇒ Route = - f ⇒ directive.tapply(hac(f)) - - /** - * Adds `apply` to Directive0. Note: The `apply` parameter is call-by-name to ensure consistent execution behavior - * with the directives producing extractions. - */ - implicit def addByNameNullaryApply(directive: Directive0): (⇒ Route) ⇒ Route = - r ⇒ directive.tapply(_ ⇒ r) - - implicit class SingleValueModifiers[T](underlying: Directive1[T]) extends AnyRef { - def map[R](f: T ⇒ R)(implicit tupler: Tupler[R]): Directive[tupler.Out] = - underlying.tmap { case Tuple1(value) ⇒ f(value) } - - def flatMap[R: Tuple](f: T ⇒ Directive[R]): Directive[R] = - underlying.tflatMap { case Tuple1(value) ⇒ f(value) } - - def require(predicate: T ⇒ Boolean, rejections: Rejection*): Directive0 = - underlying.filter(predicate, rejections: _*).tflatMap(_ ⇒ Empty) - - def filter(predicate: T ⇒ Boolean, rejections: Rejection*): Directive1[T] = - underlying.tfilter({ case Tuple1(value) ⇒ predicate(value) }, rejections: _*) - } -} - -trait ConjunctionMagnet[L] { - type Out - def apply(underlying: Directive[L]): Out -} - -object ConjunctionMagnet { - implicit def fromDirective[L, R](other: Directive[R])(implicit join: TupleOps.Join[L, R]): ConjunctionMagnet[L] { type Out = Directive[join.Out] } = - new ConjunctionMagnet[L] { - type Out = Directive[join.Out] - def apply(underlying: Directive[L]) = - Directive[join.Out] { inner ⇒ - underlying.tapply { prefix ⇒ other.tapply { suffix ⇒ inner(join(prefix, suffix)) } } - }(Tuple.yes) // we know that join will only ever produce tuples - } - - implicit def fromStandardRoute[L](route: StandardRoute): ConjunctionMagnet[L] { type Out = StandardRoute } = - new ConjunctionMagnet[L] { - type Out = StandardRoute - def apply(underlying: Directive[L]) = StandardRoute(underlying.tapply(_ ⇒ route)) - } - - implicit def fromRouteGenerator[T, R <: Route](generator: T ⇒ R): ConjunctionMagnet[Unit] { type Out = RouteGenerator[T] } = - new ConjunctionMagnet[Unit] { - type Out = RouteGenerator[T] - def apply(underlying: Directive0) = value ⇒ underlying.tapply(_ ⇒ generator(value)) - } -} \ No newline at end of file diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/Directives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/Directives.scala deleted file mode 100644 index 489ea7375a..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/Directives.scala +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server - -import directives._ - -/** - * Collects all default directives into one trait for simple importing. - * - * See [[akka.http.javadsl.server.AllDirectives]] for JavaDSL equivalent of this trait. - */ -trait Directives extends RouteConcatenation - with BasicDirectives - with CacheConditionDirectives - with CookieDirectives - with DebuggingDirectives - with CodingDirectives - with ExecutionDirectives - with FileAndResourceDirectives - with FileUploadDirectives - with FormFieldDirectives - with FutureDirectives - with HeaderDirectives - with HostDirectives - with MarshallingDirectives - with MethodDirectives - with MiscDirectives - with ParameterDirectives - with TimeoutDirectives - with PathDirectives - with RangeDirectives - with RespondWithDirectives - with RouteDirectives - with SchemeDirectives - with SecurityDirectives - with WebSocketDirectives - with FramedEntityStreamingDirectives - -object Directives extends Directives diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/ExceptionHandler.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/ExceptionHandler.scala deleted file mode 100644 index 10fc8e10ed..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/ExceptionHandler.scala +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server - -import scala.util.control.NonFatal -import akka.http.scaladsl.settings.RoutingSettings -import akka.http.scaladsl.model._ -import StatusCodes._ - -trait ExceptionHandler extends ExceptionHandler.PF { - - /** - * Creates a new [[ExceptionHandler]] which uses the given one as fallback for this one. - */ - def withFallback(that: ExceptionHandler): ExceptionHandler - - /** - * "Seals" this handler by attaching a default handler as fallback if necessary. - */ - def seal(settings: RoutingSettings): ExceptionHandler -} - -object ExceptionHandler { - type PF = PartialFunction[Throwable, Route] - - implicit def apply(pf: PF): ExceptionHandler = apply(knownToBeSealed = false)(pf) - - private def apply(knownToBeSealed: Boolean)(pf: PF): ExceptionHandler = - new ExceptionHandler { - def isDefinedAt(error: Throwable) = pf.isDefinedAt(error) - def apply(error: Throwable) = pf(error) - def withFallback(that: ExceptionHandler): ExceptionHandler = - if (!knownToBeSealed) ExceptionHandler(knownToBeSealed = false)(this orElse that) else this - def seal(settings: RoutingSettings): ExceptionHandler = - if (!knownToBeSealed) ExceptionHandler(knownToBeSealed = true)(this orElse default(settings)) else this - } - - def default(settings: RoutingSettings): ExceptionHandler = - apply(knownToBeSealed = true) { - case IllegalRequestException(info, status) ⇒ ctx ⇒ { - ctx.log.warning( - "Illegal request {}\n\t{}\n\tCompleting with '{}' response", - ctx.request, info.formatPretty, status) - ctx.complete((status, info.format(settings.verboseErrorMessages))) - } - case NonFatal(e) ⇒ ctx ⇒ { - ctx.log.error(e, "Error during processing of request {}", ctx.request) - ctx.complete(InternalServerError) - } - } - - /** - * Creates a sealed ExceptionHandler from the given one. Returns the default handler if the given one - * is `null`. - */ - def seal(handler: ExceptionHandler)(implicit settings: RoutingSettings): ExceptionHandler = - if (handler ne null) handler.seal(settings) else ExceptionHandler.default(settings) -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/PathMatcher.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/PathMatcher.scala deleted file mode 100644 index 2ca514b0bc..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/PathMatcher.scala +++ /dev/null @@ -1,536 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server - -import java.util.UUID -import scala.util.matching.Regex -import scala.annotation.tailrec -import akka.http.scaladsl.server.util.Tuple -import akka.http.scaladsl.server.util.TupleOps._ -import akka.http.scaladsl.common.NameOptionReceptacle -import akka.http.scaladsl.model.Uri.Path -import akka.http.impl.util._ - -/** - * A PathMatcher tries to match a prefix of a given string and returns either a PathMatcher.Matched instance - * if matched, otherwise PathMatcher.Unmatched. - */ -abstract class PathMatcher[L](implicit val ev: Tuple[L]) extends (Path ⇒ PathMatcher.Matching[L]) { self ⇒ - import PathMatcher._ - - def / : PathMatcher[L] = this ~ PathMatchers.Slash - - def /[R](other: PathMatcher[R])(implicit join: Join[L, R]): PathMatcher[join.Out] = - this ~ PathMatchers.Slash ~ other - - def |[R >: L: Tuple](other: PathMatcher[_ <: R]): PathMatcher[R] = - new PathMatcher[R] { - def apply(path: Path) = self(path) orElse other(path) - } - - def ~[R](other: PathMatcher[R])(implicit join: Join[L, R]): PathMatcher[join.Out] = { - implicit val joinProducesTuple = Tuple.yes[join.Out] - transform(_.andThen((restL, valuesL) ⇒ other(restL).map(join(valuesL, _)))) - } - - def unary_!(): PathMatcher0 = - new PathMatcher[Unit] { - def apply(path: Path) = if (self(path) eq Unmatched) Matched(path, ()) else Unmatched - } - - def transform[R: Tuple](f: Matching[L] ⇒ Matching[R]): PathMatcher[R] = - new PathMatcher[R] { def apply(path: Path) = f(self(path)) } - - def tmap[R: Tuple](f: L ⇒ R): PathMatcher[R] = transform(_.map(f)) - - def tflatMap[R: Tuple](f: L ⇒ Option[R]): PathMatcher[R] = transform(_.flatMap(f)) - - /** - * Same as `repeat(min = count, max = count)`. - */ - def repeat(count: Int)(implicit lift: PathMatcher.Lift[L, List]): PathMatcher[lift.Out] = - repeat(min = count, max = count) - - /** - * Same as `repeat(min = count, max = count, separator = separator)`. - */ - def repeat(count: Int, separator: PathMatcher0)(implicit lift: PathMatcher.Lift[L, List]): PathMatcher[lift.Out] = - repeat(min = count, max = count, separator = separator) - - /** - * Turns this `PathMatcher` into one that matches a number of times (with the given separator) - * and potentially extracts a `List` of the underlying matcher's extractions. - * If less than `min` applications of the underlying matcher have succeeded the produced matcher fails, - * otherwise it matches up to the given `max` number of applications. - * Note that it won't fail even if more than `max` applications could succeed! - * The "surplus" path elements will simply be left unmatched. - * - * The result type depends on the type of the underlying matcher: - * - * - * - * - * - * - *
If a `matcher` is of typethen `matcher.repeat(...)` is of type
`PathMatcher0``PathMatcher0`
`PathMatcher1[T]``PathMatcher1[List[T]`
`PathMatcher[L :Tuple]``PathMatcher[List[L]]`
- */ - def repeat(min: Int, max: Int, separator: PathMatcher0 = PathMatchers.Neutral)(implicit lift: PathMatcher.Lift[L, List]): PathMatcher[lift.Out] = - new PathMatcher[lift.Out]()(lift.OutIsTuple) { - require(min >= 0, "`min` must be >= 0") - require(max >= min, "`max` must be >= `min`") - def apply(path: Path) = rec(path, 1) - def rec(path: Path, count: Int): Matching[lift.Out] = { - def done = if (count >= min) Matched(path, lift()) else Unmatched - if (count <= max) { - self(path) match { - case Matched(remaining, extractions) ⇒ - def done1 = if (count >= min) Matched(remaining, lift(extractions)) else Unmatched - separator(remaining) match { - case Matched(remaining2, _) ⇒ rec(remaining2, count + 1) match { - case Matched(`remaining2`, _) ⇒ done1 // we made no progress, so "go back" to before the separator - case Matched(rest, result) ⇒ Matched(rest, lift(extractions, result)) - case Unmatched ⇒ Unmatched - } - case Unmatched ⇒ done1 - } - case Unmatched ⇒ done - } - } else done - } - } -} - -object PathMatcher extends ImplicitPathMatcherConstruction { - sealed abstract class Matching[+L: Tuple] { - def map[R: Tuple](f: L ⇒ R): Matching[R] - def flatMap[R: Tuple](f: L ⇒ Option[R]): Matching[R] - def andThen[R: Tuple](f: (Path, L) ⇒ Matching[R]): Matching[R] - def orElse[R >: L](other: ⇒ Matching[R]): Matching[R] - } - case class Matched[L: Tuple](pathRest: Path, extractions: L) extends Matching[L] { - def map[R: Tuple](f: L ⇒ R) = Matched(pathRest, f(extractions)) - def flatMap[R: Tuple](f: L ⇒ Option[R]) = f(extractions) match { - case Some(valuesR) ⇒ Matched(pathRest, valuesR) - case None ⇒ Unmatched - } - def andThen[R: Tuple](f: (Path, L) ⇒ Matching[R]) = f(pathRest, extractions) - def orElse[R >: L](other: ⇒ Matching[R]) = this - } - object Matched { val Empty = Matched(Path.Empty, ()) } - case object Unmatched extends Matching[Nothing] { - def map[R: Tuple](f: Nothing ⇒ R) = this - def flatMap[R: Tuple](f: Nothing ⇒ Option[R]) = this - def andThen[R: Tuple](f: (Path, Nothing) ⇒ Matching[R]) = this - def orElse[R](other: ⇒ Matching[R]) = other - } - - /** - * Creates a PathMatcher that always matches, consumes nothing and extracts the given Tuple of values. - */ - def provide[L: Tuple](extractions: L): PathMatcher[L] = - new PathMatcher[L] { - def apply(path: Path) = Matched(path, extractions)(ev) - } - - /** - * Creates a PathMatcher that matches and consumes the given path prefix and extracts the given list of extractions. - * If the given prefix is empty the returned PathMatcher matches always and consumes nothing. - */ - def apply[L: Tuple](prefix: Path, extractions: L): PathMatcher[L] = - if (prefix.isEmpty) provide(extractions) - else new PathMatcher[L] { - def apply(path: Path) = - if (path startsWith prefix) Matched(path dropChars prefix.charCount, extractions)(ev) - else Unmatched - } - - /** Provoke implicit conversions to PathMatcher to be applied */ - def apply[L](magnet: PathMatcher[L]): PathMatcher[L] = magnet - - implicit class PathMatcher1Ops[T](matcher: PathMatcher1[T]) { - def map[R](f: T ⇒ R): PathMatcher1[R] = matcher.tmap { case Tuple1(e) ⇒ Tuple1(f(e)) } - def flatMap[R](f: T ⇒ Option[R]): PathMatcher1[R] = - matcher.tflatMap { case Tuple1(e) ⇒ f(e).map(x ⇒ Tuple1(x)) } - } - - implicit class EnhancedPathMatcher[L](underlying: PathMatcher[L]) { - def ?(implicit lift: PathMatcher.Lift[L, Option]): PathMatcher[lift.Out] = - new PathMatcher[lift.Out]()(lift.OutIsTuple) { - def apply(path: Path) = underlying(path) match { - case Matched(rest, extractions) ⇒ Matched(rest, lift(extractions)) - case Unmatched ⇒ Matched(path, lift()) - } - } - } - - sealed trait Lift[L, M[+_]] { - type Out - def OutIsTuple: Tuple[Out] - def apply(): Out - def apply(value: L): Out - def apply(value: L, more: Out): Out - } - object Lift extends LowLevelLiftImplicits { - trait MOps[M[+_]] { - def apply(): M[Nothing] - def apply[T](value: T): M[T] - def apply[T](value: T, more: M[T]): M[T] - } - object MOps { - implicit val OptionMOps: MOps[Option] = - new MOps[Option] { - def apply(): Option[Nothing] = None - def apply[T](value: T): Option[T] = Some(value) - def apply[T](value: T, more: Option[T]): Option[T] = Some(value) - } - implicit val ListMOps: MOps[List] = - new MOps[List] { - def apply(): List[Nothing] = Nil - def apply[T](value: T): List[T] = value :: Nil - def apply[T](value: T, more: List[T]): List[T] = value :: more - } - } - implicit def liftUnit[M[+_]]: Lift[Unit, M] { type Out = Unit } = - new Lift[Unit, M] { - type Out = Unit - def OutIsTuple = implicitly[Tuple[Out]] - def apply() = () - def apply(value: Unit) = value - def apply(value: Unit, more: Out) = value - } - implicit def liftSingleElement[A, M[+_]](implicit mops: MOps[M]): Lift[Tuple1[A], M] { type Out = Tuple1[M[A]] } = - new Lift[Tuple1[A], M] { - type Out = Tuple1[M[A]] - def OutIsTuple = implicitly[Tuple[Out]] - def apply() = Tuple1(mops()) - def apply(value: Tuple1[A]) = Tuple1(mops(value._1)) - def apply(value: Tuple1[A], more: Out) = Tuple1(mops(value._1, more._1)) - } - } - - trait LowLevelLiftImplicits { - import Lift._ - implicit def default[T, M[+_]](implicit mops: MOps[M]): Lift[T, M] { type Out = Tuple1[M[T]] } = - new Lift[T, M] { - type Out = Tuple1[M[T]] - def OutIsTuple = implicitly[Tuple[Out]] - def apply() = Tuple1(mops()) - def apply(value: T) = Tuple1(mops(value)) - def apply(value: T, more: Out) = Tuple1(mops(value, more._1)) - } - } -} - -/** - * @groupname pathmatcherimpl Path matcher implicits - * @groupprio pathmatcherimpl 172 - */ -trait ImplicitPathMatcherConstruction { - import PathMatcher._ - - /** - * Creates a PathMatcher that consumes (a prefix of) the first path segment - * (if the path begins with a segment) and extracts a given value. - * - * @group pathmatcherimpl - */ - implicit def _stringExtractionPair2PathMatcher[T](tuple: (String, T)): PathMatcher1[T] = - PathMatcher(tuple._1 :: Path.Empty, Tuple1(tuple._2)) - - /** - * Creates a PathMatcher that consumes (a prefix of) the first path segment - * (if the path begins with a segment). - * - * @group pathmatcherimpl - */ - implicit def _segmentStringToPathMatcher(segment: String): PathMatcher0 = - PathMatcher(segment :: Path.Empty, ()) - - /** - * @group pathmatcherimpl - */ - implicit def _stringNameOptionReceptacle2PathMatcher(nr: NameOptionReceptacle[String]): PathMatcher0 = - PathMatcher(nr.name).? - - /** - * Creates a PathMatcher that consumes (a prefix of) the first path segment - * if the path begins with a segment (a prefix of) which matches the given regex. - * Extracts either the complete match (if the regex doesn't contain a capture group) or - * the capture group (if the regex contains exactly one). - * If the regex contains more than one capture group the method throws an IllegalArgumentException. - * - * @group pathmatcherimpl - */ - implicit def _regex2PathMatcher(regex: Regex): PathMatcher1[String] = regex.groupCount match { - case 0 ⇒ new PathMatcher1[String] { - def apply(path: Path) = path match { - case Path.Segment(segment, tail) ⇒ regex findPrefixOf segment match { - case Some(m) ⇒ Matched(segment.substring(m.length) :: tail, Tuple1(m)) - case None ⇒ Unmatched - } - case _ ⇒ Unmatched - } - } - case 1 ⇒ new PathMatcher1[String] { - def apply(path: Path) = path match { - case Path.Segment(segment, tail) ⇒ regex findPrefixMatchOf segment match { - case Some(m) ⇒ Matched(segment.substring(m.end) :: tail, Tuple1(m.group(1))) - case None ⇒ Unmatched - } - case _ ⇒ Unmatched - } - } - case _ ⇒ throw new IllegalArgumentException("Path regex '" + regex.pattern.pattern + - "' must not contain more than one capturing group") - } - /** - * Creates a PathMatcher from the given Map of path segments (prefixes) to extracted values. - * If the unmatched path starts with a segment having one of the maps keys as a prefix - * the matcher consumes this path segment (prefix) and extracts the corresponding map value. - * - * @group pathmatcherimpl - */ - implicit def _valueMap2PathMatcher[T](valueMap: Map[String, T]): PathMatcher1[T] = - if (valueMap.isEmpty) PathMatchers.nothingMatcher - else valueMap.map { case (prefix, value) ⇒ _stringExtractionPair2PathMatcher((prefix, value)) }.reduceLeft(_ | _) -} - -/** - * @groupname pathmatcher Path matchers - * @groupprio pathmatcher 171 - */ -trait PathMatchers { - import PathMatcher._ - - /** - * Converts a path string containing slashes into a PathMatcher that interprets slashes as - * path segment separators. - * - * @group pathmatcher - */ - def separateOnSlashes(string: String): PathMatcher0 = { - @tailrec def split(ix: Int = 0, matcher: PathMatcher0 = null): PathMatcher0 = { - val nextIx = string.indexOf('/', ix) - def append(m: PathMatcher0) = if (matcher eq null) m else matcher / m - if (nextIx < 0) append(string.substring(ix)) - else split(nextIx + 1, append(string.substring(ix, nextIx))) - } - split() - } - - /** - * A PathMatcher that matches a single slash character ('/'). - * - * @group pathmatcher - */ - object Slash extends PathMatcher0 { - def apply(path: Path) = path match { - case Path.Slash(tail) ⇒ Matched(tail, ()) - case _ ⇒ Unmatched - } - } - - /** - * A PathMatcher that matches the very end of the requests URI path. - * - * @group pathmatcher - */ - object PathEnd extends PathMatcher0 { - def apply(path: Path) = path match { - case Path.Empty ⇒ Matched.Empty - case _ ⇒ Unmatched - } - } - - /** - * A PathMatcher that matches and extracts the complete remaining, - * unmatched part of the request's URI path as an (encoded!) String. - * If you need access to the remaining unencoded elements of the path - * use the `RemainingPath` matcher! - * - * @group pathmatcher - */ - object Remaining extends PathMatcher1[String] { - def apply(path: Path) = Matched(Path.Empty, Tuple1(path.toString)) - } - - /** - * A PathMatcher that matches and extracts the complete remaining, - * unmatched part of the request's URI path. - * - * @group pathmatcher - */ - object RemainingPath extends PathMatcher1[Path] { - def apply(path: Path) = Matched(Path.Empty, Tuple1(path)) - } - - /** - * A PathMatcher that efficiently matches a number of digits and extracts their (non-negative) Int value. - * The matcher will not match 0 digits or a sequence of digits that would represent an Int value larger - * than Int.MaxValue. - * - * @group pathmatcher - */ - object IntNumber extends NumberMatcher[Int](Int.MaxValue, 10) { - def fromChar(c: Char) = fromDecimalChar(c) - } - - /** - * A PathMatcher that efficiently matches a number of digits and extracts their (non-negative) Long value. - * The matcher will not match 0 digits or a sequence of digits that would represent an Long value larger - * than Long.MaxValue. - * - * @group pathmatcher - */ - object LongNumber extends NumberMatcher[Long](Long.MaxValue, 10) { - def fromChar(c: Char) = fromDecimalChar(c) - } - - /** - * A PathMatcher that efficiently matches a number of hex-digits and extracts their (non-negative) Int value. - * The matcher will not match 0 digits or a sequence of digits that would represent an Int value larger - * than Int.MaxValue. - * - * @group pathmatcher - */ - object HexIntNumber extends NumberMatcher[Int](Int.MaxValue, 16) { - def fromChar(c: Char) = fromHexChar(c) - } - - /** - * A PathMatcher that efficiently matches a number of hex-digits and extracts their (non-negative) Long value. - * The matcher will not match 0 digits or a sequence of digits that would represent an Long value larger - * than Long.MaxValue. - * - * @group pathmatcher - */ - object HexLongNumber extends NumberMatcher[Long](Long.MaxValue, 16) { - def fromChar(c: Char) = fromHexChar(c) - } - - // common implementation of Number matchers - /** - * @group pathmatcher - */ - abstract class NumberMatcher[@specialized(Int, Long) T](max: T, base: T)(implicit x: Integral[T]) - extends PathMatcher1[T] { - - import x._ // import implicit conversions for numeric operators - val minusOne = x.zero - x.one - val maxDivBase = max / base - - def apply(path: Path) = path match { - case Path.Segment(segment, tail) ⇒ - @tailrec def digits(ix: Int = 0, value: T = minusOne): Matching[Tuple1[T]] = { - val a = if (ix < segment.length) fromChar(segment charAt ix) else minusOne - if (a == minusOne) { - if (value == minusOne) Unmatched - else Matched(if (ix < segment.length) segment.substring(ix) :: tail else tail, Tuple1(value)) - } else { - if (value == minusOne) digits(ix + 1, a) - else if (value <= maxDivBase && value * base <= max - a) // protect from overflow - digits(ix + 1, value * base + a) - else Unmatched - } - } - digits() - - case _ ⇒ Unmatched - } - - def fromChar(c: Char): T - - def fromDecimalChar(c: Char): T = if ('0' <= c && c <= '9') x.fromInt(c - '0') else minusOne - - def fromHexChar(c: Char): T = - if ('0' <= c && c <= '9') x.fromInt(c - '0') else { - val cn = c | 0x20 // normalize to lowercase - if ('a' <= cn && cn <= 'f') x.fromInt(cn - 'a' + 10) else minusOne - } - } - - /** - * A PathMatcher that matches and extracts a Double value. The matched string representation is the pure decimal, - * optionally signed form of a double value, i.e. without exponent. - * - * @group pathmatcher - */ - val DoubleNumber: PathMatcher1[Double] = - PathMatcher("""[+-]?\d*\.?\d*""".r) flatMap { string ⇒ - try Some(java.lang.Double.parseDouble(string)) - catch { case _: NumberFormatException ⇒ None } - } - - /** - * A PathMatcher that matches and extracts a java.util.UUID instance. - * - * @group pathmatcher - */ - val JavaUUID: PathMatcher1[UUID] = - PathMatcher("""[\da-fA-F]{8}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{12}""".r) flatMap { string ⇒ - try Some(UUID.fromString(string)) - catch { case _: IllegalArgumentException ⇒ None } - } - - /** - * A PathMatcher that always matches, doesn't consume anything and extracts nothing. - * Serves mainly as a neutral element in PathMatcher composition. - * - * @group pathmatcher - */ - val Neutral: PathMatcher0 = PathMatcher.provide(()) - - /** - * A PathMatcher that matches if the unmatched path starts with a path segment. - * If so the path segment is extracted as a String. - * - * @group pathmatcher - */ - object Segment extends PathMatcher1[String] { - def apply(path: Path) = path match { - case Path.Segment(segment, tail) ⇒ Matched(tail, Tuple1(segment)) - case _ ⇒ Unmatched - } - } - - /** - * A PathMatcher that matches up to 128 remaining segments as a List[String]. - * This can also be no segments resulting in the empty list. - * If the path has a trailing slash this slash will *not* be matched. - * - * @group pathmatcher - */ - val Segments: PathMatcher1[List[String]] = Segments(min = 0, max = 128) - - /** - * A PathMatcher that matches the given number of path segments (separated by slashes) as a List[String]. - * If there are more than `count` segments present the remaining ones will be left unmatched. - * If the path has a trailing slash this slash will *not* be matched. - * - * @group pathmatcher - */ - def Segments(count: Int): PathMatcher1[List[String]] = Segment.repeat(count, separator = Slash) - - /** - * A PathMatcher that matches between `min` and `max` (both inclusively) path segments (separated by slashes) - * as a List[String]. If there are more than `count` segments present the remaining ones will be left unmatched. - * If the path has a trailing slash this slash will *not* be matched. - * - * @group pathmatcher - */ - def Segments(min: Int, max: Int): PathMatcher1[List[String]] = Segment.repeat(min, max, separator = Slash) - - /** - * A PathMatcher that never matches anything. - * - * @group pathmatcher - */ - def nothingMatcher[L: Tuple]: PathMatcher[L] = - new PathMatcher[L] { - def apply(p: Path) = Unmatched - } -} - -object PathMatchers extends PathMatchers diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/Rejection.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/Rejection.scala deleted file mode 100644 index eb912fc931..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/Rejection.scala +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server - -import java.lang.Iterable -import java.util.Optional -import java.util.function.Function - -import akka.japi.Util - -import scala.collection.immutable -import akka.http.scaladsl.model._ -import akka.http.javadsl -import akka.http.javadsl.{ model, server ⇒ jserver } -import headers._ -import akka.http.impl.util.JavaMapping._ -import akka.http.impl.util.JavaMapping.Implicits._ -import akka.http.javadsl.server.RoutingJavaMapping -import RoutingJavaMapping._ -import akka.pattern.CircuitBreakerOpenException -import akka.http.javadsl.model.headers.{ HttpOrigin ⇒ JHttpOrigin } -import akka.http.scaladsl.model.headers.{ HttpOrigin ⇒ SHttpOrigin } - -import scala.collection.JavaConverters._ -import scala.compat.java8.OptionConverters - -/** - * A rejection encapsulates a specific reason why a Route was not able to handle a request. Rejections are gathered - * up over the course of a Route evaluation and finally converted to [[akka.http.scaladsl.model.HttpResponse]]s by the - * `handleRejections` directive, if there was no way for the request to be completed. - */ -trait Rejection extends akka.http.javadsl.server.Rejection - -trait RejectionWithOptionalCause extends Rejection { - final def getCause: Optional[Throwable] = OptionConverters.toJava(cause) - def cause: Option[Throwable] -} - -/** - * Rejection created by method filters. - * Signals that the request was rejected because the HTTP method is unsupported. - */ -final case class MethodRejection(supported: HttpMethod) - extends jserver.MethodRejection with Rejection - -/** - * Rejection created by scheme filters. - * Signals that the request was rejected because the Uri scheme is unsupported. - */ -final case class SchemeRejection(supported: String) - extends jserver.SchemeRejection with Rejection - -/** - * Rejection created by parameter filters. - * Signals that the request was rejected because a query parameter was not found. - */ -final case class MissingQueryParamRejection(parameterName: String) - extends jserver.MissingQueryParamRejection with Rejection - -/** - * Rejection created by parameter filters. - * Signals that the request was rejected because a query parameter could not be interpreted. - */ -final case class MalformedQueryParamRejection(parameterName: String, errorMsg: String, cause: Option[Throwable] = None) - extends jserver.MalformedQueryParamRejection with RejectionWithOptionalCause - -/** - * Rejection created by form field filters. - * Signals that the request was rejected because a form field was not found. - */ -final case class MissingFormFieldRejection(fieldName: String) - extends jserver.MissingFormFieldRejection with Rejection - -/** - * Rejection created by form field filters. - * Signals that the request was rejected because a form field could not be interpreted. - */ -final case class MalformedFormFieldRejection(fieldName: String, errorMsg: String, cause: Option[Throwable] = None) - extends jserver.MalformedFormFieldRejection with RejectionWithOptionalCause - -/** - * Rejection created by header directives. - * Signals that the request was rejected because a required header could not be found. - */ -final case class MissingHeaderRejection(headerName: String) - extends jserver.MissingHeaderRejection with Rejection - -/** - * Rejection created by header directives. - * Signals that the request was rejected because a header value is malformed. - */ -final case class MalformedHeaderRejection(headerName: String, errorMsg: String, cause: Option[Throwable] = None) - extends jserver.MalformedHeaderRejection with RejectionWithOptionalCause - -/** - * Rejection created by [[akka.http.scaladsl.server.directives.HeaderDirectives.checkSameOrigin]]. - * Signals that the request was rejected because `Origin` header value is invalid. - */ -final case class InvalidOriginRejection(allowedOrigins: immutable.Seq[SHttpOrigin]) - extends jserver.InvalidOriginRejection with Rejection { - override def getAllowedOrigins: java.util.List[JHttpOrigin] = allowedOrigins.map(_.asJava).asJava -} - -/** - * Rejection created by unmarshallers. - * Signals that the request was rejected because the requests content-type is unsupported. - */ -final case class UnsupportedRequestContentTypeRejection(supported: immutable.Set[ContentTypeRange]) - extends jserver.UnsupportedRequestContentTypeRejection with Rejection { - override def getSupported: java.util.Set[model.ContentTypeRange] = - scala.collection.mutable.Set(supported.map(_.asJava).toVector: _*).asJava // TODO optimise -} - -/** - * Rejection created by decoding filters. - * Signals that the request was rejected because the requests content encoding is unsupported. - */ -final case class UnsupportedRequestEncodingRejection(supported: HttpEncoding) - extends jserver.UnsupportedRequestEncodingRejection with Rejection - -/** - * Rejection created by range directives. - * Signals that the request was rejected because the requests contains only unsatisfiable ByteRanges. - * The actualEntityLength gives the client a hint to create satisfiable ByteRanges. - */ -final case class UnsatisfiableRangeRejection(unsatisfiableRanges: immutable.Seq[ByteRange], actualEntityLength: Long) - extends jserver.UnsatisfiableRangeRejection with Rejection { - override def getUnsatisfiableRanges: Iterable[model.headers.ByteRange] = unsatisfiableRanges.map(_.asJava).asJava -} - -/** - * Rejection created by range directives. - * Signals that the request contains too many ranges. An irregular high number of ranges - * indicates a broken client or a denial of service attack. - */ -final case class TooManyRangesRejection(maxRanges: Int) - extends jserver.TooManyRangesRejection with Rejection - -/** - * Rejection created by unmarshallers. - * Signals that the request was rejected because unmarshalling failed with an error that wasn't - * an `IllegalArgumentException`. Usually that means that the request content was not of the expected format. - * Note that semantic issues with the request content (e.g. because some parameter was out of range) - * will usually trigger a `ValidationRejection` instead. - */ -final case class MalformedRequestContentRejection(message: String, cause: Throwable) - extends jserver.MalformedRequestContentRejection with Rejection { override def getCause: Throwable = cause } - -/** - * Rejection created by unmarshallers. - * Signals that the request was rejected because an message body entity was expected but not supplied. - */ -case object RequestEntityExpectedRejection - extends jserver.RequestEntityExpectedRejection with Rejection - -/** - * Rejection created by marshallers. - * Signals that the request was rejected because the service is not capable of producing a response entity whose - * content type is accepted by the client - */ -final case class UnacceptedResponseContentTypeRejection(supported: immutable.Set[ContentNegotiator.Alternative]) - extends jserver.UnacceptedResponseContentTypeRejection with Rejection - -/** - * Rejection created by encoding filters. - * Signals that the request was rejected because the service is not capable of producing a response entity whose - * content encoding is accepted by the client - */ -final case class UnacceptedResponseEncodingRejection(supported: immutable.Set[HttpEncoding]) - extends jserver.UnacceptedResponseEncodingRejection with Rejection { - override def getSupported: java.util.Set[model.headers.HttpEncoding] = - scala.collection.mutable.Set(supported.map(_.asJava).toVector: _*).asJava // TODO optimise -} -object UnacceptedResponseEncodingRejection { - def apply(supported: HttpEncoding): UnacceptedResponseEncodingRejection = UnacceptedResponseEncodingRejection(Set(supported)) -} - -/** - * Rejection created by the various [[akka.http.scaladsl.server.directives.SecurityDirectives]]. - * Signals that the request was rejected because the user could not be authenticated. The reason for the rejection is - * specified in the cause. - */ -final case class AuthenticationFailedRejection(cause: AuthenticationFailedRejection.Cause, challenge: HttpChallenge) - extends jserver.AuthenticationFailedRejection with Rejection - -object AuthenticationFailedRejection { - /** - * Signals the cause of the failed authentication. - */ - sealed trait Cause extends jserver.AuthenticationFailedRejection.Cause - - /** - * Signals the cause of the rejecting was that the user could not be authenticated, because the `WWW-Authenticate` - * header was not supplied. - */ - case object CredentialsMissing extends jserver.AuthenticationFailedRejection.CredentialsMissing with Cause - - /** - * Signals the cause of the rejecting was that the user could not be authenticated, because the supplied credentials - * are invalid. - */ - case object CredentialsRejected extends jserver.AuthenticationFailedRejection.CredentialsRejected with Cause -} - -/** - * Rejection created by the 'authorize' directive. - * Signals that the request was rejected because the user is not authorized. - */ -case object AuthorizationFailedRejection - extends jserver.AuthorizationFailedRejection with Rejection - -/** - * Rejection created by the `cookie` directive. - * Signals that the request was rejected because a cookie was not found. - */ -final case class MissingCookieRejection(cookieName: String) - extends jserver.MissingCookieRejection with Rejection - -/** - * Rejection created when a websocket request was expected but none was found. - */ -case object ExpectedWebSocketRequestRejection - extends jserver.ExpectedWebSocketRequestRejection with Rejection - -/** - * Rejection created when a websocket request was not handled because none of the given subprotocols - * was supported. - */ -final case class UnsupportedWebSocketSubprotocolRejection(supportedProtocol: String) - extends jserver.UnsupportedWebSocketSubprotocolRejection with Rejection - -/** - * Rejection created by the `validation` directive as well as for `IllegalArgumentExceptions` - * thrown by domain model constructors (e.g. via `require`). - * It signals that an expected value was semantically invalid. - */ -final case class ValidationRejection(message: String, cause: Option[Throwable] = None) - extends jserver.ValidationRejection with RejectionWithOptionalCause - -/** - * A special Rejection that serves as a container for a transformation function on rejections. - * It is used by some directives to "cancel" rejections that are added by later directives of a similar type. - * - * Consider this route structure for example: - * - * {{{ - * put { reject(ValidationRejection("no") } ~ get { ... } - * }}} - * - * If this structure is applied to a PUT request the list of rejections coming back contains three elements: - * - * 1. A ValidationRejection - * 2. A MethodRejection - * 3. A TransformationRejection holding a function filtering out the MethodRejection - * - * so that in the end the RejectionHandler will only see one rejection (the ValidationRejection), because the - * MethodRejection added by the `get` directive is canceled by the `put` directive (since the HTTP method - * did indeed match eventually). - */ -final case class TransformationRejection(transform: immutable.Seq[Rejection] ⇒ immutable.Seq[Rejection]) - extends jserver.TransformationRejection with Rejection { - override def getTransform = new Function[Iterable[jserver.Rejection], Iterable[jserver.Rejection]] { - override def apply(t: Iterable[jserver.Rejection]): Iterable[jserver.Rejection] = - // explicit collects instead of implicits is because of unidoc failing compilation on .asScala and .asJava here - transform(Util.immutableSeq(t).collect { case r: Rejection ⇒ r }).collect[jserver.Rejection, Seq[jserver.Rejection]] { case j: jserver.Rejection ⇒ j }.asJava // TODO "asJavaDeep" and optimise? - } -} - -/** - * Rejection created by the `onCompleteWithBreaker` directive. - * Signals that the request was rejected because the supplied circuit breaker is open and requests are failing fast. - */ -final case class CircuitBreakerOpenRejection(cause: CircuitBreakerOpenException) - extends jserver.CircuitBreakerOpenRejection with Rejection - -/** - * A Throwable wrapping a Rejection. - * Can be used for marshalling `Future[T]` or `Try[T]` instances, whose failure side is supposed to trigger a route - * rejection rather than an Exception that is handled by the nearest ExceptionHandler. - * (Custom marshallers can of course use it as well.) - */ -final case class RejectionError(rejection: Rejection) extends RuntimeException diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/RejectionHandler.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/RejectionHandler.scala deleted file mode 100644 index bc6c6a451f..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/RejectionHandler.scala +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server - -import akka.http.scaladsl.model.StatusCodes._ -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers._ -import akka.http.scaladsl.server.AuthenticationFailedRejection._ - -import scala.annotation.tailrec -import scala.collection.immutable -import scala.reflect.ClassTag - -trait RejectionHandler extends (immutable.Seq[Rejection] ⇒ Option[Route]) { self ⇒ - import RejectionHandler._ - - /** - * Creates a new [[RejectionHandler]] which uses the given one as fallback for this one. - */ - def withFallback(that: RejectionHandler): RejectionHandler = - (this, that) match { - case (a: BuiltRejectionHandler, _) if a.isDefault ⇒ this // the default handler already handles everything - case (a: BuiltRejectionHandler, b: BuiltRejectionHandler) ⇒ - new BuiltRejectionHandler(a.cases ++ b.cases, a.notFound orElse b.notFound, b.isDefault) - case _ ⇒ new RejectionHandler { - def apply(rejections: immutable.Seq[Rejection]): Option[Route] = - self(rejections) orElse that(rejections) - } - } - - /** - * "Seals" this handler by attaching a default handler as fallback if necessary. - */ - def seal: RejectionHandler = - this match { - case x: BuiltRejectionHandler if x.isDefault ⇒ x - case _ ⇒ withFallback(default) - } -} - -object RejectionHandler { - - /** - * Creates a new [[RejectionHandler]] builder. - */ - def newBuilder(): Builder = new Builder(isDefault = false) - - final class Builder private[RejectionHandler] (isDefault: Boolean) { - private[this] val cases = new immutable.VectorBuilder[Handler] - private[this] var notFound: Option[Route] = None - - /** - * Handles a single [[Rejection]] with the given partial function. - */ - def handle(pf: PartialFunction[Rejection, Route]): this.type = { - cases += CaseHandler(pf) - this - } - - /** - * Handles several Rejections of the same type at the same time. - * The seq passed to the given function is guaranteed to be non-empty. - */ - def handleAll[T <: Rejection: ClassTag](f: immutable.Seq[T] ⇒ Route): this.type = { - val runtimeClass = implicitly[ClassTag[T]].runtimeClass - cases += TypeHandler[T](runtimeClass, f) - this - } - - /** - * Handles the special "not found" case using the given [[Route]]. - */ - def handleNotFound(route: Route): this.type = { - notFound = Some(route) - this - } - - /** - * Convenience method for handling rejections created by created by the onCompleteWithBreaker directive. - * Signals that the request was rejected because the supplied circuit breaker is open and requests are failing fast. - * - * Use to customise the error response being written instead of the default [[ServiceUnavailable]] response. - */ - def handleCircuitBreakerOpenRejection(handler: CircuitBreakerOpenRejection ⇒ Route): this.type = - handle { case r: CircuitBreakerOpenRejection ⇒ handler(r) } - - def result(): RejectionHandler = - new BuiltRejectionHandler(cases.result(), notFound, isDefault) - } - - private sealed abstract class Handler - private final case class CaseHandler(pf: PartialFunction[Rejection, Route]) extends Handler - private final case class TypeHandler[T <: Rejection]( - runtimeClass: Class[_], f: immutable.Seq[T] ⇒ Route) extends Handler with PartialFunction[Rejection, T] { - def isDefinedAt(rejection: Rejection) = runtimeClass isInstance rejection - def apply(rejection: Rejection) = rejection.asInstanceOf[T] - } - - private class BuiltRejectionHandler( - val cases: Vector[Handler], - val notFound: Option[Route], - val isDefault: Boolean) extends RejectionHandler { - def apply(rejections: immutable.Seq[Rejection]): Option[Route] = - if (rejections.nonEmpty) { - @tailrec def rec(ix: Int): Option[Route] = - if (ix < cases.length) { - cases(ix) match { - case CaseHandler(pf) ⇒ - val route = rejections collectFirst pf - if (route.isEmpty) rec(ix + 1) else route - case x @ TypeHandler(_, f) ⇒ - val rejs = rejections collect x - if (rejs.isEmpty) rec(ix + 1) else Some(f(rejs)) - } - } else None - rec(0) - } else notFound - } - - import Directives._ - - /** - * Default [[RejectionHandler]] instance. - */ - final val default = - newBuilder() - .handleAll[SchemeRejection] { rejections ⇒ - val schemes = rejections.map(_.supported).mkString(", ") - complete((BadRequest, "Uri scheme not allowed, supported schemes: " + schemes)) - } - .handleAll[MethodRejection] { rejections ⇒ - val (methods, names) = rejections.map(r ⇒ r.supported → r.supported.name).unzip - complete((MethodNotAllowed, List(Allow(methods)), "HTTP method not allowed, supported methods: " + names.mkString(", "))) - } - .handle { - case AuthorizationFailedRejection ⇒ - complete((Forbidden, "The supplied authentication is not authorized to access this resource")) - } - .handle { - case MalformedFormFieldRejection(name, msg, _) ⇒ - complete((BadRequest, "The form field '" + name + "' was malformed:\n" + msg)) - } - .handle { - case MalformedHeaderRejection(headerName, msg, _) ⇒ - complete((BadRequest, s"The value of HTTP header '$headerName' was malformed:\n" + msg)) - } - .handle { - case MalformedQueryParamRejection(name, msg, _) ⇒ - complete((BadRequest, "The query parameter '" + name + "' was malformed:\n" + msg)) - } - .handle { - case MalformedRequestContentRejection(msg, _) ⇒ - complete((BadRequest, "The request content was malformed:\n" + msg)) - } - .handle { - case MissingCookieRejection(cookieName) ⇒ - complete((BadRequest, "Request is missing required cookie '" + cookieName + '\'')) - } - .handle { - case MissingFormFieldRejection(fieldName) ⇒ - complete((BadRequest, "Request is missing required form field '" + fieldName + '\'')) - } - .handle { - case MissingHeaderRejection(headerName) ⇒ - complete((BadRequest, "Request is missing required HTTP header '" + headerName + '\'')) - } - .handle { - case InvalidOriginRejection(allowedOrigins) ⇒ - complete((Forbidden, s"Allowed `Origin` header values: ${allowedOrigins.mkString(", ")}")) - } - .handle { - case MissingQueryParamRejection(paramName) ⇒ - complete((NotFound, "Request is missing required query parameter '" + paramName + '\'')) - } - .handle { - case RequestEntityExpectedRejection ⇒ - complete((BadRequest, "Request entity expected but not supplied")) - } - .handle { - case TooManyRangesRejection(_) ⇒ - complete((RequestedRangeNotSatisfiable, "Request contains too many ranges")) - } - .handle { - case CircuitBreakerOpenRejection(_) ⇒ - complete(ServiceUnavailable) - } - .handle { - case UnsatisfiableRangeRejection(unsatisfiableRanges, actualEntityLength) ⇒ - complete((RequestedRangeNotSatisfiable, List(`Content-Range`(ContentRange.Unsatisfiable(actualEntityLength))), - unsatisfiableRanges.mkString("None of the following requested Ranges were satisfiable:\n", "\n", ""))) - } - .handleAll[AuthenticationFailedRejection] { rejections ⇒ - val rejectionMessage = rejections.head.cause match { - case CredentialsMissing ⇒ "The resource requires authentication, which was not supplied with the request" - case CredentialsRejected ⇒ "The supplied authentication is invalid" - } - // Multiple challenges per WWW-Authenticate header are allowed per spec, - // however, it seems many browsers will ignore all challenges but the first. - // Therefore, multiple WWW-Authenticate headers are rendered, instead. - // - // See https://code.google.com/p/chromium/issues/detail?id=103220 - // and https://bugzilla.mozilla.org/show_bug.cgi?id=669675 - val authenticateHeaders = rejections.map(r ⇒ `WWW-Authenticate`(r.challenge)) - complete((Unauthorized, authenticateHeaders, rejectionMessage)) - } - .handleAll[UnacceptedResponseContentTypeRejection] { rejections ⇒ - val supported = rejections.flatMap(_.supported) - val msg = supported.map(_.format).mkString("Resource representation is only available with these types:\n", "\n", "") - complete((NotAcceptable, msg)) - } - .handleAll[UnacceptedResponseEncodingRejection] { rejections ⇒ - val supported = rejections.flatMap(_.supported) - complete((NotAcceptable, "Resource representation is only available with these Content-Encodings:\n" + - supported.map(_.value).mkString("\n"))) - } - .handleAll[UnsupportedRequestContentTypeRejection] { rejections ⇒ - val supported = rejections.flatMap(_.supported).mkString(" or ") - complete((UnsupportedMediaType, "The request's Content-Type is not supported. Expected:\n" + supported)) - } - .handleAll[UnsupportedRequestEncodingRejection] { rejections ⇒ - val supported = rejections.map(_.supported.value).mkString(" or ") - complete((BadRequest, "The request's Content-Encoding is not supported. Expected:\n" + supported)) - } - .handle { case ExpectedWebSocketRequestRejection ⇒ complete((BadRequest, "Expected WebSocket Upgrade request")) } - .handleAll[UnsupportedWebSocketSubprotocolRejection] { rejections ⇒ - val supported = rejections.map(_.supportedProtocol) - complete(HttpResponse( - BadRequest, - entity = s"None of the websocket subprotocols offered in the request are supported. Supported are ${supported.map("'" + _ + "'").mkString(",")}.", - headers = `Sec-WebSocket-Protocol`(supported) :: Nil)) - } - .handle { case ValidationRejection(msg, _) ⇒ complete((BadRequest, msg)) } - .handle { case x ⇒ sys.error("Unhandled rejection: " + x) } - .handleNotFound { complete((NotFound, "The requested resource could not be found.")) } - .result() - - /** - * Filters out all TransformationRejections from the given sequence and applies them (in order) to the - * remaining rejections. - */ - def applyTransformations(rejections: immutable.Seq[Rejection]): immutable.Seq[Rejection] = { - val (transformations, rest) = rejections.partition(_.isInstanceOf[TransformationRejection]) - (rest.distinct /: transformations.asInstanceOf[Seq[TransformationRejection]]) { - case (remaining, transformation) ⇒ transformation.transform(remaining) - } - } -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/RequestContext.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/RequestContext.scala deleted file mode 100644 index 587175c5c3..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/RequestContext.scala +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server - -import scala.concurrent.{ Future, ExecutionContextExecutor } -import akka.stream.Materializer -import akka.event.LoggingAdapter -import akka.http.scaladsl.marshalling.ToResponseMarshallable - -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.StatusCodes._ -import akka.http.scaladsl.settings.{ RoutingSettings, ParserSettings } - -/** - * Immutable object encapsulating the context of an [[akka.http.scaladsl.model.HttpRequest]] - * as it flows through a akka-http Route structure. - */ -trait RequestContext { - - /** The request this context represents. Modelled as a `val` so as to enable an `import ctx.request._`. */ - val request: HttpRequest - - /** The unmatched path of this context. Modelled as a `val` so as to enable an `import ctx.unmatchedPath._`. */ - val unmatchedPath: Uri.Path - - /** - * The default ExecutionContext to be used for scheduling asynchronous logic related to this request. - */ - implicit def executionContext: ExecutionContextExecutor - - /** - * The default Materializer. - */ - implicit def materializer: Materializer - - /** - * The default LoggingAdapter to be used for logging messages related to this request. - */ - def log: LoggingAdapter - - /** - * The default RoutingSettings to be used for configuring directives. - */ - def settings: RoutingSettings - - /** - * The default ParserSettings to be used for configuring directives. - */ - def parserSettings: ParserSettings - - /** - * Returns a copy of this context with the given fields updated. - */ - def reconfigure( - executionContext: ExecutionContextExecutor = executionContext, - materializer: Materializer = materializer, - log: LoggingAdapter = log, - settings: RoutingSettings = settings): RequestContext - - /** - * Completes the request with the given ToResponseMarshallable. - */ - def complete(obj: ToResponseMarshallable): Future[RouteResult] - - /** - * Rejects the request with the given rejections. - */ - def reject(rejections: Rejection*): Future[RouteResult] - - /** - * Completes the request with redirection response of the given type to the given URI. - * - */ - def redirect(uri: Uri, redirectionType: Redirection): Future[RouteResult] - - /** - * Bubbles the given error up the response chain where it is dealt with by the closest `handleExceptions` - * directive and its `ExceptionHandler`, unless the error is a `RejectionError`. In this case the - * wrapped rejection is unpacked and "executed". - */ - def fail(error: Throwable): Future[RouteResult] - - /** - * Returns a copy of this context with the new HttpRequest. - */ - def withRequest(req: HttpRequest): RequestContext - - /** - * Returns a copy of this context with the new HttpRequest. - */ - def withExecutionContext(ec: ExecutionContextExecutor): RequestContext - - /** - * Returns a copy of this context with the new HttpRequest. - */ - def withMaterializer(materializer: Materializer): RequestContext - - /** - * Returns a copy of this context with the new LoggingAdapter. - */ - def withLog(log: LoggingAdapter): RequestContext - - /** - * Returns a copy of this context with the new RoutingSettings. - */ - def withRoutingSettings(settings: RoutingSettings): RequestContext - - /** - * Returns a copy of this context with the new [[akka.http.scaladsl.settings.ParserSettings]]. - */ - def withParserSettings(settings: ParserSettings): RequestContext - - /** - * Returns a copy of this context with the HttpRequest transformed by the given function. - */ - def mapRequest(f: HttpRequest ⇒ HttpRequest): RequestContext - - /** - * Returns a copy of this context with the unmatched path updated to the given one. - */ - def withUnmatchedPath(path: Uri.Path): RequestContext - - /** - * Returns a copy of this context with the unmatchedPath transformed by the given function. - */ - def mapUnmatchedPath(f: Uri.Path ⇒ Uri.Path): RequestContext - - /** - * Removes a potentially existing Accept header from the request headers. - */ - def withAcceptAll: RequestContext -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/RequestContextImpl.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/RequestContextImpl.scala deleted file mode 100644 index 2c31625dbf..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/RequestContextImpl.scala +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server - -import scala.concurrent.{ ExecutionContextExecutor, Future } -import akka.stream.{ ActorMaterializer, ActorMaterializerHelper, Materializer } -import akka.event.LoggingAdapter -import akka.http.scaladsl.settings.{ ParserSettings, RoutingSettings } -import akka.http.scaladsl.marshalling.{ Marshal, ToResponseMarshallable } -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.StatusCodes._ -import akka.http.scaladsl.util.FastFuture -import akka.http.scaladsl.util.FastFuture._ - -/** - * INTERNAL API - */ -private[http] class RequestContextImpl( - val request: HttpRequest, - val unmatchedPath: Uri.Path, - val executionContext: ExecutionContextExecutor, - val materializer: Materializer, - val log: LoggingAdapter, - val settings: RoutingSettings, - val parserSettings: ParserSettings) extends RequestContext { - - def this(request: HttpRequest, log: LoggingAdapter, settings: RoutingSettings, parserSettings: ParserSettings)(implicit ec: ExecutionContextExecutor, materializer: Materializer) = - this(request, request.uri.path, ec, materializer, log, settings, parserSettings) - - def this(request: HttpRequest, log: LoggingAdapter, settings: RoutingSettings)(implicit ec: ExecutionContextExecutor, materializer: Materializer) = - this(request, request.uri.path, ec, materializer, log, settings, ParserSettings(ActorMaterializerHelper.downcast(materializer).system)) - - def reconfigure(executionContext: ExecutionContextExecutor, materializer: Materializer, log: LoggingAdapter, settings: RoutingSettings): RequestContext = - copy(executionContext = executionContext, materializer = materializer, log = log, routingSettings = settings) - - override def complete(trm: ToResponseMarshallable): Future[RouteResult] = - trm(request)(executionContext) - .fast.map(res ⇒ RouteResult.Complete(res))(executionContext) - .fast.recoverWith { - case Marshal.UnacceptableResponseContentTypeException(supported) ⇒ - attemptRecoveryFromUnacceptableResponseContentTypeException(trm, supported) - case RejectionError(rej) ⇒ - Future.successful(RouteResult.Rejected(rej :: Nil)) - }(executionContext) - - override def reject(rejections: Rejection*): Future[RouteResult] = - FastFuture.successful(RouteResult.Rejected(rejections.toList)) - - override def redirect(uri: Uri, redirectionType: Redirection): Future[RouteResult] = { - //# red-impl - complete(HttpResponse( - status = redirectionType, - headers = headers.Location(uri) :: Nil, - entity = redirectionType.htmlTemplate match { - case "" ⇒ HttpEntity.Empty - case template ⇒ HttpEntity(ContentTypes.`text/html(UTF-8)`, template format uri) - })) - //# - } - - override def fail(error: Throwable): Future[RouteResult] = - FastFuture.failed(error) - - override def withRequest(request: HttpRequest): RequestContext = - if (request != this.request) copy(request = request) else this - - override def withExecutionContext(executionContext: ExecutionContextExecutor): RequestContext = - if (executionContext != this.executionContext) copy(executionContext = executionContext) else this - - override def withMaterializer(materializer: Materializer): RequestContext = - if (materializer != this.materializer) copy(materializer = materializer) else this - - override def withLog(log: LoggingAdapter): RequestContext = - if (log != this.log) copy(log = log) else this - - override def withRoutingSettings(routingSettings: RoutingSettings): RequestContext = - if (routingSettings != this.settings) copy(routingSettings = routingSettings) else this - - override def withParserSettings(parserSettings: ParserSettings): RequestContext = - if (parserSettings != this.parserSettings) copy(parserSettings = parserSettings) else this - - override def mapRequest(f: HttpRequest ⇒ HttpRequest): RequestContext = - copy(request = f(request)) - - override def withUnmatchedPath(path: Uri.Path): RequestContext = - if (path != unmatchedPath) copy(unmatchedPath = path) else this - - override def mapUnmatchedPath(f: Uri.Path ⇒ Uri.Path): RequestContext = - copy(unmatchedPath = f(unmatchedPath)) - - override def withAcceptAll: RequestContext = request.header[headers.Accept] match { - case Some(accept @ headers.Accept(ranges)) if !accept.acceptsAll ⇒ - mapRequest(_.mapHeaders(_.map { - case `accept` ⇒ - val acceptAll = - if (ranges.exists(_.isWildcard)) ranges.map(r ⇒ if (r.isWildcard) MediaRanges.`*/*;q=MIN` else r) - else ranges :+ MediaRanges.`*/*;q=MIN` - accept.copy(mediaRanges = acceptAll) - case x ⇒ x - })) - case _ ⇒ this - } - - /** Attempts recovering from the special case when non-2xx response is sent, yet content negotiation was unable to find a match. */ - private def attemptRecoveryFromUnacceptableResponseContentTypeException(trm: ToResponseMarshallable, supported: Set[ContentNegotiator.Alternative]): Future[RouteResult] = - trm.value match { - case (status: StatusCode, value) if !status.isSuccess ⇒ this.withAcceptAll.complete(trm) // retry giving up content negotiation - case _ ⇒ Future.successful(RouteResult.Rejected(UnacceptedResponseContentTypeRejection(supported) :: Nil)) - } - - private def copy( - request: HttpRequest = request, - unmatchedPath: Uri.Path = unmatchedPath, - executionContext: ExecutionContextExecutor = executionContext, - materializer: Materializer = materializer, - log: LoggingAdapter = log, - routingSettings: RoutingSettings = settings, - parserSettings: ParserSettings = parserSettings) = - new RequestContextImpl(request, unmatchedPath, executionContext, materializer, log, routingSettings, parserSettings) -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/Route.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/Route.scala deleted file mode 100644 index e60a109578..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/Route.scala +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server - -import akka.NotUsed -import akka.http.scaladsl.settings.{ ParserSettings, RoutingSettings } -import akka.stream.{ ActorMaterializer, ActorMaterializerHelper, Materializer } - -import scala.concurrent.{ ExecutionContextExecutor, Future } -import akka.stream.scaladsl.Flow -import akka.http.scaladsl.model.{ HttpRequest, HttpResponse } -import akka.http.scaladsl.util.FastFuture._ - -object Route { - - /** - * Helper for constructing a Route from a function literal. - */ - def apply(f: Route): Route = f - - /** - * "Seals" a route by wrapping it with exception handling and rejection conversion. - */ - def seal(route: Route)(implicit - routingSettings: RoutingSettings, - parserSettings: ParserSettings = null, - rejectionHandler: RejectionHandler = RejectionHandler.default, - exceptionHandler: ExceptionHandler = null): Route = { - import directives.ExecutionDirectives._ - handleExceptions(ExceptionHandler.seal(exceptionHandler)) { - handleRejections(rejectionHandler.seal) { - route - } - } - } - - /** - * Turns a `Route` into a server flow. - * - * This conversion is also implicitly available through [[RouteResult#route2HandlerFlow]]. - */ - def handlerFlow(route: Route)(implicit - routingSettings: RoutingSettings, - parserSettings: ParserSettings, - materializer: Materializer, - routingLog: RoutingLog, - executionContext: ExecutionContextExecutor = null, - rejectionHandler: RejectionHandler = RejectionHandler.default, - exceptionHandler: ExceptionHandler = null): Flow[HttpRequest, HttpResponse, NotUsed] = - Flow[HttpRequest].mapAsync(1)(asyncHandler(route)) - - /** - * Turns a `Route` into an async handler function. - */ - def asyncHandler(route: Route)(implicit - routingSettings: RoutingSettings, - parserSettings: ParserSettings, - materializer: Materializer, - routingLog: RoutingLog, - executionContext: ExecutionContextExecutor = null, - rejectionHandler: RejectionHandler = RejectionHandler.default, - exceptionHandler: ExceptionHandler = null): HttpRequest ⇒ Future[HttpResponse] = { - val effectiveEC = if (executionContext ne null) executionContext else materializer.executionContext - - { - implicit val executionContext = effectiveEC // overrides parameter - val effectiveParserSettings = if (parserSettings ne null) parserSettings else ParserSettings(ActorMaterializerHelper.downcast(materializer).system) - - val sealedRoute = seal(route) - request ⇒ - sealedRoute(new RequestContextImpl(request, routingLog.requestLog(request), routingSettings, effectiveParserSettings)).fast - .map { - case RouteResult.Complete(response) ⇒ response - case RouteResult.Rejected(rejected) ⇒ throw new IllegalStateException(s"Unhandled rejections '$rejected', unsealed RejectionHandler?!") - } - } - } -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/RouteConcatenation.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/RouteConcatenation.scala deleted file mode 100644 index 9d39ffff2b..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/RouteConcatenation.scala +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server - -import akka.http.scaladsl.server.Directives.reject -import akka.http.scaladsl.util.FastFuture -import akka.http.scaladsl.util.FastFuture._ - -/** - * @groupname concat Route concatenation - * @groupprio concat 300 - */ -trait RouteConcatenation { - - /** - * @group concat - */ - implicit def _enhanceRouteWithConcatenation(route: Route): RouteConcatenation.RouteWithConcatenation = - new RouteConcatenation.RouteWithConcatenation(route: Route) - - /** - * Tries the supplied routes in sequence, returning the result of the first route that doesn't reject the request. - * This is an alternative to direct usage of the infix ~ operator. The ~ can be prone to programmer error, because if - * it is omitted, the program will still be syntactically correct, but will not actually attempt to match multiple - * routes, as intended. - * - * @param routes subroutes to concatenate - * @return the concatenated route - */ - def concat(routes: Route*): Route = routes.foldLeft[Route](reject)(_ ~ _) -} - -object RouteConcatenation extends RouteConcatenation { - - class RouteWithConcatenation(route: Route) { - /** - * Returns a Route that chains two Routes. If the first Route rejects the request the second route is given a - * chance to act upon the request. - */ - def ~(other: Route): Route = { ctx ⇒ - import ctx.executionContext - route(ctx).fast.flatMap { - case x: RouteResult.Complete ⇒ FastFuture.successful(x) - case RouteResult.Rejected(outerRejections) ⇒ - other(ctx).fast.map { - case x: RouteResult.Complete ⇒ x - case RouteResult.Rejected(innerRejections) ⇒ RouteResult.Rejected(outerRejections ++ innerRejections) - } - } - } - } -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/RouteResult.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/RouteResult.scala deleted file mode 100644 index 0e4b613093..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/RouteResult.scala +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server - -import scala.collection.immutable -import scala.concurrent.ExecutionContext -import akka.NotUsed -import akka.http.scaladsl.settings.{ RoutingSettings, ParserSettings } -import akka.stream.Materializer -import akka.stream.scaladsl.Flow -import akka.http.scaladsl.model.{ HttpRequest, HttpResponse } -import akka.http.javadsl -import scala.collection.JavaConverters._ - -/** - * The result of handling a request. - * - * As a user you typically don't create RouteResult instances directly. - * Instead, use the methods on the [[RequestContext]] to achieve the desired effect. - */ -sealed trait RouteResult extends javadsl.server.RouteResult - -object RouteResult { - final case class Complete(response: HttpResponse) extends javadsl.server.Complete with RouteResult { - override def getResponse = response - } - final case class Rejected(rejections: immutable.Seq[Rejection]) extends javadsl.server.Rejected with RouteResult { - override def getRejections = rejections.map(r ⇒ r: javadsl.server.Rejection).toIterable.asJava - } - - implicit def route2HandlerFlow(route: Route)(implicit - routingSettings: RoutingSettings, - parserSettings: ParserSettings, - materializer: Materializer, - routingLog: RoutingLog, - executionContext: ExecutionContext = null, - rejectionHandler: RejectionHandler = RejectionHandler.default, - exceptionHandler: ExceptionHandler = null): Flow[HttpRequest, HttpResponse, NotUsed] = - Route.handlerFlow(route) -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/RoutingLog.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/RoutingLog.scala deleted file mode 100644 index 81bfd123e9..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/RoutingLog.scala +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server - -import akka.event.LoggingAdapter -import akka.actor.{ ActorSystem, ActorContext } -import akka.http.scaladsl.model.HttpRequest - -trait RoutingLog { - def log: LoggingAdapter - def requestLog(request: HttpRequest): LoggingAdapter -} - -object RoutingLog extends LowerPriorityRoutingLogImplicits { - def apply(defaultLog: LoggingAdapter): RoutingLog = - new RoutingLog { - def log = defaultLog - def requestLog(request: HttpRequest) = defaultLog - } - - implicit def fromActorContext(implicit ac: ActorContext): RoutingLog = RoutingLog(ac.system.log) -} -sealed abstract class LowerPriorityRoutingLogImplicits { - implicit def fromActorSystem(implicit system: ActorSystem): RoutingLog = RoutingLog(system.log) -} \ No newline at end of file diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/StandardRoute.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/StandardRoute.scala deleted file mode 100644 index 05f6246f54..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/StandardRoute.scala +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server - -import akka.http.scaladsl.server.util.Tuple - -/** - * A Route that can be implicitly converted into a Directive (fitting any signature). - */ -abstract class StandardRoute extends Route { - def toDirective[L: Tuple]: Directive[L] = StandardRoute.toDirective(this) -} - -object StandardRoute { - def apply(route: Route): StandardRoute = route match { - case x: StandardRoute ⇒ x - case x ⇒ new StandardRoute { def apply(ctx: RequestContext) = x(ctx) } - } - - /** - * Converts the StandardRoute into a directive that never passes the request to its inner route - * (and always returns its underlying route). - */ - implicit def toDirective[L: Tuple](route: StandardRoute): Directive[L] = - Directive[L] { _ ⇒ route } -} \ No newline at end of file diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala deleted file mode 100644 index bb077365d3..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala +++ /dev/null @@ -1,378 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import akka.actor.ActorSystem -import akka.stream.scaladsl.Source -import akka.util.ByteString - -import scala.concurrent.duration.FiniteDuration -import scala.concurrent.{ Future, ExecutionContextExecutor } -import scala.collection.immutable -import akka.event.LoggingAdapter -import akka.stream.impl.ConstantFun.scalaIdentityFunction -import akka.stream.{ ActorMaterializerHelper, Materializer } -import akka.http.scaladsl.settings.{ RoutingSettings, ParserSettings } -import akka.http.scaladsl.server.util.Tuple -import akka.http.scaladsl.util.FastFuture -import akka.http.scaladsl.model._ -import akka.http.scaladsl.util.FastFuture._ - -import scala.util.{ Failure, Success } - -/** - * @groupname basic Basic directives - * @groupprio basic 10 - */ -trait BasicDirectives { - - /** - * @group basic - */ - def mapInnerRoute(f: Route ⇒ Route): Directive0 = - Directive { inner ⇒ f(inner(())) } - - /** - * @group basic - */ - def mapRequestContext(f: RequestContext ⇒ RequestContext): Directive0 = - mapInnerRoute { inner ⇒ ctx ⇒ inner(f(ctx)) } - - /** - * @group basic - */ - def mapRequest(f: HttpRequest ⇒ HttpRequest): Directive0 = - mapRequestContext(_ mapRequest f) - - /** - * @group basic - */ - def mapRouteResultFuture(f: Future[RouteResult] ⇒ Future[RouteResult]): Directive0 = - Directive { inner ⇒ ctx ⇒ f(inner(())(ctx)) } - - /** - * @group basic - */ - def mapRouteResult(f: RouteResult ⇒ RouteResult): Directive0 = - Directive { inner ⇒ ctx ⇒ inner(())(ctx).fast.map(f)(ctx.executionContext) } - - /** - * @group basic - */ - def mapRouteResultWith(f: RouteResult ⇒ Future[RouteResult]): Directive0 = - Directive { inner ⇒ ctx ⇒ inner(())(ctx).fast.flatMap(f)(ctx.executionContext) } - - /** - * @group basic - */ - def mapRouteResultPF(f: PartialFunction[RouteResult, RouteResult]): Directive0 = - mapRouteResult(f.applyOrElse(_, conforms[RouteResult])) - - /** - * @group basic - */ - def mapRouteResultWithPF(f: PartialFunction[RouteResult, Future[RouteResult]]): Directive0 = - mapRouteResultWith(f.applyOrElse(_, FastFuture.successful[RouteResult])) - - /** - * @group basic - */ - def recoverRejections(f: immutable.Seq[Rejection] ⇒ RouteResult): Directive0 = - mapRouteResultPF { case RouteResult.Rejected(rejections) ⇒ f(rejections) } - - /** - * @group basic - */ - def recoverRejectionsWith(f: immutable.Seq[Rejection] ⇒ Future[RouteResult]): Directive0 = - mapRouteResultWithPF { case RouteResult.Rejected(rejections) ⇒ f(rejections) } - - /** - * @group basic - */ - def mapRejections(f: immutable.Seq[Rejection] ⇒ immutable.Seq[Rejection]): Directive0 = - recoverRejections(rejections ⇒ RouteResult.Rejected(f(rejections))) - - /** - * @group basic - */ - def mapResponse(f: HttpResponse ⇒ HttpResponse): Directive0 = - mapRouteResultPF { case RouteResult.Complete(response) ⇒ RouteResult.Complete(f(response)) } - - /** - * @group basic - */ - def mapResponseEntity(f: ResponseEntity ⇒ ResponseEntity): Directive0 = - mapResponse(_ mapEntity f) - - /** - * @group basic - */ - def mapResponseHeaders(f: immutable.Seq[HttpHeader] ⇒ immutable.Seq[HttpHeader]): Directive0 = - mapResponse(_ mapHeaders f) - - /** - * A Directive0 that always passes the request on to its inner route - * (i.e. does nothing with the request or the response). - * - * @group basic - */ - def pass: Directive0 = Directive.Empty - - /** - * Injects the given value into a directive. - * - * @group basic - */ - def provide[T](value: T): Directive1[T] = tprovide(Tuple1(value)) - - /** - * Injects the given values into a directive. - * - * @group basic - */ - def tprovide[L: Tuple](values: L): Directive[L] = - Directive { _(values) } - - /** - * Extracts a single value using the given function. - * - * @group basic - */ - def extract[T](f: RequestContext ⇒ T): Directive1[T] = - textract(ctx ⇒ Tuple1(f(ctx))) - - /** - * Extracts a number of values using the given function. - * - * @group basic - */ - def textract[L: Tuple](f: RequestContext ⇒ L): Directive[L] = - Directive { inner ⇒ ctx ⇒ inner(f(ctx))(ctx) } - - /** - * Adds a TransformationRejection cancelling all rejections equal to the given one - * to the list of rejections potentially coming back from the inner route. - * - * @group basic - */ - def cancelRejection(rejection: Rejection): Directive0 = - cancelRejections(_ == rejection) - - /** - * Adds a TransformationRejection cancelling all rejections of one of the given classes - * to the list of rejections potentially coming back from the inner route. - * - * @group basic - */ - def cancelRejections(classes: Class[_]*): Directive0 = - cancelRejections(r ⇒ classes.exists(_ isInstance r)) - - /** - * Adds a TransformationRejection cancelling all rejections for which the given filter function returns true - * to the list of rejections potentially coming back from the inner route. - * - * @group basic - */ - def cancelRejections(cancelFilter: Rejection ⇒ Boolean): Directive0 = - mapRejections(_ :+ TransformationRejection(_ filterNot cancelFilter)) - - /** - * Transforms the unmatchedPath of the RequestContext using the given function. - * - * @group basic - */ - def mapUnmatchedPath(f: Uri.Path ⇒ Uri.Path): Directive0 = - mapRequestContext(_ mapUnmatchedPath f) - - /** - * Extracts the yet unmatched path from the RequestContext. - * - * @group basic - */ - def extractUnmatchedPath: Directive1[Uri.Path] = BasicDirectives._extractUnmatchedPath - - /** - * Extracts the current [[HttpRequest]] instance. - * - * @group basic - */ - def extractRequest: Directive1[HttpRequest] = BasicDirectives._extractRequest - - /** - * Extracts the complete request URI. - * - * @group basic - */ - def extractUri: Directive1[Uri] = BasicDirectives._extractUri - - /** - * Runs its inner route with the given alternative [[scala.concurrent.ExecutionContextExecutor]]. - * - * @group basic - */ - def withExecutionContext(ec: ExecutionContextExecutor): Directive0 = - mapRequestContext(_ withExecutionContext ec) - - /** - * Extracts the [[scala.concurrent.ExecutionContextExecutor]] from the [[akka.http.scaladsl.server.RequestContext]]. - * - * @group basic - */ - def extractExecutionContext: Directive1[ExecutionContextExecutor] = BasicDirectives._extractExecutionContext - - /** - * Runs its inner route with the given alternative [[akka.stream.Materializer]]. - * - * @group basic - */ - def withMaterializer(materializer: Materializer): Directive0 = - mapRequestContext(_ withMaterializer materializer) - - /** - * Extracts the [[akka.stream.Materializer]] from the [[akka.http.scaladsl.server.RequestContext]]. - * - * @group basic - */ - def extractMaterializer: Directive1[Materializer] = BasicDirectives._extractMaterializer - - /** - * Extracts the [[akka.actor.ActorSystem]] if the available Materializer is an [[akka.stream.ActorMaterializer]]. - * Otherwise throws an exception as it won't be able to extract the system from arbitrary materializers. - * - * @group basic - */ - def extractActorSystem: Directive1[ActorSystem] = extract { ctx ⇒ - ActorMaterializerHelper.downcast(ctx.materializer).system - } - - /** - * Runs its inner route with the given alternative [[akka.event.LoggingAdapter]]. - * - * @group basic - */ - def withLog(log: LoggingAdapter): Directive0 = - mapRequestContext(_ withLog log) - - /** - * Extracts the [[akka.event.LoggingAdapter]] from the [[akka.http.scaladsl.server.RequestContext]]. - * - * @group basic - */ - def extractLog: Directive1[LoggingAdapter] = - BasicDirectives._extractLog - - /** - * Runs its inner route with the given alternative [[RoutingSettings]]. - * - * @group basic - */ - def withSettings(settings: RoutingSettings): Directive0 = - mapRequestContext(_ withRoutingSettings settings) - - /** - * Runs the inner route with settings mapped by the given function. - * - * @group basic - */ - def mapSettings(f: RoutingSettings ⇒ RoutingSettings): Directive0 = - mapRequestContext(ctx ⇒ ctx.withRoutingSettings(f(ctx.settings))) - - /** - * Extracts the [[RoutingSettings]] from the [[akka.http.scaladsl.server.RequestContext]]. - * - * @group basic - */ - def extractSettings: Directive1[RoutingSettings] = - BasicDirectives._extractSettings - - /** - * Extracts the [[akka.http.scaladsl.settings.ParserSettings]] from the [[akka.http.scaladsl.server.RequestContext]]. - * - * @group basic - */ - def extractParserSettings: Directive1[ParserSettings] = - BasicDirectives._extractParserSettings - - /** - * Extracts the [[akka.http.scaladsl.server.RequestContext]] itself. - * - * @group basic - */ - def extractRequestContext: Directive1[RequestContext] = BasicDirectives._extractRequestContext - - /** - * Extracts the [[akka.http.scaladsl.model.RequestEntity]] from the [[akka.http.scaladsl.server.RequestContext]]. - * - * @group basic - */ - def extractRequestEntity: Directive1[RequestEntity] = BasicDirectives._extractRequestEntity - - /** - * Extracts the entities `dataBytes` [[akka.stream.scaladsl.Source]] from the [[akka.http.scaladsl.server.RequestContext]]. - * - * @group basic - */ - def extractDataBytes: Directive1[Source[ByteString, Any]] = BasicDirectives._extractDataBytes - - /** - * WARNING: This will read the entire request entity into memory regardless of size and effectively disable streaming. - * - * Converts the HttpEntity from the [[akka.http.scaladsl.server.RequestContext]] into an - * [[akka.http.scaladsl.model.HttpEntity.Strict]] and extracts it, or fails the route if unable to drain the - * entire request body within the timeout. - * - * @param timeout The directive is failed if the stream isn't completed after the given timeout. - * @group basic - */ - def extractStrictEntity(timeout: FiniteDuration): Directive1[HttpEntity.Strict] = - extract { ctx ⇒ - import ctx.materializer - - ctx.request.entity.toStrict(timeout) - - }.flatMap { entity ⇒ - import FutureDirectives._ - - onComplete(entity).flatMap { - case Success(x) ⇒ provide(x) - case Failure(t) ⇒ StandardRoute(_.fail(t)) - } - } - - /** - * WARNING: This will read the entire request entity into memory regardless of size and effectively disable streaming. - * - * Extracts the [[akka.http.scaladsl.server.RequestContext]] itself with the strict HTTP entity, - * or fails the route if unable to drain the entire request body within the timeout. - * - * @param timeout The directive is failed if the stream isn't completed after the given timeout. - * @group basic - */ - def toStrictEntity(timeout: FiniteDuration): Directive0 = - Directive { inner ⇒ ctx ⇒ - import ctx.{ executionContext, materializer } - - ctx.request.entity.toStrict(timeout).flatMap { strictEntity ⇒ - val newCtx = ctx.mapRequest(_.copy(entity = strictEntity)) - inner(())(newCtx) - } - } - -} - -object BasicDirectives extends BasicDirectives { - private val _extractUnmatchedPath: Directive1[Uri.Path] = extract(_.unmatchedPath) - private val _extractRequest: Directive1[HttpRequest] = extract(_.request) - private val _extractUri: Directive1[Uri] = extract(_.request.uri) - private val _extractExecutionContext: Directive1[ExecutionContextExecutor] = extract(_.executionContext) - private val _extractMaterializer: Directive1[Materializer] = extract(_.materializer) - private val _extractLog: Directive1[LoggingAdapter] = extract(_.log) - private val _extractSettings: Directive1[RoutingSettings] = extract(_.settings) - private val _extractParserSettings: Directive1[ParserSettings] = extract(_.parserSettings) - private val _extractRequestContext: Directive1[RequestContext] = extract(scalaIdentityFunction) - private val _extractRequestEntity: Directive1[RequestEntity] = extract(_.request.entity) - private val _extractDataBytes: Directive1[Source[ByteString, Any]] = extract(_.request.entity.dataBytes) -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/CacheConditionDirectives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/CacheConditionDirectives.scala deleted file mode 100644 index 7320618ed2..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/CacheConditionDirectives.scala +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.DateTime -import headers._ -import HttpMethods._ -import StatusCodes._ -import EntityTag._ - -/** - * @groupname cachecondition Cache condition directives - * @groupprio cachecondition 20 - */ -trait CacheConditionDirectives { - import BasicDirectives._ - import RouteDirectives._ - - /** - * Wraps its inner route with support for Conditional Requests as defined - * by http://tools.ietf.org/html/rfc7232 - * - * In particular the algorithm defined by http://tools.ietf.org/html/rfc7232#section-6 - * is implemented by this directive. - * - * Note: if you want to combine this directive with `withRangeSupport(...)` you need to put - * it on the *outside* of the `withRangeSupport(...)` directive, i.e. `withRangeSupport(...)` - * must be on a deeper level in your route structure in order to function correctly. - * - * @group cachecondition - */ - def conditional(eTag: EntityTag): Directive0 = conditional(Some(eTag), None) - - /** - * Wraps its inner route with support for Conditional Requests as defined - * by http://tools.ietf.org/html/rfc7232 - * - * In particular the algorithm defined by http://tools.ietf.org/html/rfc7232#section-6 - * is implemented by this directive. - * - * Note: if you want to combine this directive with `withRangeSupport(...)` you need to put - * it on the *outside* of the `withRangeSupport(...)` directive, i.e. `withRangeSupport(...)` - * must be on a deeper level in your route structure in order to function correctly. - * - * @group cachecondition - */ - def conditional(lastModified: DateTime): Directive0 = conditional(None, Some(lastModified)) - - /** - * Wraps its inner route with support for Conditional Requests as defined - * by http://tools.ietf.org/html/rfc7232 - * - * In particular the algorithm defined by http://tools.ietf.org/html/rfc7232#section-6 - * is implemented by this directive. - * - * Note: if you want to combine this directive with `withRangeSupport(...)` you need to put - * it on the *outside* of the `withRangeSupport(...)` directive, i.e. `withRangeSupport(...)` - * must be on a deeper level in your route structure in order to function correctly. - * - * @group cachecondition - */ - def conditional(eTag: EntityTag, lastModified: DateTime): Directive0 = conditional(Some(eTag), Some(lastModified)) - - /** - * Wraps its inner route with support for Conditional Requests as defined - * by http://tools.ietf.org/html/rfc7232 - * - * In particular the algorithm defined by http://tools.ietf.org/html/rfc7232#section-6 - * is implemented by this directive. - * - * Note: if you want to combine this directive with `withRangeSupport(...)` you need to put - * it on the *outside* of the `withRangeSupport(...)` directive, i.e. `withRangeSupport(...)` - * must be on a deeper level in your route structure in order to function correctly. - * - * @group cachecondition - */ - def conditional(eTag: Option[EntityTag], lastModified: Option[DateTime]): Directive0 = { - def addResponseHeaders: Directive0 = - mapResponse(_.withDefaultHeaders(eTag.map(ETag(_)).toList ++ lastModified.map(`Last-Modified`(_)).toList)) - - // TODO: also handle Cache-Control and Vary - def complete304(): Route = addResponseHeaders(complete(HttpResponse(NotModified))) - def complete412(): Route = _.complete(PreconditionFailed) - - extractRequest.flatMap { request ⇒ - import request._ - mapInnerRoute { route ⇒ - def innerRouteWithRangeHeaderFilteredOut: Route = - (mapRequest(_.mapHeaders(_.filterNot(_.isInstanceOf[Range]))) & - addResponseHeaders)(route) - - def isGetOrHead = method == HEAD || method == GET - def unmodified(ifModifiedSince: DateTime) = - lastModified.get <= ifModifiedSince && ifModifiedSince.clicks < System.currentTimeMillis() - - def step1(): Route = - header[`If-Match`] match { - case Some(`If-Match`(im)) if eTag.isDefined ⇒ - if (matchesRange(eTag.get, im, weakComparison = false)) step3() else complete412() - case None ⇒ step2() - } - def step2(): Route = - header[`If-Unmodified-Since`] match { - case Some(`If-Unmodified-Since`(ius)) if lastModified.isDefined && !unmodified(ius) ⇒ complete412() - case _ ⇒ step3() - } - def step3(): Route = - header[`If-None-Match`] match { - case Some(`If-None-Match`(inm)) if eTag.isDefined ⇒ - if (!matchesRange(eTag.get, inm, weakComparison = true)) step5() - else if (isGetOrHead) complete304() else complete412() - case None ⇒ step4() - } - def step4(): Route = - if (isGetOrHead) { - header[`If-Modified-Since`] match { - case Some(`If-Modified-Since`(ims)) if lastModified.isDefined && unmodified(ims) ⇒ complete304() - case _ ⇒ step5() - } - } else step5() - def step5(): Route = - if (method == GET && header[Range].isDefined) - header[`If-Range`] match { - case Some(`If-Range`(Left(tag))) if eTag.isDefined && !matches(eTag.get, tag, weakComparison = false) ⇒ - innerRouteWithRangeHeaderFilteredOut - case Some(`If-Range`(Right(ims))) if lastModified.isDefined && !unmodified(ims) ⇒ - innerRouteWithRangeHeaderFilteredOut - case _ ⇒ step6() - } - else step6() - def step6(): Route = addResponseHeaders(route) - - step1() - } - } - } -} - -object CacheConditionDirectives extends CacheConditionDirectives diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/CodingDirectives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/CodingDirectives.scala deleted file mode 100644 index ed9eb58657..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/CodingDirectives.scala +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import scala.collection.immutable -import scala.util.control.NonFatal -import akka.http.scaladsl.model.headers.{ HttpEncoding, HttpEncodings } -import akka.http.scaladsl.model._ -import akka.http.scaladsl.coding._ -import akka.http.impl.util._ -import akka.stream.impl.fusing.GraphStages -import akka.stream.scaladsl.Flow -import akka.util.ByteString - -/** - * @groupname coding Coding directives - * @groupprio coding 50 - */ -trait CodingDirectives { - import BasicDirectives._ - import MiscDirectives._ - import RouteDirectives._ - import CodingDirectives._ - - // encoding - - /** - * Rejects the request with an UnacceptedResponseEncodingRejection - * if the given response encoding is not accepted by the client. - * - * @group coding - */ - def responseEncodingAccepted(encoding: HttpEncoding): Directive0 = - extractRequest.flatMap { request ⇒ - if (EncodingNegotiator(request.headers).isAccepted(encoding)) pass - else reject(UnacceptedResponseEncodingRejection(Set(encoding))) - } - - /** - * Encodes the response with the encoding that is requested by the client via the `Accept- - * Encoding` header. The response encoding is determined by the rules specified in - * http://tools.ietf.org/html/rfc7231#section-5.3.4. - * - * If the `Accept-Encoding` header is missing or empty or specifies an encoding other than - * identity, gzip or deflate then no encoding is used. - * - * @group coding - */ - def encodeResponse: Directive0 = - _encodeResponse(DefaultEncodeResponseEncoders) - - /** - * Encodes the response with the encoding that is requested by the client via the `Accept- - * Encoding` header. The response encoding is determined by the rules specified in - * http://tools.ietf.org/html/rfc7231#section-5.3.4. - * - * If the `Accept-Encoding` header is missing then the response is encoded using the `first` - * encoder. - * - * If the `Accept-Encoding` header is empty and `NoCoding` is part of the encoders then no - * response encoding is used. Otherwise the request is rejected. - * - * @group coding - */ - def encodeResponseWith(first: Encoder, more: Encoder*): Directive0 = - _encodeResponse(immutable.Seq(first +: more: _*)) - - // decoding - - /** - * Decodes the incoming request using the given Decoder. - * If the request encoding doesn't match the request is rejected with an `UnsupportedRequestEncodingRejection`. - * - * @group coding - */ - def decodeRequestWith(decoder: Decoder): Directive0 = { - def applyDecoder = - if (decoder == NoCoding) pass - else - extractSettings flatMap { settings ⇒ - val effectiveDecoder = decoder.withMaxBytesPerChunk(settings.decodeMaxBytesPerChunk) - mapRequest { request ⇒ - effectiveDecoder.decode(request).mapEntity { entity ⇒ - entity.transformDataBytes(Flow[ByteString].recover { - case NonFatal(e) ⇒ - throw IllegalRequestException( - StatusCodes.BadRequest, - ErrorInfo("The request's encoding is corrupt", e.getMessage)) - }) - } - } - } - - requestEntityEmpty | ( - requestEncodedWith(decoder.encoding) & - applyDecoder & - cancelRejections(classOf[UnsupportedRequestEncodingRejection])) - } - - /** - * Rejects the request with an UnsupportedRequestEncodingRejection if its encoding doesn't match the given one. - * - * @group coding - */ - def requestEncodedWith(encoding: HttpEncoding): Directive0 = - extract(_.request.encoding).flatMap { - case `encoding` ⇒ pass - case _ ⇒ reject(UnsupportedRequestEncodingRejection(encoding)) - } - - /** - * Decodes the incoming request if it is encoded with one of the given - * encoders. If the request encoding doesn't match one of the given encoders - * the request is rejected with an `UnsupportedRequestEncodingRejection`. - * If no decoders are given the default encoders (`Gzip`, `Deflate`, `NoCoding`) are used. - * - * @group coding - */ - def decodeRequestWith(decoders: Decoder*): Directive0 = - theseOrDefault(decoders).map(decodeRequestWith).reduce(_ | _) - - /** - * Decompresses the incoming request if it is `gzip` or `deflate` compressed. - * Uncompressed requests are passed through untouched. - * If the request encoded with another encoding the request is rejected with an `UnsupportedRequestEncodingRejection`. - * - * @group coding - */ - def decodeRequest: Directive0 = - decodeRequestWith(DefaultCoders: _*) - - /** - * Inspects the response entity and adds a `Content-Encoding: gzip` response header if - * the entities media-type is precompressed with gzip and no `Content-Encoding` header is present yet. - * - * @group coding - */ - def withPrecompressedMediaTypeSupport: Directive0 = - mapResponse { response ⇒ - if (response.entity.contentType.mediaType.comp != MediaType.Gzipped) response - else response.withDefaultHeaders(headers.`Content-Encoding`(HttpEncodings.gzip)) - } -} - -object CodingDirectives extends CodingDirectives { - val DefaultCoders: immutable.Seq[Coder] = immutable.Seq(Gzip, Deflate, NoCoding) - - private[http] val DefaultEncodeResponseEncoders = immutable.Seq(NoCoding, Gzip, Deflate) - - def theseOrDefault[T >: Coder](these: Seq[T]): Seq[T] = if (these.isEmpty) DefaultCoders else these - - import BasicDirectives._ - import RouteDirectives._ - - private def _encodeResponse(encoders: immutable.Seq[Encoder]): Directive0 = - BasicDirectives.extractRequest.flatMap { request ⇒ - val negotiator = EncodingNegotiator(request.headers) - val encodings: List[HttpEncoding] = encoders.map(_.encoding)(collection.breakOut) - val bestEncoder = negotiator.pickEncoding(encodings).flatMap(be ⇒ encoders.find(_.encoding == be)) - bestEncoder match { - case Some(encoder) ⇒ mapResponse(encoder.encode(_)) - case _ ⇒ - if (encoders.contains(NoCoding) && !negotiator.hasMatchingFor(HttpEncodings.identity)) pass - else reject(UnacceptedResponseEncodingRejection(encodings.toSet)) - } - } -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/CookieDirectives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/CookieDirectives.scala deleted file mode 100644 index d69e4f13ef..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/CookieDirectives.scala +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers._ -import akka.http.impl.util._ - -/** - * @groupname cookie Cookie directives - * @groupprio cookie 30 - */ -trait CookieDirectives { - import HeaderDirectives._ - import RespondWithDirectives._ - import RouteDirectives._ - - /** - * Extracts the [[HttpCookiePair]] with the given name. If the cookie is not present the - * request is rejected with a respective [[MissingCookieRejection]]. - * - * @group cookie - */ - def cookie(name: String): Directive1[HttpCookiePair] = - headerValue(findCookie(name)) | reject(MissingCookieRejection(name)) - - /** - * Extracts the [[HttpCookiePair]] with the given name as an `Option[HttpCookiePair]`. - * If the cookie is not present a value of `None` is extracted. - * - * @group cookie - */ - def optionalCookie(name: String): Directive1[Option[HttpCookiePair]] = - optionalHeaderValue(findCookie(name)) - - private def findCookie(name: String): HttpHeader ⇒ Option[HttpCookiePair] = { - case Cookie(cookies) ⇒ cookies.find(_.name == name) - case _ ⇒ None - } - - /** - * Adds a [[Set-Cookie]] response header with the given cookies. - * - * @group cookie - */ - def setCookie(first: HttpCookie, more: HttpCookie*): Directive0 = - respondWithHeaders((first :: more.toList).map(`Set-Cookie`(_))) - - /** - * Adds a [[Set-Cookie]] response header expiring the given cookies. - * - * @group cookie - */ - def deleteCookie(first: HttpCookie, more: HttpCookie*): Directive0 = - respondWithHeaders((first :: more.toList).map { c ⇒ - `Set-Cookie`(c.copy(value = "deleted", expires = Some(DateTime.MinValue))) - }) - - /** - * Adds a [[Set-Cookie]] response header expiring the cookie with the given properties. - * - * @group cookie - */ - def deleteCookie(name: String, domain: String = "", path: String = ""): Directive0 = - deleteCookie(HttpCookie(name, "", domain = domain.toOption, path = path.toOption)) - -} - -object CookieDirectives extends CookieDirectives diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/DebuggingDirectives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/DebuggingDirectives.scala deleted file mode 100644 index c274a598e1..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/DebuggingDirectives.scala +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import akka.event.Logging._ -import akka.event.LoggingAdapter -import akka.http.scaladsl.model._ -import akka.http.javadsl - -/** - * @groupname debugging Debugging directives - * @groupprio debugging 40 - */ -trait DebuggingDirectives { - import BasicDirectives._ - - /** - * Produces a log entry for every incoming request. - * - * @group debugging - */ - def logRequest(magnet: LoggingMagnet[HttpRequest ⇒ Unit]): Directive0 = - extractRequestContext.flatMap { ctx ⇒ - magnet.f(ctx.log)(ctx.request) - pass - } - - /** - * Produces a log entry for every [[RouteResult]]. - * - * @group debugging - */ - def logResult(magnet: LoggingMagnet[RouteResult ⇒ Unit]): Directive0 = - extractRequestContext.flatMap { ctx ⇒ - mapRouteResult { result ⇒ - magnet.f(ctx.log)(result) - result - } - } - - /** - * Produces a log entry for every incoming request and [[RouteResult]]. - * - * @group debugging - */ - def logRequestResult(magnet: LoggingMagnet[HttpRequest ⇒ RouteResult ⇒ Unit]): Directive0 = - extractRequestContext.flatMap { ctx ⇒ - val logResult = magnet.f(ctx.log)(ctx.request) - mapRouteResult { result ⇒ - logResult(result) - result - } - } -} - -object DebuggingDirectives extends DebuggingDirectives - -case class LoggingMagnet[T](f: LoggingAdapter ⇒ T) // # logging-magnet - -object LoggingMagnet { - implicit def forMessageFromMarker[T](marker: String): LoggingMagnet[T ⇒ Unit] = // # message-magnets - forMessageFromMarkerAndLevel[T](marker → DebugLevel) - - implicit def forMessageFromMarkerAndLevel[T](markerAndLevel: (String, LogLevel)): LoggingMagnet[T ⇒ Unit] = // # message-magnets - forMessageFromFullShow[T] { - val (marker, level) = markerAndLevel - Message ⇒ LogEntry(Message, marker, level) - } - - implicit def forMessageFromShow[T](show: T ⇒ String): LoggingMagnet[T ⇒ Unit] = // # message-magnets - forMessageFromFullShow[T](msg ⇒ LogEntry(show(msg), DebugLevel)) - - implicit def forMessageFromFullShow[T](show: T ⇒ LogEntry): LoggingMagnet[T ⇒ Unit] = // # message-magnets - LoggingMagnet(log ⇒ show(_).logTo(log)) - - implicit def forRequestResponseFromMarker(marker: String): LoggingMagnet[HttpRequest ⇒ RouteResult ⇒ Unit] = // # request-response-magnets - forRequestResponseFromMarkerAndLevel(marker → DebugLevel) - - implicit def forRequestResponseFromMarkerAndLevel(markerAndLevel: (String, LogLevel)): LoggingMagnet[HttpRequest ⇒ RouteResult ⇒ Unit] = // # request-response-magnets - forRequestResponseFromFullShow { - val (marker, level) = markerAndLevel - request ⇒ response ⇒ Some( - LogEntry("Response for\n Request : " + request + "\n Response: " + response, marker, level)) - } - - implicit def forRequestResponseFromFullShow(show: HttpRequest ⇒ RouteResult ⇒ Option[LogEntry]): LoggingMagnet[HttpRequest ⇒ RouteResult ⇒ Unit] = // # request-response-magnets - LoggingMagnet { log ⇒ request ⇒ - val showResult = show(request) - result ⇒ showResult(result).foreach(_.logTo(log)) - } -} - -case class LogEntry(obj: Any, level: LogLevel = DebugLevel) extends javadsl.server.directives.LogEntry { - def logTo(log: LoggingAdapter): Unit = { - log.log(level, obj.toString) - } - override def getObj: Any = obj - override def getLevel: LogLevel = level -} - -object LogEntry { - def apply(obj: Any, marker: String, level: LogLevel): LogEntry = - LogEntry(if (marker.isEmpty) obj else marker + ": " + obj, level) -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/ExecutionDirectives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/ExecutionDirectives.scala deleted file mode 100644 index 639d2ca80a..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/ExecutionDirectives.scala +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import scala.collection.immutable -import scala.concurrent.Future -import scala.util.control.NonFatal -import akka.http.scaladsl.util.FastFuture -import akka.http.scaladsl.util.FastFuture._ - -/** - * @groupname execution Execution directives - * @groupprio execution 60 - */ -trait ExecutionDirectives { - import BasicDirectives._ - - /** - * Transforms exceptions thrown during evaluation of its inner route using the given - * [[akka.http.scaladsl.server.ExceptionHandler]]. - * - * @group execution - */ - def handleExceptions(handler: ExceptionHandler): Directive0 = - Directive { innerRouteBuilder ⇒ ctx ⇒ - import ctx.executionContext - def handleException: PartialFunction[Throwable, Future[RouteResult]] = - handler andThen (_(ctx.withAcceptAll)) - try innerRouteBuilder(())(ctx).fast.recoverWith(handleException) - catch { - case NonFatal(e) ⇒ handleException.applyOrElse[Throwable, Future[RouteResult]](e, throw _) - } - } - - /** - * Transforms rejections produced by its inner route using the given - * [[akka.http.scaladsl.server.RejectionHandler]]. - * - * @group execution - */ - def handleRejections(handler: RejectionHandler): Directive0 = - extractRequestContext flatMap { ctx ⇒ - val maxIterations = 8 - // allow for up to `maxIterations` nested rejections from RejectionHandler before bailing out - def handle(rejections: immutable.Seq[Rejection], originalRejections: immutable.Seq[Rejection], iterationsLeft: Int = maxIterations): Future[RouteResult] = - if (iterationsLeft > 0) { - handler(rejections) match { - case Some(route) ⇒ recoverRejectionsWith(handle(_, originalRejections, iterationsLeft - 1))(route)(ctx.withAcceptAll) - case None ⇒ FastFuture.successful(RouteResult.Rejected(rejections)) - } - } else - sys.error(s"Rejection handler still produced new rejections after $maxIterations iterations. " + - s"Is there an infinite handler cycle? Initial rejections: $originalRejections final rejections: $rejections") - - recoverRejectionsWith { rejections ⇒ - val transformed = RejectionHandler.applyTransformations(rejections) - handle(transformed, transformed) - } - } -} - -object ExecutionDirectives extends ExecutionDirectives diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/FileAndResourceDirectives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/FileAndResourceDirectives.scala deleted file mode 100644 index 538072502e..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/FileAndResourceDirectives.scala +++ /dev/null @@ -1,428 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import java.io.File -import java.net.{ URI, URL } - -import akka.http.javadsl.{ marshalling, model } -import akka.stream.ActorAttributes -import akka.stream.scaladsl.{ FileIO, StreamConverters } - -import scala.annotation.tailrec -import akka.actor.ActorSystem -import akka.event.LoggingAdapter -import akka.http.scaladsl.marshalling.{ Marshaller, ToEntityMarshaller } -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers._ -import akka.http.impl.util._ -import akka.http.javadsl - -import scala.collection.JavaConverters._ -import JavaMapping.Implicits._ -import akka.http.javadsl.server.RoutingJavaMapping - -/** - * @groupname fileandresource File and resource directives - * @groupprio fileandresource 70 - */ -trait FileAndResourceDirectives { - import CacheConditionDirectives._ - import MethodDirectives._ - import FileAndResourceDirectives._ - import RouteDirectives._ - import BasicDirectives._ - import RouteConcatenation._ - - /** - * Completes GET requests with the content of the given file. - * If the file cannot be found or read the request is rejected. - * - * @group fileandresource - */ - def getFromFile(fileName: String)(implicit resolver: ContentTypeResolver): Route = - getFromFile(new File(fileName)) - - /** - * Completes GET requests with the content of the given file. - * If the file cannot be found or read the request is rejected. - * - * @group fileandresource - */ - def getFromFile(file: File)(implicit resolver: ContentTypeResolver): Route = - getFromFile(file, resolver(file.getName)) - - /** - * Completes GET requests with the content of the given file. - * If the file cannot be found or read the request is rejected. - * - * @group fileandresource - */ - def getFromFile(file: File, contentType: ContentType): Route = - get { - if (file.isFile && file.canRead) - conditionalFor(file.length, file.lastModified) { - if (file.length > 0) { - withRangeSupportAndPrecompressedMediaTypeSupportAndExtractSettings { settings ⇒ - complete { - HttpEntity.Default(contentType, file.length, - FileIO.fromPath(file.toPath).withAttributes(ActorAttributes.dispatcher(settings.fileIODispatcher))) - } - } - } else complete(HttpEntity.Empty) - } - else reject - } - - private def conditionalFor(length: Long, lastModified: Long): Directive0 = - extractSettings.flatMap(settings ⇒ - if (settings.fileGetConditional) { - val tag = java.lang.Long.toHexString(lastModified ^ java.lang.Long.reverse(length)) - val lastModifiedDateTime = DateTime(math.min(lastModified, System.currentTimeMillis)) - conditional(EntityTag(tag), lastModifiedDateTime) - } else pass) - - /** - * Completes GET requests with the content of the given class-path resource. - * If the resource cannot be found or read the Route rejects the request. - * - * @group fileandresource - */ - def getFromResource(resourceName: String)(implicit resolver: ContentTypeResolver): Route = - getFromResource(resourceName, resolver(resourceName)) - - /** - * Completes GET requests with the content of the given resource. - * If the resource is a directory or cannot be found or read the Route rejects the request. - * - * @group fileandresource - */ - def getFromResource(resourceName: String, contentType: ContentType, classLoader: ClassLoader = _defaultClassLoader): Route = - if (!resourceName.endsWith("/")) - get { - Option(classLoader.getResource(resourceName)) flatMap ResourceFile.apply match { - case Some(ResourceFile(url, length, lastModified)) ⇒ - conditionalFor(length, lastModified) { - if (length > 0) { - withRangeSupportAndPrecompressedMediaTypeSupportAndExtractSettings { settings ⇒ - complete { - HttpEntity.Default(contentType, length, - StreamConverters.fromInputStream(() ⇒ url.openStream()) - .withAttributes(ActorAttributes.dispatcher(settings.fileIODispatcher))) // TODO is this needed? It already uses `val inputStreamSource = name("inputStreamSource") and IODispatcher` - } - } - } else complete(HttpEntity.Empty) - } - case _ ⇒ reject // not found or directory - } - } - else reject // don't serve the content of resource "directories" - - /** - * Completes GET requests with the content of a file underneath the given directory. - * If the file cannot be read the Route rejects the request. - * - * @group fileandresource - */ - def getFromDirectory(directoryName: String)(implicit resolver: ContentTypeResolver): Route = - extractUnmatchedPath { unmatchedPath ⇒ - extractLog { log ⇒ - safeDirectoryChildPath(withTrailingSlash(directoryName), unmatchedPath, log) match { - case "" ⇒ reject - case fileName ⇒ getFromFile(fileName) - } - } - } - - /** - * Completes GET requests with a unified listing of the contents of all given directories. - * The actual rendering of the directory contents is performed by the in-scope `Marshaller[DirectoryListing]`. - * - * @group fileandresource - */ - def listDirectoryContents(directories: String*)(implicit renderer: DirectoryRenderer): Route = - get { - extractRequestContext { ctx ⇒ - val path = ctx.unmatchedPath - val fullPath = ctx.request.uri.path.toString - val matchedLength = fullPath.lastIndexOf(path.toString) - require(matchedLength >= 0, s"Unmatched path '$path' wasn't a suffix of full path '$fullPath'. " + - "This usually means that ctx.unmatchedPath was manipulated inconsistently " + - "with ctx.request.uri.path") - val pathPrefix = fullPath.substring(0, matchedLength) - val pathString = withTrailingSlash(safeJoinPaths("/", path, ctx.log, '/')) - val dirs = directories flatMap { dir ⇒ - safeDirectoryChildPath(withTrailingSlash(dir), path, ctx.log) match { - case "" ⇒ None - case fileName ⇒ - val file = new File(fileName) - if (file.isDirectory && file.canRead) Some(file) else None - } - } - implicit val marshaller: ToEntityMarshaller[DirectoryListing] = renderer.marshaller(ctx.settings.renderVanityFooter) - - if (dirs.isEmpty) reject - else complete(DirectoryListing(pathPrefix + pathString, isRoot = pathString == "/", dirs.flatMap(_.listFiles))) - } - } - - /** - * Same as `getFromBrowseableDirectories` with only one directory. - * - * @group fileandresource - */ - def getFromBrowseableDirectory(directory: String)(implicit renderer: DirectoryRenderer, resolver: ContentTypeResolver): Route = - getFromBrowseableDirectories(directory) - - /** - * Serves the content of the given directories as a file system browser, i.e. files are sent and directories - * served as browseable listings. - * - * @group fileandresource - */ - def getFromBrowseableDirectories(directories: String*)(implicit renderer: DirectoryRenderer, resolver: ContentTypeResolver): Route = { - directories.map(getFromDirectory).reduceLeft(_ ~ _) ~ listDirectoryContents(directories: _*) - } - - /** - * Same as "getFromDirectory" except that the file is not fetched from the file system but rather from a - * "resource directory". - * If the requested resource is itself a directory or cannot be found or read the Route rejects the request. - * - * @group fileandresource - */ - def getFromResourceDirectory(directoryName: String, classLoader: ClassLoader = _defaultClassLoader)(implicit resolver: ContentTypeResolver): Route = { - val base = if (directoryName.isEmpty) "" else withTrailingSlash(directoryName) - - extractUnmatchedPath { path ⇒ - extractLog { log ⇒ - safeJoinPaths(base, path, log, separator = '/') match { - case "" ⇒ reject - case resourceName ⇒ getFromResource(resourceName, resolver(resourceName), classLoader) - } - } - } - } - - protected[http] def _defaultClassLoader: ClassLoader = classOf[ActorSystem].getClassLoader -} - -object FileAndResourceDirectives extends FileAndResourceDirectives { - private val withRangeSupportAndPrecompressedMediaTypeSupportAndExtractSettings = - RangeDirectives.withRangeSupport & - CodingDirectives.withPrecompressedMediaTypeSupport & - BasicDirectives.extractSettings - - private def withTrailingSlash(path: String): String = if (path endsWith "/") path else path + '/' - - /** - * Given a base directory and a (Uri) path, returns a path to a location contained in the base directory, - * while checking that no path traversal is possible. Path traversal is prevented by two individual measures: - * - A path segment must not be ".." and must not contain slashes or backslashes that may carry special meaning in - * file-system paths. This logic is intentionally a bit conservative as it might also prevent legitimate access - * to files containing one of those characters on a file-system that allows those characters in file names - * (e.g. backslash on posix). - * - Resulting paths are checked to be "contained" in the base directory. "Contained" means that the canonical location - * of the file (according to File.getCanonicalPath) has the canonical version of the basePath as a prefix. The exact - * semantics depend on the implementation of `File.getCanonicalPath` that may or may not resolve symbolic links and - * similar structures depending on the OS and the JDK implementation of file system accesses. - */ - private def safeDirectoryChildPath(basePath: String, path: Uri.Path, log: LoggingAdapter, separator: Char = File.separatorChar): String = - safeJoinPaths(basePath, path, log, separator) match { - case "" ⇒ "" - case path ⇒ checkIsSafeDescendant(basePath, path, log) - } - - private def safeJoinPaths(base: String, path: Uri.Path, log: LoggingAdapter, separator: Char = File.separatorChar): String = { - import java.lang.StringBuilder - @tailrec def rec(p: Uri.Path, result: StringBuilder = new StringBuilder(base)): String = - p match { - case Uri.Path.Empty ⇒ result.toString - case Uri.Path.Slash(tail) ⇒ rec(tail, result.append(separator)) - case Uri.Path.Segment(head, tail) ⇒ - if (head.indexOf('/') >= 0 || head.indexOf('\\') >= 0 || head == "..") { - log.warning("File-system path for base [{}] and Uri.Path [{}] contains suspicious path segment [{}], " + - "GET access was disallowed", base, path, head) - "" - } else rec(tail, result.append(head)) - } - rec(if (path.startsWithSlash) path.tail else path) - } - - /** - * Check against directory traversal attempts by making sure that the final is a "true child" - * of the given base directory. - * - * Returns "" if the finalPath is suspicious and the canonical path otherwise. - */ - private def checkIsSafeDescendant(basePath: String, finalPath: String, log: LoggingAdapter): String = { - val baseFile = new File(basePath) - val finalFile = new File(finalPath) - val canonicalFinalPath = finalFile.getCanonicalPath - - if (!canonicalFinalPath.startsWith(baseFile.getCanonicalPath)) { - log.warning(s"[$finalFile] points to a location that is not part of [$baseFile]. This might be a directory " + - "traversal attempt.") - "" - } else canonicalFinalPath - } - - object ResourceFile { - def apply(url: URL): Option[ResourceFile] = url.getProtocol match { - case "file" ⇒ - val file = new File(url.toURI) - if (file.isDirectory) None - else Some(ResourceFile(url, file.length(), file.lastModified())) - case "jar" ⇒ - val path = new URI(url.getPath).getPath // remove "file:" prefix and normalize whitespace - val bangIndex = path.indexOf('!') - val filePath = path.substring(0, bangIndex) - val resourcePath = path.substring(bangIndex + 2) - val jar = new java.util.zip.ZipFile(filePath) - try { - val entry = jar.getEntry(resourcePath) - Option(jar.getInputStream(entry)) map { is ⇒ - is.close() - ResourceFile(url, entry.getSize, entry.getTime) - } - } finally jar.close() - case _ ⇒ - val conn = url.openConnection() - try { - conn.setUseCaches(false) // otherwise the JDK will keep the connection open when we close! - val len = conn.getContentLength - val lm = conn.getLastModified - Some(ResourceFile(url, len, lm)) - } finally conn.getInputStream.close() - } - } - case class ResourceFile(url: URL, length: Long, lastModified: Long) - - trait DirectoryRenderer extends akka.http.javadsl.server.directives.DirectoryRenderer { - type JDL = akka.http.javadsl.server.directives.DirectoryListing - type SDL = akka.http.scaladsl.server.directives.DirectoryListing - type SRE = akka.http.scaladsl.model.RequestEntity - type JRE = akka.http.javadsl.model.RequestEntity - - def marshaller(renderVanityFooter: Boolean): ToEntityMarshaller[DirectoryListing] - - final override def directoryMarshaller(renderVanityFooter: Boolean): marshalling.Marshaller[JDL, JRE] = { - val combined = Marshaller.combined[JDL, SDL, SRE](x ⇒ JavaMapping.toScala(x)(RoutingJavaMapping.convertDirectoryListing))(marshaller(renderVanityFooter)) - .map(_.asJava) - marshalling.Marshaller.fromScala(combined) - } - - } - trait LowLevelDirectoryRenderer { - implicit def defaultDirectoryRenderer: DirectoryRenderer = - new DirectoryRenderer { - def marshaller(renderVanityFooter: Boolean): ToEntityMarshaller[DirectoryListing] = - DirectoryListing.directoryMarshaller(renderVanityFooter) - } - } - object DirectoryRenderer extends LowLevelDirectoryRenderer { - implicit def liftMarshaller(implicit _marshaller: ToEntityMarshaller[DirectoryListing]): DirectoryRenderer = - new DirectoryRenderer { - def marshaller(renderVanityFooter: Boolean): ToEntityMarshaller[DirectoryListing] = _marshaller - } - } -} - -trait ContentTypeResolver extends akka.http.javadsl.server.directives.ContentTypeResolver { - def apply(fileName: String): ContentType - final override def resolve(fileName: String): model.ContentType = apply(fileName) -} - -object ContentTypeResolver { - - /** - * The default way of resolving a filename to a ContentType is by looking up the file extension in the - * registry of all defined media-types. By default all non-binary file content is assumed to be UTF-8 encoded. - */ - implicit val Default: ContentTypeResolver = withDefaultCharset(HttpCharsets.`UTF-8`) - - def withDefaultCharset(charset: HttpCharset): ContentTypeResolver = - new ContentTypeResolver { - def apply(fileName: String) = { - val lastDotIx = fileName.lastIndexOf('.') - val mediaType = if (lastDotIx >= 0) { - fileName.substring(lastDotIx + 1) match { - case "gz" ⇒ fileName.lastIndexOf('.', lastDotIx - 1) match { - case -1 ⇒ MediaTypes.`application/octet-stream` - case x ⇒ MediaTypes.forExtension(fileName.substring(x + 1, lastDotIx)).withComp(MediaType.Gzipped) - } - case ext ⇒ MediaTypes.forExtension(ext) - } - } else MediaTypes.`application/octet-stream` - ContentType(mediaType, () ⇒ charset) - } - } - - def apply(f: String ⇒ ContentType): ContentTypeResolver = - new ContentTypeResolver { - def apply(fileName: String): ContentType = f(fileName) - } -} - -final case class DirectoryListing(path: String, isRoot: Boolean, files: Seq[File]) extends javadsl.server.directives.DirectoryListing { - override def getPath: String = path - override def getFiles: java.util.List[File] = files.asJava -} - -object DirectoryListing { - - private val html = - """ - |Index of $ - | - |

Index of $

- |
- |
-      |$
- |
$ - |
- |rendered by Akka Http on $ - |
$ - | - | - |""".stripMarginWithNewline("\n") split '$' - - def directoryMarshaller(renderVanityFooter: Boolean): ToEntityMarshaller[DirectoryListing] = - Marshaller.StringMarshaller.wrapWithEC(MediaTypes.`text/html`) { implicit ec ⇒ listing ⇒ - val DirectoryListing(path, isRoot, files) = listing - val filesAndNames = files.map(file ⇒ file → file.getName).sortBy(_._2) - val deduped = filesAndNames.zipWithIndex.flatMap { - case (fan @ (file, name), ix) ⇒ - if (ix == 0 || filesAndNames(ix - 1)._2 != name) Some(fan) else None - } - val (directoryFilesAndNames, fileFilesAndNames) = deduped.partition(_._1.isDirectory) - def maxNameLength(seq: Seq[(File, String)]) = if (seq.isEmpty) 0 else seq.map(_._2.length).max - val maxNameLen = math.max(maxNameLength(directoryFilesAndNames) + 1, maxNameLength(fileFilesAndNames)) - val sb = new java.lang.StringBuilder - sb.append(html(0)).append(path).append(html(1)).append(path).append(html(2)) - if (!isRoot) { - val secondToLastSlash = path.lastIndexOf('/', path.lastIndexOf('/', path.length - 1) - 1) - sb.append("../\n" format path.substring(0, secondToLastSlash)) - } - def lastModified(file: File) = DateTime(file.lastModified).toIsoLikeDateTimeString - def start(name: String) = - sb.append("").append(name).append("") - .append(" " * (maxNameLen - name.length)) - def renderDirectory(file: File, name: String) = - start(name + '/').append(" ").append(lastModified(file)).append('\n') - def renderFile(file: File, name: String) = { - val size = akka.http.impl.util.humanReadableByteCount(file.length, si = true) - start(name).append(" ").append(lastModified(file)) - sb.append(" ".substring(size.length)).append(size).append('\n') - } - for ((file, name) ← directoryFilesAndNames) renderDirectory(file, name) - for ((file, name) ← fileFilesAndNames) renderFile(file, name) - if (isRoot && files.isEmpty) sb.append("(no files)\n") - sb.append(html(3)) - if (renderVanityFooter) sb.append(html(4)).append(DateTime.now.toIsoLikeDateTimeString).append(html(5)) - sb.append(html(6)).toString - } -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/FileUploadDirectives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/FileUploadDirectives.scala deleted file mode 100644 index 3f497c4f8e..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/FileUploadDirectives.scala +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2015-2016 Lightbend Inc. - */ -package akka.http.scaladsl.server.directives - -import java.io.File -import akka.http.scaladsl.server.{ Directive1, MissingFormFieldRejection } -import akka.http.scaladsl.model.{ ContentType, Multipart } -import akka.util.ByteString - -import scala.concurrent.Future -import scala.util.{ Failure, Success } -import akka.stream.scaladsl._ -import akka.http.javadsl - -/** - * @groupname fileupload File upload directives - * @groupprio fileupload 80 - */ -trait FileUploadDirectives { - - import BasicDirectives._ - import RouteDirectives._ - import FutureDirectives._ - import MarshallingDirectives._ - - /** - * Streams the bytes of the file submitted using multipart with the given file name into a temporary file on disk. - * If there is an error writing to disk the request will be failed with the thrown exception, if there is no such - * field the request will be rejected, if there are multiple file parts with the same name, the first one will be - * used and the subsequent ones ignored. - * - * @group fileupload - */ - def uploadedFile(fieldName: String): Directive1[(FileInfo, File)] = - extractRequestContext.flatMap { ctx ⇒ - import ctx.executionContext - import ctx.materializer - - fileUpload(fieldName).flatMap { - case (fileInfo, bytes) ⇒ - - val destination = File.createTempFile("akka-http-upload", ".tmp") - val uploadedF: Future[(FileInfo, File)] = bytes.runWith(FileIO.toPath(destination.toPath)) - .map(_ ⇒ (fileInfo, destination)) - - onComplete[(FileInfo, File)](uploadedF).flatMap { - - case Success(uploaded) ⇒ - provide(uploaded) - - case Failure(ex) ⇒ - destination.delete() - failWith(ex) - - } - } - } - - /** - * Collects each body part that is a multipart file as a tuple containing metadata and a `Source` - * for streaming the file contents somewhere. If there is no such field the request will be rejected, - * if there are multiple file parts with the same name, the first one will be used and the subsequent - * ones ignored. - * - * @group fileupload - */ - def fileUpload(fieldName: String): Directive1[(FileInfo, Source[ByteString, Any])] = - entity(as[Multipart.FormData]).flatMap { formData ⇒ - extractRequestContext.flatMap { ctx ⇒ - implicit val mat = ctx.materializer - implicit val ec = ctx.executionContext - - val onePartSource: Source[(FileInfo, Source[ByteString, Any]), Any] = formData.parts - .filter(part ⇒ part.filename.isDefined && part.name == fieldName) - .map(part ⇒ (FileInfo(part.name, part.filename.get, part.entity.contentType), part.entity.dataBytes)) - .take(1) - - val onePartF = onePartSource.runWith(Sink.headOption[(FileInfo, Source[ByteString, Any])]) - - onSuccess(onePartF) - } - - }.flatMap { - case Some(tuple) ⇒ provide(tuple) - case None ⇒ reject(MissingFormFieldRejection(fieldName)) - } -} - -object FileUploadDirectives extends FileUploadDirectives - -/** - * Additional metadata about the file being uploaded/that was uploaded using the [[FileUploadDirectives]] - * - * @param fieldName Name of the form field the file was uploaded in - * @param fileName User specified name of the uploaded file - * @param contentType Content type of the file - */ -final case class FileInfo(fieldName: String, fileName: String, contentType: ContentType) extends javadsl.server.directives.FileInfo { - override def getFieldName = fieldName - override def getFileName = fileName - override def getContentType = contentType -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/FormFieldDirectives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/FormFieldDirectives.scala deleted file mode 100644 index 4b170422f3..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/FormFieldDirectives.scala +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import akka.http.impl.util._ -import akka.http.scaladsl.common._ -import akka.http.scaladsl.server.directives.RouteDirectives._ -import akka.http.scaladsl.unmarshalling.Unmarshaller.UnsupportedContentTypeException -import akka.http.scaladsl.util.FastFuture._ - -import scala.annotation.tailrec -import scala.collection.immutable -import scala.concurrent.Future -import scala.util.{ Failure, Success } - -/** - * @groupname form Form field directives - * @groupprio form 90 - */ -trait FormFieldDirectives extends ToNameReceptacleEnhancements { - import FormFieldDirectives._ - - /** - * Extracts HTTP form fields from the request as a ``Map[String, String]``. - * - * @group form - */ - def formFieldMap: Directive1[Map[String, String]] = _formFieldMap - - /** - * Extracts HTTP form fields from the request as a ``Map[String, List[String]]``. - * - * @group form - */ - def formFieldMultiMap: Directive1[Map[String, List[String]]] = _formFieldMultiMap - - /** - * Extracts HTTP form fields from the request as a ``Seq[(String, String)]``. - * - * @group form - */ - def formFieldSeq: Directive1[immutable.Seq[(String, String)]] = _formFieldSeq - - /** - * Extracts an HTTP form field from the request. - * Rejects the request if the defined form field matcher(s) don't match. - * - * @group form - */ - def formField(pdm: FieldMagnet): pdm.Out = pdm() - - /** - * Extracts a number of HTTP form field from the request. - * Rejects the request if the defined form field matcher(s) don't match. - * - * @group form - */ - def formFields(pdm: FieldMagnet): pdm.Out = pdm() - -} - -object FormFieldDirectives extends FormFieldDirectives { - - private val _formFieldSeq: Directive1[immutable.Seq[(String, String)]] = { - import BasicDirectives._ - import FutureDirectives._ - import akka.http.scaladsl.unmarshalling._ - - extract { ctx ⇒ - import ctx.{ executionContext, materializer } - Unmarshal(ctx.request.entity).to[StrictForm].fast.flatMap { form ⇒ - val fields = form.fields.collect { - case (name, field) if name.nonEmpty ⇒ - Unmarshal(field).to[String].map(fieldString ⇒ (name, fieldString)) - } - Future.sequence(fields) - } - }.flatMap { sequenceF ⇒ - onComplete(sequenceF).flatMap { - case Success(x) ⇒ provide(x) - case Failure(x: UnsupportedContentTypeException) ⇒ reject(UnsupportedRequestContentTypeRejection(x.supported)) - case Failure(_) ⇒ reject // TODO Use correct rejections - } - } - } - - private val _formFieldMultiMap: Directive1[Map[String, List[String]]] = { - @tailrec def append( - map: Map[String, List[String]], - fields: immutable.Seq[(String, String)]): Map[String, List[String]] = { - if (fields.isEmpty) { - map - } else { - val (key, value) = fields.head - append(map.updated(key, value :: map.getOrElse(key, Nil)), fields.tail) - } - } - - _formFieldSeq.map { - case seq ⇒ - append(Map.empty, seq) - } - } - - private val _formFieldMap: Directive1[Map[String, String]] = _formFieldSeq.map(_.toMap) - - sealed trait FieldMagnet { - type Out - def apply(): Out - } - object FieldMagnet { - implicit def apply[T](value: T)(implicit fdef: FieldDef[T]): FieldMagnet { type Out = fdef.Out } = - new FieldMagnet { - type Out = fdef.Out - def apply() = fdef(value) - } - } - - type FieldDefAux[A, B] = FieldDef[A] { type Out = B } - sealed trait FieldDef[T] { - type Out - def apply(value: T): Out - } - object FieldDef { - def fieldDef[A, B](f: A ⇒ B): FieldDefAux[A, B] = - new FieldDef[A] { - type Out = B - def apply(value: A) = f(value) - } - - import BasicDirectives._ - import FutureDirectives._ - import RouteDirectives._ - import akka.http.scaladsl.unmarshalling.{ FromStrictFormFieldUnmarshaller ⇒ FSFFU, _ } - type SFU = FromEntityUnmarshaller[StrictForm] - type FSFFOU[T] = Unmarshaller[Option[StrictForm.Field], T] - - private def extractField[A, B](f: A ⇒ Directive1[B]): FieldDefAux[A, Directive1[B]] = fieldDef(f) - private def handleFieldResult[T](fieldName: String, result: Future[T]): Directive1[T] = onComplete(result).flatMap { - case Success(x) ⇒ provide(x) - case Failure(Unmarshaller.NoContentException) ⇒ reject(MissingFormFieldRejection(fieldName)) - case Failure(x: UnsupportedContentTypeException) ⇒ reject(UnsupportedRequestContentTypeRejection(x.supported)) - case Failure(x) ⇒ reject(MalformedFormFieldRejection(fieldName, x.getMessage.nullAsEmpty, Option(x.getCause))) - } - - //////////////////// "regular" formField extraction //////////////////// - - private def fieldOfForm[T](fieldName: String, fu: Unmarshaller[Option[StrictForm.Field], T])(implicit sfu: SFU): RequestContext ⇒ Future[T] = { ctx ⇒ - import ctx.{ executionContext, materializer } - sfu(ctx.request.entity).fast.flatMap(form ⇒ fu(form field fieldName)) - } - private def filter[T](fieldName: String, fu: FSFFOU[T])(implicit sfu: SFU): Directive1[T] = - extract(fieldOfForm(fieldName, fu)).flatMap(r ⇒ handleFieldResult(fieldName, r)) - implicit def forString(implicit sfu: SFU, fu: FSFFU[String]): FieldDefAux[String, Directive1[String]] = - extractField[String, String] { fieldName ⇒ filter(fieldName, fu) } - implicit def forSymbol(implicit sfu: SFU, fu: FSFFU[String]): FieldDefAux[Symbol, Directive1[String]] = - extractField[Symbol, String] { symbol ⇒ filter(symbol.name, fu) } - implicit def forNR[T](implicit sfu: SFU, fu: FSFFU[T]): FieldDefAux[NameReceptacle[T], Directive1[T]] = - extractField[NameReceptacle[T], T] { nr ⇒ filter(nr.name, fu) } - implicit def forNUR[T](implicit sfu: SFU): FieldDefAux[NameUnmarshallerReceptacle[T], Directive1[T]] = - extractField[NameUnmarshallerReceptacle[T], T] { nr ⇒ filter(nr.name, StrictForm.Field.unmarshallerFromFSU(nr.um)) } - implicit def forNOR[T](implicit sfu: SFU, fu: FSFFOU[T]): FieldDefAux[NameOptionReceptacle[T], Directive1[Option[T]]] = - extractField[NameOptionReceptacle[T], Option[T]] { nr ⇒ filter[Option[T]](nr.name, fu) } - implicit def forNDR[T](implicit sfu: SFU, fu: FSFFOU[T]): FieldDefAux[NameDefaultReceptacle[T], Directive1[T]] = - extractField[NameDefaultReceptacle[T], T] { nr ⇒ filter(nr.name, fu withDefaultValue nr.default) } - implicit def forNOUR[T](implicit sfu: SFU): FieldDefAux[NameOptionUnmarshallerReceptacle[T], Directive1[Option[T]]] = - extractField[NameOptionUnmarshallerReceptacle[T], Option[T]] { nr ⇒ filter[Option[T]](nr.name, StrictForm.Field.unmarshallerFromFSU(nr.um): FSFFOU[T]) } - implicit def forNDUR[T](implicit sfu: SFU): FieldDefAux[NameDefaultUnmarshallerReceptacle[T], Directive1[T]] = - extractField[NameDefaultUnmarshallerReceptacle[T], T] { nr ⇒ filter(nr.name, (StrictForm.Field.unmarshallerFromFSU(nr.um): FSFFOU[T]) withDefaultValue nr.default) } - - //////////////////// required formField support //////////////////// - - private def requiredFilter[T](fieldName: String, fu: Unmarshaller[Option[StrictForm.Field], T], - requiredValue: Any)(implicit sfu: SFU): Directive0 = - extract(fieldOfForm(fieldName, fu)).flatMap { - onComplete(_).flatMap { - case Success(value) if value == requiredValue ⇒ pass - case _ ⇒ reject - } - } - implicit def forRVR[T](implicit sfu: SFU, fu: FSFFU[T]): FieldDefAux[RequiredValueReceptacle[T], Directive0] = - fieldDef[RequiredValueReceptacle[T], Directive0] { rvr ⇒ requiredFilter(rvr.name, fu, rvr.requiredValue) } - implicit def forRVDR[T](implicit sfu: SFU): FieldDefAux[RequiredValueUnmarshallerReceptacle[T], Directive0] = - fieldDef[RequiredValueUnmarshallerReceptacle[T], Directive0] { rvr ⇒ requiredFilter(rvr.name, StrictForm.Field.unmarshallerFromFSU(rvr.um), rvr.requiredValue) } - - //////////////////// repeated formField support //////////////////// - - private def repeatedFilter[T](fieldName: String, fu: FSFFU[T])(implicit sfu: SFU): Directive1[Iterable[T]] = - extract { ctx ⇒ - import ctx.{ executionContext, materializer } - sfu(ctx.request.entity).fast.flatMap(form ⇒ Future.sequence(form.fields.collect { case (`fieldName`, value) ⇒ fu(value) })) - }.flatMap { result ⇒ - handleFieldResult(fieldName, result) - } - implicit def forRepVR[T](implicit sfu: SFU, fu: FSFFU[T]): FieldDefAux[RepeatedValueReceptacle[T], Directive1[Iterable[T]]] = - extractField[RepeatedValueReceptacle[T], Iterable[T]] { rvr ⇒ repeatedFilter(rvr.name, fu) } - implicit def forRepVDR[T](implicit sfu: SFU): FieldDefAux[RepeatedValueUnmarshallerReceptacle[T], Directive1[Iterable[T]]] = - extractField[RepeatedValueUnmarshallerReceptacle[T], Iterable[T]] { rvr ⇒ repeatedFilter(rvr.name, StrictForm.Field.unmarshallerFromFSU(rvr.um)) } - - //////////////////// tuple support //////////////////// - - import akka.http.scaladsl.server.util.BinaryPolyFunc - import akka.http.scaladsl.server.util.TupleOps._ - - implicit def forTuple[T](implicit fold: FoldLeft[Directive0, T, ConvertFieldDefAndConcatenate.type]): FieldDefAux[T, fold.Out] = - fieldDef[T, fold.Out](fold(pass, _)) - - object ConvertFieldDefAndConcatenate extends BinaryPolyFunc { - implicit def from[P, TA, TB](implicit fdef: FieldDefAux[P, Directive[TB]], ev: Join[TA, TB]): BinaryPolyFunc.Case[Directive[TA], P, ConvertFieldDefAndConcatenate.type] { type Out = Directive[ev.Out] } = - at[Directive[TA], P] { (a, t) ⇒ a & fdef(t) } - } - } -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/FramedEntityStreamingDirectives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/FramedEntityStreamingDirectives.scala deleted file mode 100644 index dc4a5ab001..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/FramedEntityStreamingDirectives.scala +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2009-2015 Typesafe Inc. - */ -package akka.http.scaladsl.server.directives - -import akka.NotUsed -import akka.http.scaladsl.common -import akka.http.scaladsl.common.EntityStreamingSupport -import akka.http.scaladsl.marshalling._ -import akka.http.scaladsl.model._ -import akka.http.scaladsl.unmarshalling.{ Unmarshaller, _ } -import akka.http.scaladsl.util.FastFuture -import akka.stream.scaladsl.{ Flow, Keep, Source } -import akka.util.ByteString - -import scala.language.implicitConversions - -/** - * Allows the [[MarshallingDirectives.entity]] directive to extract a [[Source]] of elements. - * - * See [[common.EntityStreamingSupport]] for useful default framing `Flow` instances and - * support traits such as `SprayJsonSupport` (or your other favourite JSON library) to provide the needed [[Marshaller]] s. - */ -trait FramedEntityStreamingDirectives extends MarshallingDirectives { - - type RequestToSourceUnmarshaller[T] = FromRequestUnmarshaller[Source[T, NotUsed]] - - /** - * Extracts entity as [[Source]] of elements of type `T`. - * This is achieved by applying the implicitly provided (in the following order): - * - * - 1st: chunk-up the incoming [[ByteString]]s by applying the `Content-Type`-aware framing - * - 2nd: apply the [[Unmarshaller]] (from [[ByteString]] to `T`) for each of the respective "chunks" (e.g. for each JSON element contained within an array). - * - * The request will be rejected with an [[akka.http.scaladsl.server.UnsupportedRequestContentTypeRejection]] if - * its [[ContentType]] is not supported by the used `framing` or `unmarshaller`. - * - * Cancelling extracted [[Source]] closes the connection abruptly (same as cancelling the `entity.dataBytes`). - * - * See also [[MiscDirectives.withoutSizeLimit]] as you may want to allow streaming infinite streams of data in this route. - * By default the uploaded data is limited by the `akka.http.parsing.max-content-length`. - */ - final def asSourceOf[T](implicit um: FromByteStringUnmarshaller[T], support: EntityStreamingSupport): RequestToSourceUnmarshaller[T] = - asSourceOfInternal(um, support) - - /** - * Extracts entity as [[Source]] of elements of type `T`. - * This is achieved by applying the implicitly provided (in the following order): - * - * - 1st: chunk-up the incoming [[ByteString]]s by applying the `Content-Type`-aware framing - * - 2nd: apply the [[Unmarshaller]] (from [[ByteString]] to `T`) for each of the respective "chunks" (e.g. for each JSON element contained within an array). - * - * The request will be rejected with an [[akka.http.scaladsl.server.UnsupportedRequestContentTypeRejection]] if - * its [[ContentType]] is not supported by the used `framing` or `unmarshaller`. - * - * Cancelling extracted [[Source]] closes the connection abruptly (same as cancelling the `entity.dataBytes`). - * - * See also [[MiscDirectives.withoutSizeLimit]] as you may want to allow streaming infinite streams of data in this route. - * By default the uploaded data is limited by the `akka.http.parsing.max-content-length`. - */ - final def asSourceOf[T](support: EntityStreamingSupport)(implicit um: FromByteStringUnmarshaller[T]): RequestToSourceUnmarshaller[T] = - asSourceOfInternal(um, support) - - // format: OFF - private final def asSourceOfInternal[T](um: Unmarshaller[ByteString, T], support: EntityStreamingSupport): RequestToSourceUnmarshaller[T] = - Unmarshaller.withMaterializer[HttpRequest, Source[T, NotUsed]] { implicit ec ⇒ implicit mat ⇒ req ⇒ - val entity = req.entity - if (support.supported.matches(entity.contentType)) { - val bytes = entity.dataBytes - val frames = bytes.via(support.framingDecoder) - val marshalling = - if (support.unordered) Flow[ByteString].mapAsyncUnordered(support.parallelism)(bs => um(bs)(ec, mat)) - else Flow[ByteString].mapAsync(support.parallelism)(bs => um(bs)(ec, mat)) - - val elements = frames.viaMat(marshalling)(Keep.right) - FastFuture.successful(elements) - - } else FastFuture.failed(Unmarshaller.UnsupportedContentTypeException(support.supported)) - } - // format: ON - -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/FutureDirectives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/FutureDirectives.scala deleted file mode 100644 index 73c64e9572..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/FutureDirectives.scala +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import akka.http.scaladsl.marshalling.ToResponseMarshaller -import akka.http.scaladsl.server.Directives._ -import akka.http.scaladsl.server.util.Tupler -import akka.http.scaladsl.util.FastFuture._ -import akka.pattern.{ CircuitBreaker, CircuitBreakerOpenException } -import akka.stream.scaladsl.Sink - -import scala.concurrent.Future -import scala.util.{ Failure, Success, Try } - -// format: OFF - -/** - * @groupname future Future directives - * @groupprio future 100 - */ -trait FutureDirectives { - - import RouteDirectives._ - - /** - * "Unwraps" a `Future[T]` and runs the inner route after future - * completion with the future's value as an extraction of type `Try[T]`. - * - * @group future - */ - def onComplete[T](future: ⇒ Future[T]): Directive1[Try[T]] = - Directive { inner ⇒ ctx ⇒ - import ctx.executionContext - future.fast.transformWith(t ⇒ inner(Tuple1(t))(ctx)) - } - - /** - * "Unwraps" a `Future[T]` and runs the inner route after future - * completion with the future's value as an extraction of type `T` if - * the supplied `CircuitBreaker` is closed. - * - * If the supplied [[CircuitBreaker]] is open the request is rejected - * with a [[CircuitBreakerOpenRejection]]. - * - * @group future - */ - def onCompleteWithBreaker[T](breaker: CircuitBreaker)(future: ⇒ Future[T]): Directive1[Try[T]] = - onComplete(breaker.withCircuitBreaker(future)).flatMap { - case Failure(ex: CircuitBreakerOpenException) ⇒ - extractRequestContext.flatMap { ctx ⇒ - ctx.request.entity.dataBytes.runWith(Sink.cancelled)(ctx.materializer) - reject(CircuitBreakerOpenRejection(ex)) - } - case x ⇒ provide(x) - } - - /** - * "Unwraps" a `Future[T]` and runs the inner route after future - * completion with the future's value as an extraction of type `T`. - * If the future fails its failure Throwable is bubbled up to the nearest - * ExceptionHandler. - * If type `T` is already a Tuple it is directly expanded into the respective - * number of extractions. - * - * @group future - */ - def onSuccess(magnet: OnSuccessMagnet): Directive[magnet.Out] = magnet.directive - - /** - * "Unwraps" a `Future[T]` and runs the inner route when the future has failed - * with the future's failure exception as an extraction of type `Throwable`. - * If the future succeeds the request is completed using the values marshaller - * (This directive therefore requires a marshaller for the futures type to be - * implicitly available.) - * - * @group future - */ - def completeOrRecoverWith(magnet: CompleteOrRecoverWithMagnet): Directive1[Throwable] = magnet.directive -} - -object FutureDirectives extends FutureDirectives - -trait OnSuccessMagnet { - type Out - def directive: Directive[Out] -} - -object OnSuccessMagnet { - implicit def apply[T](future: ⇒ Future[T])(implicit tupler: Tupler[T]): OnSuccessMagnet { type Out = tupler.Out } = - new OnSuccessMagnet { - type Out = tupler.Out - val directive = Directive[tupler.Out] { inner ⇒ ctx ⇒ - import ctx.executionContext - future.fast.flatMap(t ⇒ inner(tupler(t))(ctx)) - }(tupler.OutIsTuple) - } -} - -trait CompleteOrRecoverWithMagnet { - def directive: Directive1[Throwable] -} - -object CompleteOrRecoverWithMagnet { - implicit def apply[T](future: ⇒ Future[T])(implicit m: ToResponseMarshaller[T]): CompleteOrRecoverWithMagnet = - new CompleteOrRecoverWithMagnet { - val directive = Directive[Tuple1[Throwable]] { inner ⇒ ctx ⇒ - import ctx.executionContext - future.fast.transformWith { - case Success(res) ⇒ ctx.complete(res) - case Failure(error) ⇒ inner(Tuple1(error))(ctx) - } - } - } -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/HeaderDirectives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/HeaderDirectives.scala deleted file mode 100644 index 1fa50f54bd..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/HeaderDirectives.scala +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import akka.http.impl.util._ -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers._ - -import scala.reflect.ClassTag -import scala.util.control.NonFatal - -/** - * @groupname header Header directives - * @groupprio header 110 - */ -trait HeaderDirectives { - import BasicDirectives._ - import RouteDirectives._ - - /** - * Checks that request comes from the same origin. Extracts the [[Origin]] header value and verifies that - * allowed range contains the obtained value. In the case of absent of the [[Origin]] header rejects - * with [[MissingHeaderRejection]]. If the origin value is not in the allowed range - * rejects with an [[InvalidOriginRejection]] and [[StatusCodes.Forbidden]] status. - * - * @group header - */ - def checkSameOrigin(allowed: HttpOriginRange.Default): Directive0 = { - headerValueByType[Origin]().flatMap { origin ⇒ - if (origin.origins.exists(allowed.matches)) pass - else reject(InvalidOriginRejection(allowed.origins)) - } - } - - /** - * Extracts an HTTP header value using the given function. If the function result is undefined for all headers the - * request is rejected with an empty rejection set. If the given function throws an exception the request is rejected - * with a [[akka.http.scaladsl.server.MalformedHeaderRejection]]. - * - * @group header - */ - def headerValue[T](f: HttpHeader ⇒ Option[T]): Directive1[T] = { - val protectedF: HttpHeader ⇒ Option[Either[Rejection, T]] = header ⇒ - try f(header).map(Right.apply) - catch { - case NonFatal(e) ⇒ Some(Left(MalformedHeaderRejection(header.name, e.getMessage.nullAsEmpty, Some(e)))) - } - - extract(_.request.headers.collectFirst(Function.unlift(protectedF))).flatMap { - case Some(Right(a)) ⇒ provide(a) - case Some(Left(rejection)) ⇒ reject(rejection) - case None ⇒ reject - } - } - - /** - * Extracts an HTTP header value using the given partial function. If the function is undefined for all headers the - * request is rejected with an empty rejection set. - * - * @group header - */ - def headerValuePF[T](pf: PartialFunction[HttpHeader, T]): Directive1[T] = headerValue(pf.lift) - - /** - * Extracts the value of the first HTTP request header with the given name. - * If no header with a matching name is found the request is rejected with a [[akka.http.scaladsl.server.MissingHeaderRejection]]. - * - * @group header - */ - def headerValueByName(headerName: Symbol): Directive1[String] = headerValueByName(headerName.name) - - /** - * Extracts the value of the HTTP request header with the given name. - * If no header with a matching name is found the request is rejected with a [[akka.http.scaladsl.server.MissingHeaderRejection]]. - * - * @group header - */ - def headerValueByName(headerName: String): Directive1[String] = - headerValue(optionalValue(headerName.toLowerCase)) | reject(MissingHeaderRejection(headerName)) - - /** - * Extracts the first HTTP request header of the given type. - * If no header with a matching type is found the request is rejected with a [[akka.http.scaladsl.server.MissingHeaderRejection]]. - * - * Custom headers will only be matched by this directive if they extend [[ModeledCustomHeader]] - * and provide a companion extending [[ModeledCustomHeaderCompanion]]. - * - * @group header - */ - def headerValueByType[T](magnet: HeaderMagnet[T]): Directive1[T] = - headerValuePF(magnet.extractPF) | reject(MissingHeaderRejection(magnet.headerName)) - - //#optional-header - /** - * Extracts an optional HTTP header value using the given function. - * If the given function throws an exception the request is rejected - * with a [[akka.http.scaladsl.server.MalformedHeaderRejection]]. - * - * @group header - */ - def optionalHeaderValue[T](f: HttpHeader ⇒ Option[T]): Directive1[Option[T]] = - headerValue(f).map(Some(_): Option[T]).recoverPF { - case Nil ⇒ provide(None) - } - //# - - /** - * Extracts an optional HTTP header value using the given partial function. - * If the given function throws an exception the request is rejected - * with a [[akka.http.scaladsl.server.MalformedHeaderRejection]]. - * - * @group header - */ - def optionalHeaderValuePF[T](pf: PartialFunction[HttpHeader, T]): Directive1[Option[T]] = - optionalHeaderValue(pf.lift) - - /** - * Extracts the value of the optional HTTP request header with the given name. - * - * @group header - */ - def optionalHeaderValueByName(headerName: Symbol): Directive1[Option[String]] = - optionalHeaderValueByName(headerName.name) - - /** - * Extracts the value of the optional HTTP request header with the given name. - * - * @group header - */ - def optionalHeaderValueByName(headerName: String): Directive1[Option[String]] = { - val lowerCaseName = headerName.toLowerCase - extract(_.request.headers.collectFirst { - case HttpHeader(`lowerCaseName`, value) ⇒ value - }) - } - - /** - * Extract the header value of the optional HTTP request header with the given type. - * - * Custom headers will only be matched by this directive if they extend [[ModeledCustomHeader]] - * and provide a companion extending [[ModeledCustomHeaderCompanion]]. - * - * @group header - */ - def optionalHeaderValueByType[T <: HttpHeader](magnet: HeaderMagnet[T]): Directive1[Option[T]] = - optionalHeaderValuePF(magnet.extractPF) - - private def optionalValue(lowerCaseName: String): HttpHeader ⇒ Option[String] = { - case HttpHeader(`lowerCaseName`, value) ⇒ Some(value) - case _ ⇒ None - } -} - -object HeaderDirectives extends HeaderDirectives - -trait HeaderMagnet[T] { - def classTag: ClassTag[T] - def runtimeClass: Class[T] - def headerName = ModeledCompanion.nameFromClass(runtimeClass) - - /** - * Returns a partial function that checks if the input value is of runtime type - * T and returns the value if it does. Doesn't take erased information into account. - */ - def extractPF: PartialFunction[HttpHeader, T] -} -object HeaderMagnet extends LowPriorityHeaderMagnetImplicits { - - /** - * If possible we want to apply the special logic for [[ModeledCustomHeader]] to extract custom headers by type, - * otherwise the default `fromUnit` is good enough (for headers that the parser emits in the right type already). - */ - implicit def fromUnitForModeledCustomHeader[T <: ModeledCustomHeader[T], H <: ModeledCustomHeaderCompanion[T]](u: Unit)(implicit tag: ClassTag[T], companion: ModeledCustomHeaderCompanion[T]): HeaderMagnet[T] = - fromClassTagForModeledCustomHeader[T, H](tag, companion) - - implicit def fromClassForModeledCustomHeader[T <: ModeledCustomHeader[T], H <: ModeledCustomHeaderCompanion[T]](clazz: Class[T], companion: ModeledCustomHeaderCompanion[T]): HeaderMagnet[T] = - fromClassTagForModeledCustomHeader(ClassTag(clazz), companion) - - implicit def fromClassTagForModeledCustomHeader[T <: ModeledCustomHeader[T], H <: ModeledCustomHeaderCompanion[T]](tag: ClassTag[T], companion: ModeledCustomHeaderCompanion[T]): HeaderMagnet[T] = - new HeaderMagnet[T] { - override def runtimeClass = tag.runtimeClass.asInstanceOf[Class[T]] - override def classTag = tag - override def extractPF = { - case h if h.is(companion.lowercaseName) ⇒ companion.apply(h.value) - } - } - -} - -trait LowPriorityHeaderMagnetImplicits { - implicit def fromClassNormalHeader[T <: HttpHeader](clazz: Class[T]): HeaderMagnet[T] = - fromClassTagNormalHeader(ClassTag(clazz)) - - // TODO DRY? - implicit def fromClassNormalJavaHeader[T <: akka.http.javadsl.model.HttpHeader](clazz: Class[T]): HeaderMagnet[T] = - new HeaderMagnet[T] { - override def classTag: ClassTag[T] = ClassTag(clazz) - override def runtimeClass: Class[T] = clazz - override def extractPF: PartialFunction[HttpHeader, T] = { case x if runtimeClass.isAssignableFrom(x.getClass) ⇒ x.asInstanceOf[T] } - } - - implicit def fromUnitNormalHeader[T <: HttpHeader](u: Unit)(implicit tag: ClassTag[T]): HeaderMagnet[T] = - fromClassTagNormalHeader(tag) - - implicit def fromClassTagNormalHeader[T <: HttpHeader](tag: ClassTag[T]): HeaderMagnet[T] = - new HeaderMagnet[T] { - val classTag: ClassTag[T] = tag - val runtimeClass: Class[T] = tag.runtimeClass.asInstanceOf[Class[T]] - val extractPF: PartialFunction[Any, T] = { case x if runtimeClass.isAssignableFrom(x.getClass) ⇒ x.asInstanceOf[T] } - } -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/HostDirectives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/HostDirectives.scala deleted file mode 100644 index 775adff8da..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/HostDirectives.scala +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import scala.util.matching.Regex -import akka.http.impl.util._ - -/** - * @groupname host Host directives - * @groupprio host 110 - */ -trait HostDirectives { - import BasicDirectives._ - import RouteDirectives._ - - /** - * Extracts the hostname part of the Host request header value. - * - * @group host - */ - def extractHost: Directive1[String] = HostDirectives._extractHost - - /** - * Rejects all requests with a host name different from the given ones. - * - * @group host - */ - def host(hostNames: String*): Directive0 = host(hostNames.contains(_)) - - //#require-host - /** - * Rejects all requests for whose host name the given predicate function returns false. - * - * @group host - */ - def host(predicate: String ⇒ Boolean): Directive0 = extractHost.require(predicate) - //# - - /** - * Rejects all requests with a host name that doesn't have a prefix matching the given regular expression. - * For all matching requests the prefix string matching the regex is extracted and passed to the inner route. - * If the regex contains a capturing group only the string matched by this group is extracted. - * If the regex contains more than one capturing group an IllegalArgumentException is thrown. - * - * @group host - */ - def host(regex: Regex): Directive1[String] = { - def forFunc(regexMatch: String ⇒ Option[String]): Directive1[String] = { - extractHost.flatMap { name ⇒ - regexMatch(name) match { - case Some(matched) ⇒ provide(matched) - case None ⇒ reject - } - } - } - - regex.groupCount match { - case 0 ⇒ forFunc(regex.findPrefixOf(_)) - case 1 ⇒ forFunc(regex.findPrefixMatchOf(_).map(_.group(1))) - case _ ⇒ throw new IllegalArgumentException("Path regex '" + regex.pattern.pattern + - "' must not contain more than one capturing group") - } - } - -} - -object HostDirectives extends HostDirectives { - import BasicDirectives._ - - private val _extractHost: Directive1[String] = - extract(_.request.uri.authority.host.address) -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/MarshallingDirectives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/MarshallingDirectives.scala deleted file mode 100644 index d887def314..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/MarshallingDirectives.scala +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import scala.concurrent.Promise -import scala.util.{ Failure, Success } -import akka.http.scaladsl.marshalling.ToResponseMarshaller -import akka.http.scaladsl.unmarshalling.{ Unmarshaller, FromRequestUnmarshaller } -import akka.http.impl.util._ - -/** - * @groupname marshalling Marshalling directives - * @groupprio marshalling 120 - */ -trait MarshallingDirectives { - import BasicDirectives._ - import FutureDirectives._ - import RouteDirectives._ - - /** - * Unmarshalls the requests entity to the given type passes it to its inner Route. - * If there is a problem with unmarshalling the request is rejected with the [[Rejection]] - * produced by the unmarshaller. - * - * @group marshalling - */ - def entity[T](um: FromRequestUnmarshaller[T]): Directive1[T] = - extractRequestContext.flatMap[Tuple1[T]] { ctx ⇒ - import ctx.executionContext - import ctx.materializer - onComplete(um(ctx.request)) flatMap { - case Success(value) ⇒ provide(value) - case Failure(RejectionError(r)) ⇒ reject(r) - case Failure(Unmarshaller.NoContentException) ⇒ reject(RequestEntityExpectedRejection) - case Failure(Unmarshaller.UnsupportedContentTypeException(x)) ⇒ reject(UnsupportedRequestContentTypeRejection(x)) - case Failure(x: IllegalArgumentException) ⇒ reject(ValidationRejection(x.getMessage.nullAsEmpty, Some(x))) - case Failure(x) ⇒ reject(MalformedRequestContentRejection(x.getMessage.nullAsEmpty, x)) - } - } & cancelRejections(RequestEntityExpectedRejection.getClass, classOf[UnsupportedRequestContentTypeRejection]) - - /** - * Returns the in-scope [[FromRequestUnmarshaller]] for the given type. - * - * @group marshalling - */ - def as[T](implicit um: FromRequestUnmarshaller[T]) = um - - /** - * Uses the marshaller for the given type to produce a completion function that is passed to its inner function. - * You can use it do decouple marshaller resolution from request completion. - * - * @group marshalling - */ - def completeWith[T](marshaller: ToResponseMarshaller[T])(inner: (T ⇒ Unit) ⇒ Unit): Route = - extractRequestContext { ctx ⇒ - implicit val m = marshaller - complete { - val promise = Promise[T]() - inner(promise.success(_)) - promise.future - } - } - - /** - * Returns the in-scope Marshaller for the given type. - * - * @group marshalling - */ - def instanceOf[T](implicit m: ToResponseMarshaller[T]): ToResponseMarshaller[T] = m - - /** - * Completes the request using the given function. The input to the function is produced with the in-scope - * entity unmarshaller and the result value of the function is marshalled with the in-scope marshaller. - * - * @group marshalling - */ - def handleWith[A, B](f: A ⇒ B)(implicit um: FromRequestUnmarshaller[A], m: ToResponseMarshaller[B]): Route = - entity(um) { a ⇒ complete(f(a)) } -} - -object MarshallingDirectives extends MarshallingDirectives diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/MethodDirectives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/MethodDirectives.scala deleted file mode 100644 index 5dffa97171..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/MethodDirectives.scala +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import akka.http.scaladsl.model.{ StatusCodes, HttpMethod } -import akka.http.scaladsl.model.HttpMethods._ - -/** - * @groupname method Method directives - * @groupprio method 130 - */ -trait MethodDirectives { - import BasicDirectives._ - import RouteDirectives._ - import ParameterDirectives._ - import MethodDirectives._ - - /** - * Rejects all non-DELETE requests. - * - * @group method - */ - def delete: Directive0 = _delete - - /** - * Rejects all non-GET requests. - * - * @group method - */ - def get: Directive0 = _get - - /** - * Rejects all non-HEAD requests. - * - * @group method - */ - def head: Directive0 = _head - - /** - * Rejects all non-OPTIONS requests. - * - * @group method - */ - def options: Directive0 = _options - - /** - * Rejects all non-PATCH requests. - * - * @group method - */ - def patch: Directive0 = _patch - - /** - * Rejects all non-POST requests. - * - * @group method - */ - def post: Directive0 = _post - - /** - * Rejects all non-PUT requests. - * - * @group method - */ - def put: Directive0 = _put - - /** - * Extracts the request method. - * - * @group method - */ - def extractMethod: Directive1[HttpMethod] = _extractMethod - - //#method - /** - * Rejects all requests whose HTTP method does not match the given one. - * - * @group method - */ - def method(httpMethod: HttpMethod): Directive0 = - extractMethod.flatMap[Unit] { - case `httpMethod` ⇒ pass - case _ ⇒ reject(MethodRejection(httpMethod)) - } & cancelRejections(classOf[MethodRejection]) - //# - - /** - * Changes the HTTP method of the request to the value of the specified query string parameter. If the query string - * parameter is not specified this directive has no effect. If the query string is specified as something that is not - * a HTTP method, then this directive completes the request with a `501 Not Implemented` response. - * - * This directive is useful for: - * - Use in combination with JSONP (JSONP only supports GET) - * - Supporting older browsers that lack support for certain HTTP methods. E.g. IE8 does not support PATCH - * - * @group method - */ - def overrideMethodWithParameter(paramName: String): Directive0 = - parameter(paramName?) flatMap { - case Some(method) ⇒ - getForKey(method.toUpperCase) match { - case Some(m) ⇒ mapRequest(_.copy(method = m)) - case _ ⇒ complete(StatusCodes.NotImplemented) - } - case None ⇒ pass - } -} - -object MethodDirectives extends MethodDirectives { - private val _extractMethod: Directive1[HttpMethod] = - BasicDirectives.extract(_.request.method) - - // format: OFF - private val _delete : Directive0 = method(DELETE) - private val _get : Directive0 = method(GET) - private val _head : Directive0 = method(HEAD) - private val _options: Directive0 = method(OPTIONS) - private val _patch : Directive0 = method(PATCH) - private val _post : Directive0 = method(POST) - private val _put : Directive0 = method(PUT) - // format: ON -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/MiscDirectives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/MiscDirectives.scala deleted file mode 100644 index 011e916f6d..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/MiscDirectives.scala +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import akka.http.scaladsl.model._ -import akka.http.scaladsl.server.directives.BasicDirectives._ -import headers._ - -/** - * @groupname misc Miscellaneous directives - * @groupprio misc 140 - */ -trait MiscDirectives { - import RouteDirectives._ - - /** - * Checks the given condition before running its inner route. - * If the condition fails the route is rejected with a [[ValidationRejection]]. - * - * @group misc - */ - def validate(check: ⇒ Boolean, errorMsg: String): Directive0 = - Directive { inner ⇒ if (check) inner(()) else reject(ValidationRejection(errorMsg)) } - - /** - * Extracts the client's IP from either the X-Forwarded-For, Remote-Address or X-Real-IP header - * (in that order of priority). - * - * @group misc - */ - def extractClientIP: Directive1[RemoteAddress] = MiscDirectives._extractClientIP - - /** - * Rejects if the request entity is non-empty. - * - * @group misc - */ - def requestEntityEmpty: Directive0 = MiscDirectives._requestEntityEmpty - - /** - * Rejects with a [[RequestEntityExpectedRejection]] if the request entity is empty. - * Non-empty requests are passed on unchanged to the inner route. - * - * @group misc - */ - def requestEntityPresent: Directive0 = MiscDirectives._requestEntityPresent - - /** - * Converts responses with an empty entity into (empty) rejections. - * This way you can, for example, have the marshalling of a ''None'' option - * be treated as if the request could not be matched. - * - * @group misc - */ - def rejectEmptyResponse: Directive0 = MiscDirectives._rejectEmptyResponse - - /** - * Inspects the request's `Accept-Language` header and determines, - * which of the given language alternatives is preferred by the client. - * (See http://tools.ietf.org/html/rfc7231#section-5.3.5 for more details on the - * negotiation logic.) - * If there are several best language alternatives that the client - * has equal preference for (even if this preference is zero!) - * the order of the arguments is used as a tie breaker (First one wins). - * - * @group misc - */ - def selectPreferredLanguage(first: Language, more: Language*): Directive1[Language] = - BasicDirectives.extractRequest.map { request ⇒ - LanguageNegotiator(request.headers).pickLanguage(first :: List(more: _*)) getOrElse first - } - - /** - * Fails the stream with [[akka.http.scaladsl.model.EntityStreamSizeException]] if its request entity size exceeds - * given limit. Limit given as parameter overrides limit configured with `akka.http.parsing.max-content-length`. - * - * Beware that request entity size check is executed when entity is consumed. - * - * @group misc - */ - def withSizeLimit(maxBytes: Long): Directive0 = - mapRequestContext(_.mapRequest(_.mapEntity(_.withSizeLimit(maxBytes)))) - - /** - * - * Disables the size limit (configured by `akka.http.parsing.max-content-length` by default) checking on the incoming - * [[HttpRequest]] entity. - * Can be useful when handling arbitrarily large data uploads in specific parts of your routes. - * - * @group misc - */ - def withoutSizeLimit: Directive0 = MiscDirectives._withoutSizeLimit -} - -object MiscDirectives extends MiscDirectives { - import BasicDirectives._ - import HeaderDirectives._ - import RouteDirectives._ - import RouteResult._ - - private val _extractClientIP: Directive1[RemoteAddress] = - headerValuePF { case `X-Forwarded-For`(Seq(address, _*)) ⇒ address } | - headerValuePF { case `Remote-Address`(address) ⇒ address } | - headerValuePF { case `X-Real-Ip`(address) ⇒ address } | - provide(RemoteAddress.Unknown) - - private val _requestEntityEmpty: Directive0 = - extract(_.request.entity.isKnownEmpty).flatMap(if (_) pass else reject) - - private val _requestEntityPresent: Directive0 = - extract(_.request.entity.isKnownEmpty).flatMap(if (_) reject else pass) - - private val _rejectEmptyResponse: Directive0 = - mapRouteResult { - case Complete(response) if response.entity.isKnownEmpty ⇒ Rejected(Nil) - case x ⇒ x - } - - private val _withoutSizeLimit: Directive0 = - mapRequestContext(_.mapRequest(_.mapEntity(_.withoutSizeLimit))) -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/ParameterDirectives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/ParameterDirectives.scala deleted file mode 100644 index de711ed89e..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/ParameterDirectives.scala +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import akka.http.javadsl.server.directives.CorrespondsTo - -import scala.collection.immutable -import scala.concurrent.{ ExecutionContext, Future } -import scala.util.{ Failure, Success } -import akka.http.scaladsl.common._ -import akka.http.impl.util._ - -/** - * @groupname param Parameter directives - * @groupprio param 150 - */ -trait ParameterDirectives extends ToNameReceptacleEnhancements { - import ParameterDirectives._ - - /** - * Extracts the request's query parameters as a `Map[String, String]`. - * - * @group param - */ - def parameterMap: Directive1[Map[String, String]] = _parameterMap - - /** - * Extracts the request's query parameters as a `Map[String, List[String]]`. - * - * @group param - */ - def parameterMultiMap: Directive1[Map[String, List[String]]] = _parameterMultiMap - - /** - * Extracts the request's query parameters as a `Seq[(String, String)]`. - * - * @group param - */ - def parameterSeq: Directive1[immutable.Seq[(String, String)]] = _parameterSeq - - /** - * Extracts a query parameter value from the request. - * Rejects the request if the defined query parameter matcher(s) don't match. - * - * Due to a bug in Scala 2.10, invocations of this method sometimes fail to compile with an - * "too many arguments for method parameter" or "type mismatch" error. - * - * As a workaround add an `import ParameterDirectives.ParamMagnet` or use Scala 2.11.x. - * - * @group param - */ - def parameter(pdm: ParamMagnet): pdm.Out = pdm() - - /** - * Extracts a number of query parameter values from the request. - * Rejects the request if the defined query parameter matcher(s) don't match. - * - * Due to a bug in Scala 2.10, invocations of this method sometimes fail to compile with an - * "too many arguments for method parameters" or "type mismatch" error. - * - * As a workaround add an `import ParameterDirectives.ParamMagnet` or use Scala 2.11.x. - * - * @group param - */ - def parameters(pdm: ParamMagnet): pdm.Out = pdm() - -} - -object ParameterDirectives extends ParameterDirectives { - import BasicDirectives._ - - private val _parameterMap: Directive1[Map[String, String]] = - extract(_.request.uri.query().toMap) - - private val _parameterMultiMap: Directive1[Map[String, List[String]]] = - extract(_.request.uri.query().toMultiMap) - - private val _parameterSeq: Directive1[immutable.Seq[(String, String)]] = - extract(_.request.uri.query().toSeq) - - sealed trait ParamMagnet { - type Out - def apply(): Out - } - object ParamMagnet { - implicit def apply[T](value: T)(implicit pdef: ParamDef[T]): ParamMagnet { type Out = pdef.Out } = - new ParamMagnet { - type Out = pdef.Out - def apply() = pdef(value) - } - } - - type ParamDefAux[T, U] = ParamDef[T] { type Out = U } - sealed trait ParamDef[T] { - type Out - def apply(value: T): Out - } - object ParamDef { - def paramDef[A, B](f: A ⇒ B): ParamDefAux[A, B] = - new ParamDef[A] { - type Out = B - def apply(value: A) = f(value) - } - - import akka.http.scaladsl.unmarshalling.{ FromStringUnmarshaller ⇒ FSU, _ } - import BasicDirectives._ - import RouteDirectives._ - import FutureDirectives._ - type FSOU[T] = Unmarshaller[Option[String], T] - - private def extractParameter[A, B](f: A ⇒ Directive1[B]): ParamDefAux[A, Directive1[B]] = paramDef(f) - private def handleParamResult[T](paramName: String, result: Future[T])(implicit ec: ExecutionContext): Directive1[T] = - onComplete(result).flatMap { - case Success(x) ⇒ provide(x) - case Failure(Unmarshaller.NoContentException) ⇒ reject(MissingQueryParamRejection(paramName)) - case Failure(x) ⇒ reject(MalformedQueryParamRejection(paramName, x.getMessage.nullAsEmpty, Option(x.getCause))) - } - - //////////////////// "regular" parameter extraction ////////////////////// - - private def filter[T](paramName: String, fsou: FSOU[T]): Directive1[T] = - extractRequestContext flatMap { ctx ⇒ - import ctx.executionContext - import ctx.materializer - handleParamResult(paramName, fsou(ctx.request.uri.query().get(paramName))) - } - implicit def forString(implicit fsu: FSU[String]): ParamDefAux[String, Directive1[String]] = - extractParameter[String, String] { string ⇒ filter(string, fsu) } - implicit def forSymbol(implicit fsu: FSU[String]): ParamDefAux[Symbol, Directive1[String]] = - extractParameter[Symbol, String] { symbol ⇒ filter(symbol.name, fsu) } - implicit def forNR[T](implicit fsu: FSU[T]): ParamDefAux[NameReceptacle[T], Directive1[T]] = - extractParameter[NameReceptacle[T], T] { nr ⇒ filter(nr.name, fsu) } - implicit def forNUR[T]: ParamDefAux[NameUnmarshallerReceptacle[T], Directive1[T]] = - extractParameter[NameUnmarshallerReceptacle[T], T] { nr ⇒ filter(nr.name, nr.um) } - implicit def forNOR[T](implicit fsou: FSOU[T]): ParamDefAux[NameOptionReceptacle[T], Directive1[Option[T]]] = - extractParameter[NameOptionReceptacle[T], Option[T]] { nr ⇒ filter[Option[T]](nr.name, fsou) } - implicit def forNDR[T](implicit fsou: FSOU[T]): ParamDefAux[NameDefaultReceptacle[T], Directive1[T]] = - extractParameter[NameDefaultReceptacle[T], T] { nr ⇒ filter[T](nr.name, fsou withDefaultValue nr.default) } - implicit def forNOUR[T]: ParamDefAux[NameOptionUnmarshallerReceptacle[T], Directive1[Option[T]]] = - extractParameter[NameOptionUnmarshallerReceptacle[T], Option[T]] { nr ⇒ filter(nr.name, nr.um: FSOU[T]) } - implicit def forNDUR[T]: ParamDefAux[NameDefaultUnmarshallerReceptacle[T], Directive1[T]] = - extractParameter[NameDefaultUnmarshallerReceptacle[T], T] { nr ⇒ filter[T](nr.name, (nr.um: FSOU[T]) withDefaultValue nr.default) } - - //////////////////// required parameter support //////////////////// - - private def requiredFilter[T](paramName: String, fsou: FSOU[T], requiredValue: Any): Directive0 = - extractRequestContext flatMap { ctx ⇒ - import ctx.executionContext - import ctx.materializer - onComplete(fsou(ctx.request.uri.query().get(paramName))) flatMap { - case Success(value) if value == requiredValue ⇒ pass - case _ ⇒ reject - } - } - implicit def forRVR[T](implicit fsu: FSU[T]): ParamDefAux[RequiredValueReceptacle[T], Directive0] = - paramDef[RequiredValueReceptacle[T], Directive0] { rvr ⇒ requiredFilter(rvr.name, fsu, rvr.requiredValue) } - implicit def forRVDR[T]: ParamDefAux[RequiredValueUnmarshallerReceptacle[T], Directive0] = - paramDef[RequiredValueUnmarshallerReceptacle[T], Directive0] { rvr ⇒ requiredFilter(rvr.name, rvr.um, rvr.requiredValue) } - - //////////////////// repeated parameter support //////////////////// - - private def repeatedFilter[T](paramName: String, fsu: FSU[T]): Directive1[Iterable[T]] = - extractRequestContext flatMap { ctx ⇒ - import ctx.executionContext - import ctx.materializer - handleParamResult(paramName, Future.sequence(ctx.request.uri.query().getAll(paramName).map(fsu.apply))) - } - implicit def forRepVR[T](implicit fsu: FSU[T]): ParamDefAux[RepeatedValueReceptacle[T], Directive1[Iterable[T]]] = - extractParameter[RepeatedValueReceptacle[T], Iterable[T]] { rvr ⇒ repeatedFilter(rvr.name, fsu) } - implicit def forRepVDR[T]: ParamDefAux[RepeatedValueUnmarshallerReceptacle[T], Directive1[Iterable[T]]] = - extractParameter[RepeatedValueUnmarshallerReceptacle[T], Iterable[T]] { rvr ⇒ repeatedFilter(rvr.name, rvr.um) } - - //////////////////// tuple support //////////////////// - - import akka.http.scaladsl.server.util.TupleOps._ - import akka.http.scaladsl.server.util.BinaryPolyFunc - - implicit def forTuple[T](implicit fold: FoldLeft[Directive0, T, ConvertParamDefAndConcatenate.type]): ParamDefAux[T, fold.Out] = - paramDef[T, fold.Out](fold(BasicDirectives.pass, _)) - - object ConvertParamDefAndConcatenate extends BinaryPolyFunc { - implicit def from[P, TA, TB](implicit pdef: ParamDef[P] { type Out = Directive[TB] }, ev: Join[TA, TB]): BinaryPolyFunc.Case[Directive[TA], P, ConvertParamDefAndConcatenate.type] { type Out = Directive[ev.Out] } = - at[Directive[TA], P] { (a, t) ⇒ a & pdef(t) } - } - } -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/PathDirectives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/PathDirectives.scala deleted file mode 100644 index b566a15702..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/PathDirectives.scala +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import akka.http.scaladsl.common.ToNameReceptacleEnhancements -import akka.http.scaladsl.model.StatusCodes -import akka.http.scaladsl.model.Uri.Path - -/** - * @groupname path Path directives - * @groupprio path 170 - */ -trait PathDirectives extends PathMatchers with ImplicitPathMatcherConstruction with ToNameReceptacleEnhancements { - import BasicDirectives._ - import RouteDirectives._ - import PathMatcher._ - - /** - * Applies the given [[PathMatcher]] to the remaining unmatched path after consuming a leading slash. - * The matcher has to match the remaining path completely. - * If matched the value extracted by the [[PathMatcher]] is extracted on the directive level. - * - * @group path - */ - def path[L](pm: PathMatcher[L]): Directive[L] = pathPrefix(pm ~ PathEnd) - - /** - * Applies the given [[PathMatcher]] to a prefix of the remaining unmatched path after consuming a leading slash. - * The matcher has to match a prefix of the remaining path. - * If matched the value extracted by the PathMatcher is extracted on the directive level. - * - * @group path - */ - def pathPrefix[L](pm: PathMatcher[L]): Directive[L] = rawPathPrefix(Slash ~ pm) - - /** - * Applies the given matcher directly to a prefix of the unmatched path of the - * [[RequestContext]] (i.e. without implicitly consuming a leading slash). - * The matcher has to match a prefix of the remaining path. - * If matched the value extracted by the PathMatcher is extracted on the directive level. - * - * @group path - */ - def rawPathPrefix[L](pm: PathMatcher[L]): Directive[L] = { - implicit val LIsTuple = pm.ev - extract(ctx ⇒ pm(ctx.unmatchedPath)).flatMap { - case Matched(rest, values) ⇒ tprovide(values) & mapRequestContext(_ withUnmatchedPath rest) - case Unmatched ⇒ reject - } - } - - /** - * Checks whether the unmatchedPath of the [[RequestContext]] has a prefix matched by the - * given PathMatcher. In analogy to the `pathPrefix` directive a leading slash is implied. - * - * @group path - */ - def pathPrefixTest[L](pm: PathMatcher[L]): Directive[L] = rawPathPrefixTest(Slash ~ pm) - - /** - * Checks whether the unmatchedPath of the [[RequestContext]] has a prefix matched by the - * given PathMatcher. However, as opposed to the `pathPrefix` directive the matched path is not - * actually "consumed". - * - * @group path - */ - def rawPathPrefixTest[L](pm: PathMatcher[L]): Directive[L] = { - implicit val LIsTuple = pm.ev - extract(ctx ⇒ pm(ctx.unmatchedPath)).flatMap { - case Matched(_, values) ⇒ tprovide(values) - case Unmatched ⇒ reject - } - } - - /** - * Applies the given [[PathMatcher]] to a suffix of the remaining unmatchedPath of the [[RequestContext]]. - * If matched the value extracted by the [[PathMatcher]] is extracted and the matched parts of the path are consumed. - * Note that, for efficiency reasons, the given [[PathMatcher]] must match the desired suffix in reversed-segment - * order, i.e. `pathSuffix("baz" / "bar")` would match `/foo/bar/baz`! - * - * @group path - */ - def pathSuffix[L](pm: PathMatcher[L]): Directive[L] = { - implicit val LIsTuple = pm.ev - extract(ctx ⇒ pm(ctx.unmatchedPath.reverse)).flatMap { - case Matched(rest, values) ⇒ tprovide(values) & mapRequestContext(_.withUnmatchedPath(rest.reverse)) - case Unmatched ⇒ reject - } - } - - /** - * Checks whether the unmatchedPath of the [[RequestContext]] has a suffix matched by the - * given PathMatcher. However, as opposed to the pathSuffix directive the matched path is not - * actually "consumed". - * Note that, for efficiency reasons, the given PathMatcher must match the desired suffix in reversed-segment - * order, i.e. `pathSuffixTest("baz" / "bar")` would match `/foo/bar/baz`! - * - * @group path - */ - def pathSuffixTest[L](pm: PathMatcher[L]): Directive[L] = { - implicit val LIsTuple = pm.ev - extract(ctx ⇒ pm(ctx.unmatchedPath.reverse)).flatMap { - case Matched(_, values) ⇒ tprovide(values) - case Unmatched ⇒ reject - } - } - - /** - * Rejects the request if the unmatchedPath of the [[RequestContext]] is non-empty, - * or said differently: only passes on the request to its inner route if the request path - * has been matched completely. - * - * @group path - */ - def pathEnd: Directive0 = rawPathPrefix(PathEnd) - - /** - * 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. - * - * Note that trailing slash and non-trailing slash URLs are '''not''' the same, although they often serve - * the same content. It is recommended to serve only one URL version and make the other redirect to it using - * [[redirectToTrailingSlashIfMissing]] or [[redirectToNoTrailingSlashIfPresent]] directive. - * - * For example: - * {{{ - * def route = { - * // redirect '/users/' to '/users', '/users/:userId/' to '/users/:userId' - * redirectToNoTrailingSlashIfPresent(Found) { - * pathPrefix("users") { - * pathEnd { - * // user list ... - * } ~ - * path(UUID) { userId => - * // user profile ... - * } - * } - * } - * } - * }}} - * - * For further information, refer to: - * @see [[http://googlewebmastercentral.blogspot.de/2010/04/to-slash-or-not-to-slash.html]] - * - * @group path - */ - def pathEndOrSingleSlash: Directive0 = rawPathPrefix(Slash.? ~ PathEnd) - - /** - * Only passes on the request to its inner route if the request path - * consists of exactly one remaining slash. - * - * @group path - */ - def pathSingleSlash: Directive0 = pathPrefix(PathEnd) - - /** - * If the request path doesn't end with a slash, redirect to the same uri with trailing slash in the path. - * - * '''Caveat''': [[path]] without trailing slash and [[pathEnd]] directives will not match inside of this directive. - * - * @group path - */ - def redirectToTrailingSlashIfMissing(redirectionType: StatusCodes.Redirection): Directive0 = - extractUri.flatMap { uri ⇒ - if (uri.path.endsWithSlash) pass - else { - val newPath = uri.path ++ Path.SingleSlash - val newUri = uri.withPath(newPath) - redirect(newUri, redirectionType) - } - } - - /** - * If the request path ends with a slash, redirect to the same uri without trailing slash in the path. - * - * '''Caveat''': [[pathSingleSlash]] directive will not match inside of this directive. - * - * @group path - */ - def redirectToNoTrailingSlashIfPresent(redirectionType: StatusCodes.Redirection): Directive0 = - extractUri.flatMap { uri ⇒ - if (uri.path.endsWithSlash) { - val newPath = uri.path.reverse.tail.reverse - val newUri = uri.withPath(newPath) - redirect(newUri, redirectionType) - } else pass - } - -} - -object PathDirectives extends PathDirectives diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/RangeDirectives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/RangeDirectives.scala deleted file mode 100644 index bfc841750f..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/RangeDirectives.scala +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import akka.http.scaladsl.model.StatusCodes._ -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers._ -import akka.http.scaladsl.server.RouteResult.Complete -import akka.http.impl.util._ -import akka.stream.scaladsl._ -import scala.collection.immutable -import akka.util.ByteString -import akka.stream.SourceShape -import akka.stream.OverflowStrategy - -/** - * @groupname range Range directives - * @groupprio range 180 - */ -trait RangeDirectives { - import akka.http.scaladsl.server.directives.BasicDirectives._ - import akka.http.scaladsl.server.directives.RouteDirectives._ - - /** - * Answers GET requests with an `Accept-Ranges: bytes` header and converts HttpResponses coming back from its inner - * route into partial responses if the initial request contained a valid `Range` request header. The requested - * byte-ranges may be coalesced. - * This directive is transparent to non-GET requests - * Rejects requests with unsatisfiable ranges `UnsatisfiableRangeRejection`. - * Rejects requests with too many expected ranges. - * - * Note: if you want to combine this directive with `conditional(...)` you need to put - * it on the *inside* of the `conditional(...)` directive, i.e. `conditional(...)` must be - * on a higher level in your route structure in order to function correctly. - * - * @see [[https://tools.ietf.org/html/rfc7233]] - * - * @group range - */ - def withRangeSupport: Directive0 = - extractRequestContext.flatMap { ctx ⇒ - val settings = ctx.settings - implicit val log = ctx.log - import settings.{ rangeCountLimit, rangeCoalescingThreshold } - - class IndexRange(val start: Long, val end: Long) { - def length = end - start - def apply(entity: UniversalEntity): UniversalEntity = entity.transformDataBytes(length, StreamUtils.sliceBytesTransformer(start, length)) - def distance(other: IndexRange) = mergedEnd(other) - mergedStart(other) - (length + other.length) - def mergeWith(other: IndexRange) = new IndexRange(mergedStart(other), mergedEnd(other)) - def contentRange(entityLength: Long) = ContentRange(start, end - 1, entityLength) - private def mergedStart(other: IndexRange) = math.min(start, other.start) - private def mergedEnd(other: IndexRange) = math.max(end, other.end) - } - - def indexRange(entityLength: Long)(range: ByteRange): IndexRange = - range match { - case ByteRange.Slice(start, end) ⇒ new IndexRange(start, math.min(end + 1, entityLength)) - case ByteRange.FromOffset(first) ⇒ new IndexRange(first, entityLength) - case ByteRange.Suffix(suffixLength) ⇒ new IndexRange(math.max(0, entityLength - suffixLength), entityLength) - } - - // See comment of the `range-coalescing-threshold` setting in `reference.conf` for the rationale of this behavior. - def coalesceRanges(iRanges: Seq[IndexRange]): Seq[IndexRange] = - iRanges.foldLeft(Seq.empty[IndexRange]) { (acc, iRange) ⇒ - val (mergeCandidates, otherCandidates) = acc.partition(_.distance(iRange) <= rangeCoalescingThreshold) - val merged = mergeCandidates.foldLeft(iRange)(_ mergeWith _) - otherCandidates :+ merged - } - - def multipartRanges(ranges: Seq[ByteRange], entity: UniversalEntity): Multipart.ByteRanges = { - val length = entity.contentLength - val iRanges: Seq[IndexRange] = ranges.map(indexRange(length)) - - // It's only possible to run once over the input entity data stream because it's not known if the - // source is reusable. - // Therefore, ranges need to be sorted to prevent that some selected ranges already start to accumulate data - // but cannot be sent out because another range is blocking the queue. - val coalescedRanges = coalesceRanges(iRanges).sortBy(_.start) - val source = coalescedRanges.size match { - case 0 ⇒ Source.empty - case 1 ⇒ - val range = coalescedRanges.head - val flow = StreamUtils.sliceBytesTransformer(range.start, range.length) - val bytes = entity.dataBytes.via(flow) - val part = Multipart.ByteRanges.BodyPart(range.contentRange(length), HttpEntity(entity.contentType, range.length, bytes)) - Source.single(part) - case n ⇒ - Source fromGraph GraphDSL.create() { implicit b ⇒ - import GraphDSL.Implicits._ - val bcast = b.add(Broadcast[ByteString](n)) - val merge = b.add(Concat[Multipart.ByteRanges.BodyPart](n)) - for (range ← coalescedRanges) { - val flow = StreamUtils.sliceBytesTransformer(range.start, range.length) - bcast ~> flow.buffer(16, OverflowStrategy.backpressure).prefixAndTail(0).map { - case (_, bytes) ⇒ - Multipart.ByteRanges.BodyPart(range.contentRange(length), HttpEntity(entity.contentType, range.length, bytes)) - } ~> merge - } - entity.dataBytes ~> bcast - SourceShape(merge.out) - } - } - Multipart.ByteRanges(source) - } - - def rangeResponse(range: ByteRange, entity: UniversalEntity, length: Long, headers: immutable.Seq[HttpHeader]) = { - val aiRange = indexRange(length)(range) - HttpResponse(PartialContent, `Content-Range`(aiRange.contentRange(length)) +: headers, aiRange(entity)) - } - - def satisfiable(entityLength: Long)(range: ByteRange): Boolean = - range match { - case ByteRange.Slice(firstPos, _) ⇒ firstPos < entityLength - case ByteRange.FromOffset(firstPos) ⇒ firstPos < entityLength - case ByteRange.Suffix(length) ⇒ length > 0 - } - def universal(entity: HttpEntity): Option[UniversalEntity] = entity match { - case u: UniversalEntity ⇒ Some(u) - case _ ⇒ None - } - - def applyRanges(ranges: immutable.Seq[ByteRange]): Directive0 = - extractRequestContext.flatMap { ctx ⇒ - mapRouteResultWithPF { - case Complete(HttpResponse(OK, headers, entity, protocol)) ⇒ - universal(entity) match { - case Some(entity) ⇒ - val length = entity.contentLength - ranges.filter(satisfiable(length)) match { - case Nil ⇒ ctx.reject(UnsatisfiableRangeRejection(ranges, length)) - case Seq(satisfiableRange) ⇒ ctx.complete(rangeResponse(satisfiableRange, entity, length, headers)) - case satisfiableRanges ⇒ - ctx.complete((PartialContent, headers, multipartRanges(satisfiableRanges, entity))) - } - case None ⇒ - // Ranges not supported for Chunked or CloseDelimited responses - ctx.reject(UnsatisfiableRangeRejection(ranges, -1)) // FIXME: provide better error - } - } - } - - def rangeHeaderOfGetRequests(ctx: RequestContext): Option[Range] = - if (ctx.request.method == HttpMethods.GET) ctx.request.header[Range] else None - - extract(rangeHeaderOfGetRequests).flatMap { - case Some(Range(RangeUnits.Bytes, ranges)) ⇒ - if (ranges.size <= rangeCountLimit) applyRanges(ranges) & RangeDirectives.respondWithAcceptByteRangesHeader - else reject(TooManyRangesRejection(rangeCountLimit)) - case _ ⇒ MethodDirectives.get & RangeDirectives.respondWithAcceptByteRangesHeader | pass - } - } -} - -object RangeDirectives extends RangeDirectives { - private val respondWithAcceptByteRangesHeader: Directive0 = - RespondWithDirectives.respondWithHeader(`Accept-Ranges`(RangeUnits.Bytes)) -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/RespondWithDirectives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/RespondWithDirectives.scala deleted file mode 100644 index 3784cbd799..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/RespondWithDirectives.scala +++ /dev/null @@ -1,64 +0,0 @@ -package akka.http.scaladsl.server -package directives - -import akka.http.scaladsl.model._ -import scala.collection.immutable - -/** - * @groupname response Response directives - * @groupprio response 190 - */ -trait RespondWithDirectives { - import BasicDirectives._ - - /** - * Unconditionally adds the given response header to all HTTP responses of its inner Route. - * - * @group response - */ - def respondWithHeader(responseHeader: HttpHeader): Directive0 = respondWithHeaders(responseHeader) - - /** - * Adds the given response header to all HTTP responses of its inner Route, - * if the response from the inner Route doesn't already contain a header with the same name. - * - * @group response - */ - def respondWithDefaultHeader(responseHeader: HttpHeader): Directive0 = respondWithDefaultHeaders(responseHeader) - - /** - * Unconditionally adds the given response headers to all HTTP responses of its inner Route. - * - * @group response - */ - def respondWithHeaders(responseHeaders: HttpHeader*): Directive0 = - respondWithHeaders(responseHeaders.toList) - - /** - * Unconditionally adds the given response headers to all HTTP responses of its inner Route. - * - * @group response - */ - def respondWithHeaders(responseHeaders: immutable.Seq[HttpHeader]): Directive0 = - mapResponseHeaders(responseHeaders ++ _) - - /** - * Adds the given response headers to all HTTP responses of its inner Route, - * if a header already exists it is not added again. - * - * @group response - */ - def respondWithDefaultHeaders(responseHeaders: HttpHeader*): Directive0 = - respondWithDefaultHeaders(responseHeaders.toList) - - /** - * Adds the given response headers to all HTTP responses of its inner Route, - * if a header already exists it is not added again. - * - * @group response - */ - def respondWithDefaultHeaders(responseHeaders: immutable.Seq[HttpHeader]): Directive0 = - mapResponse(_.withDefaultHeaders(responseHeaders)) -} - -object RespondWithDirectives extends RespondWithDirectives diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/RouteDirectives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/RouteDirectives.scala deleted file mode 100644 index 4a6e2e0f7f..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/RouteDirectives.scala +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import akka.http.scaladsl.marshalling.ToResponseMarshallable -import akka.http.scaladsl.model._ -import StatusCodes._ - -/** - * @groupname route Route directives - * @groupprio route 200 - */ -trait RouteDirectives { - - /** - * Rejects the request with an empty set of rejections. - * - * @group route - */ - def reject: StandardRoute = RouteDirectives._reject - - /** - * Rejects the request with the given rejections. - * - * @group route - */ - def reject(rejections: Rejection*): StandardRoute = - StandardRoute(_.reject(rejections: _*)) - - /** - * Completes the request with redirection response of the given type to the given URI. - * - * @group route - */ - def redirect(uri: Uri, redirectionType: Redirection): StandardRoute = - StandardRoute(_.redirect(uri, redirectionType)) - - /** - * Completes the request using the given arguments. - * - * @group route - */ - def complete(m: ⇒ ToResponseMarshallable): StandardRoute = - StandardRoute(_.complete(m)) - - /** - * Bubbles the given error up the response chain, where it is dealt with by the closest `handleExceptions` - * directive and its ExceptionHandler. - * - * @group route - */ - def failWith(error: Throwable): StandardRoute = - StandardRoute(_.fail(error)) -} - -object RouteDirectives extends RouteDirectives { - private val _reject = StandardRoute(_.reject()) -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/SchemeDirectives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/SchemeDirectives.scala deleted file mode 100644 index e8f7df06a8..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/SchemeDirectives.scala +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -/** - * @groupname scheme Scheme directives - * @groupprio scheme 210 - */ -trait SchemeDirectives { - import BasicDirectives._ - - /** - * Extracts the Uri scheme from the request. - * - * @group scheme - */ - def extractScheme: Directive1[String] = SchemeDirectives._extractScheme - - /** - * Rejects all requests whose Uri scheme does not match the given one. - * - * @group scheme - */ - def scheme(name: String): Directive0 = - extractScheme.require(_ == name, SchemeRejection(name)) & cancelRejections(classOf[SchemeRejection]) -} - -object SchemeDirectives extends SchemeDirectives { - import BasicDirectives._ - - private val _extractScheme: Directive1[String] = extract(_.request.uri.scheme) -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/SecurityDirectives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/SecurityDirectives.scala deleted file mode 100644 index 5a6b8d6bac..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/SecurityDirectives.scala +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import scala.reflect.ClassTag -import scala.concurrent.Future -import akka.http.impl.util._ -import akka.http.scaladsl.util.FastFuture -import akka.http.scaladsl.util.FastFuture._ -import akka.http.scaladsl.model.headers._ -import akka.http.scaladsl.server.AuthenticationFailedRejection.{ CredentialsRejected, CredentialsMissing } - -import scala.util.Success - -/** - * Provides directives for securing an inner route using the standard Http authentication headers [[`WWW-Authenticate`]] - * and [[Authorization]]. Most prominently, HTTP Basic authentication and OAuth 2.0 Authorization Framework - * as defined in RFC 2617 and RFC 6750 respectively. - * - * See: RFC 2617. - * See: RFC 6750. - * - * @groupname security Security directives - * @groupprio security 220 - */ -trait SecurityDirectives { - import BasicDirectives._ - import HeaderDirectives._ - import FutureDirectives._ - import RouteDirectives._ - - //#authentication-result - /** - * The result of an HTTP authentication attempt is either the user object or - * an HttpChallenge to present to the browser. - * - * @group security - */ - type AuthenticationResult[+T] = Either[HttpChallenge, T] - //#authentication-result - - //#authenticator - /** - * @group security - */ - type Authenticator[T] = Credentials ⇒ Option[T] - //#authenticator - //#async-authenticator - /** - * @group security - */ - type AsyncAuthenticator[T] = Credentials ⇒ Future[Option[T]] - //#async-authenticator - //#authenticator-pf - /** - * @group security - */ - type AuthenticatorPF[T] = PartialFunction[Credentials, T] - //#authenticator-pf - //#async-authenticator-pf - /** - * @group security - */ - type AsyncAuthenticatorPF[T] = PartialFunction[Credentials, Future[T]] - //#async-authenticator-pf - - /** - * Extracts the potentially present [[HttpCredentials]] provided with the request's [[Authorization]] header. - * - * @group security - */ - def extractCredentials: Directive1[Option[HttpCredentials]] = - optionalHeaderValueByType[Authorization](()).map(_.map(_.credentials)) - - /** - * Wraps the inner route with Http Basic authentication support using a given `Authenticator[T]`. - * The given authenticator determines whether the credentials in the request are valid - * and, if so, which user object to supply to the inner route. - * - * @group security - */ - def authenticateBasic[T](realm: String, authenticator: Authenticator[T]): AuthenticationDirective[T] = - authenticateBasicAsync(realm, cred ⇒ FastFuture.successful(authenticator(cred))) - - /** - * Wraps the inner route with Http Basic authentication support. - * The given authenticator determines whether the credentials in the request are valid - * and, if so, which user object to supply to the inner route. - * - * @group security - */ - def authenticateBasicAsync[T](realm: String, authenticator: AsyncAuthenticator[T]): AuthenticationDirective[T] = - extractExecutionContext.flatMap { implicit ec ⇒ - authenticateOrRejectWithChallenge[BasicHttpCredentials, T] { cred ⇒ - authenticator(Credentials(cred)).fast.map { - case Some(t) ⇒ AuthenticationResult.success(t) - case None ⇒ AuthenticationResult.failWithChallenge(HttpChallenges.basic(realm)) - } - } - } - - /** - * A directive that wraps the inner route with Http Basic authentication support. - * The given authenticator determines whether the credentials in the request are valid - * and, if so, which user object to supply to the inner route. - * - * @group security - */ - def authenticateBasicPF[T](realm: String, authenticator: AuthenticatorPF[T]): AuthenticationDirective[T] = - authenticateBasic(realm, authenticator.lift) - - /** - * A directive that wraps the inner route with Http Basic authentication support. - * The given authenticator determines whether the credentials in the request are valid - * and, if so, which user object to supply to the inner route. - * - * @group security - */ - def authenticateBasicPFAsync[T](realm: String, authenticator: AsyncAuthenticatorPF[T]): AuthenticationDirective[T] = - extractExecutionContext.flatMap { implicit ec ⇒ - authenticateBasicAsync(realm, credentials ⇒ - if (authenticator isDefinedAt credentials) authenticator(credentials).fast.map(Some(_)) - else FastFuture.successful(None)) - } - - /** - * A directive that wraps the inner route with OAuth2 Bearer Token authentication support. - * The given authenticator determines whether the credentials in the request are valid - * and, if so, which user object to supply to the inner route. - * - * @group security - */ - def authenticateOAuth2[T](realm: String, authenticator: Authenticator[T]): AuthenticationDirective[T] = - authenticateOAuth2Async(realm, cred ⇒ FastFuture.successful(authenticator(cred))) - - /** - * A directive that wraps the inner route with OAuth2 Bearer Token authentication support. - * The given authenticator determines whether the credentials in the request are valid - * and, if so, which user object to supply to the inner route. - * - * @group security - */ - def authenticateOAuth2Async[T](realm: String, authenticator: AsyncAuthenticator[T]): AuthenticationDirective[T] = - extractExecutionContext.flatMap { implicit ec ⇒ - authenticateOrRejectWithChallenge[OAuth2BearerToken, T] { cred ⇒ - authenticator(Credentials(cred)).fast.map { - case Some(t) ⇒ AuthenticationResult.success(t) - case None ⇒ AuthenticationResult.failWithChallenge(HttpChallenges.oAuth2(realm)) - } - } - } - - /** - * A directive that wraps the inner route with OAuth2 Bearer Token authentication support. - * The given authenticator determines whether the credentials in the request are valid - * and, if so, which user object to supply to the inner route. - * - * @group security - */ - def authenticateOAuth2PF[T](realm: String, authenticator: AuthenticatorPF[T]): AuthenticationDirective[T] = - authenticateOAuth2(realm, authenticator.lift) - - /** - * A directive that wraps the inner route with OAuth2 Bearer Token authentication support. - * The given authenticator determines whether the credentials in the request are valid - * and, if so, which user object to supply to the inner route. - * - * @group security - */ - def authenticateOAuth2PFAsync[T](realm: String, authenticator: AsyncAuthenticatorPF[T]): AuthenticationDirective[T] = - extractExecutionContext.flatMap { implicit ec ⇒ - authenticateOAuth2Async(realm, credentials ⇒ - if (authenticator isDefinedAt credentials) authenticator(credentials).fast.map(Some(_)) - else FastFuture.successful(None)) - } - - /** - * Lifts an authenticator function into a directive. The authenticator function gets passed in credentials from the - * [[Authorization]] header of the request. If the function returns `Right(user)` the user object is provided - * to the inner route. If the function returns `Left(challenge)` the request is rejected with an - * [[AuthenticationFailedRejection]] that contains this challenge to be added to the response. - * - * @group security - */ - def authenticateOrRejectWithChallenge[T](authenticator: Option[HttpCredentials] ⇒ Future[AuthenticationResult[T]]): AuthenticationDirective[T] = - extractExecutionContext.flatMap { implicit ec ⇒ - extractCredentials.flatMap { cred ⇒ - onSuccess(authenticator(cred)).flatMap { - case Right(user) ⇒ provide(user) - case Left(challenge) ⇒ - val cause = if (cred.isEmpty) CredentialsMissing else CredentialsRejected - reject(AuthenticationFailedRejection(cause, challenge)): Directive1[T] - } - } - } - - /** - * Lifts an authenticator function into a directive. Same as `authenticateOrRejectWithChallenge` - * but only applies the authenticator function with a certain type of credentials. - * - * @group security - */ - def authenticateOrRejectWithChallenge[C <: HttpCredentials: ClassTag, T]( - authenticator: Option[C] ⇒ Future[AuthenticationResult[T]]): AuthenticationDirective[T] = - authenticateOrRejectWithChallenge[T](cred ⇒ authenticator(cred collect { case c: C ⇒ c })) - - /** - * Applies the given authorization check to the request. - * If the check fails the route is rejected with an [[AuthorizationFailedRejection]]. - * - * @group security - */ - def authorize(check: ⇒ Boolean): Directive0 = authorize(_ ⇒ check) - - /** - * Applies the given authorization check to the request. - * If the check fails the route is rejected with an [[AuthorizationFailedRejection]]. - * - * @group security - */ - def authorize(check: RequestContext ⇒ Boolean): Directive0 = - authorizeAsync(ctx ⇒ Future.successful(check(ctx))) - - /** - * Asynchronous version of [[authorize]]. - * If the [[Future]] fails or is completed with `false` - * authorization fails and the route is rejected with an [[AuthorizationFailedRejection]]. - * - * @group security - */ - def authorizeAsync(check: ⇒ Future[Boolean]): Directive0 = - authorizeAsync(ctx ⇒ check) - - /** - * Asynchronous version of [[authorize]]. - * If the [[Future]] fails or is completed with `false` - * authorization fails and the route is rejected with an [[AuthorizationFailedRejection]]. - * - * @group security - */ - def authorizeAsync(check: RequestContext ⇒ Future[Boolean]): Directive0 = - extractExecutionContext.flatMap { implicit ec ⇒ - extract(check).flatMap[Unit] { fa ⇒ - onComplete(fa).flatMap { - case Success(true) ⇒ pass - case _ ⇒ reject(AuthorizationFailedRejection) - } - } - } -} - -object SecurityDirectives extends SecurityDirectives - -/** - * Represents authentication credentials supplied with a request. Credentials can either be - * [[Credentials.Missing]] or can be [[Credentials.Provided]] in which case an identifier is - * supplied and a function to check the known secret against the provided one in a secure fashion. - */ -sealed trait Credentials -object Credentials { - case object Missing extends Credentials - abstract case class Provided(identifier: String) extends Credentials { - - /** - * First applies the passed in `hasher` function to the received secret part of the Credentials - * and then safely compares the passed in `secret` with the hashed received secret. - * This method can be used if the secret is not stored in plain text. - * Use of this method instead of manual String equality testing is recommended in order to guard against timing attacks. - * - * See also [[EnhancedString#secure_==]], for more information. - */ - def verify(secret: String, hasher: String ⇒ String): Boolean - - /** - * Safely compares the passed in `secret` with the received secret part of the Credentials. - * Use of this method instead of manual String equality testing is recommended in order to guard against timing attacks. - * - * See also [[EnhancedString#secure_==]], for more information. - */ - def verify(secret: String): Boolean = verify(secret, x ⇒ x) - } - - def apply(cred: Option[HttpCredentials]): Credentials = { - cred match { - case Some(BasicHttpCredentials(username, receivedSecret)) ⇒ - new Credentials.Provided(username) { - def verify(secret: String, hasher: String ⇒ String): Boolean = secret secure_== hasher(receivedSecret) - } - case Some(OAuth2BearerToken(token)) ⇒ - new Credentials.Provided(token) { - def verify(secret: String, hasher: String ⇒ String): Boolean = secret secure_== hasher(token) - } - case Some(GenericHttpCredentials(scheme, token, params)) ⇒ - throw new UnsupportedOperationException("cannot verify generic HTTP credentials") - case None ⇒ Credentials.Missing - } - } -} - -import SecurityDirectives._ - -object AuthenticationResult { - def success[T](user: T): AuthenticationResult[T] = Right(user) - def failWithChallenge(challenge: HttpChallenge): AuthenticationResult[Nothing] = Left(challenge) -} - -trait AuthenticationDirective[T] extends Directive1[T] { - import BasicDirectives._ - import RouteDirectives._ - - /** - * Returns a copy of this [[AuthenticationDirective]] that will provide `Some(user)` if credentials - * were supplied and otherwise `None`. - */ - def optional: Directive1[Option[T]] = - this.map(Some(_): Option[T]) recover { - case AuthenticationFailedRejection(CredentialsMissing, _) +: _ ⇒ provide(None) - case rejs ⇒ reject(rejs: _*) - } - - /** - * Returns a copy of this [[AuthenticationDirective]] that uses the given object as the - * anonymous user which will be used if no credentials were supplied in the request. - */ - def withAnonymousUser(anonymous: T): Directive1[T] = optional map (_ getOrElse anonymous) -} -object AuthenticationDirective { - implicit def apply[T](other: Directive1[T]): AuthenticationDirective[T] = - new AuthenticationDirective[T] { def tapply(inner: Tuple1[T] ⇒ Route) = other.tapply(inner) } -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/TimeoutDirectives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/TimeoutDirectives.scala deleted file mode 100644 index 835adafa9c..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/TimeoutDirectives.scala +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server.directives - -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers.`Timeout-Access` -import akka.http.scaladsl.server.{ Directive, Directive0 } - -import scala.concurrent.duration.Duration - -/** - * @groupname timeout Timeout directives - * @groupprio timeout 160 - */ -trait TimeoutDirectives { - - /** - * @group timeout - */ - def withoutRequestTimeout: Directive0 = - withRequestTimeout(Duration.Inf) - - /** - * Tries to set a new request timeout and handler (if provided) at the same time. - * - * Due to the inherent raciness it is not guaranteed that the update will be applied before - * the previously set timeout has expired! - * - * @group timeout - */ - def withRequestTimeout(timeout: Duration): Directive0 = - withRequestTimeout(timeout, None) - - /** - * Tries to set a new request timeout and handler (if provided) at the same time. - * - * Due to the inherent raciness it is not guaranteed that the update will be applied before - * the previously set timeout has expired! - * - * @param handler optional custom "timeout response" function. If left None, the default timeout HttpResponse will be used. - * - * @group timeout - */ - def withRequestTimeout(timeout: Duration, handler: HttpRequest ⇒ HttpResponse): Directive0 = - withRequestTimeout(timeout, Some(handler)) - - /** - * Tries to set a new request timeout and handler (if provided) at the same time. - * - * Due to the inherent raciness it is not guaranteed that the update will be applied before - * the previously set timeout has expired! - * - * @param handler optional custom "timeout response" function. If left None, the default timeout HttpResponse will be used. - * - * @group timeout - */ - def withRequestTimeout(timeout: Duration, handler: Option[HttpRequest ⇒ HttpResponse]): Directive0 = - Directive { inner ⇒ ctx ⇒ - ctx.request.header[`Timeout-Access`] match { - case Some(t) ⇒ - handler match { - case Some(h) ⇒ t.timeoutAccess.update(timeout, h) - case _ ⇒ t.timeoutAccess.updateTimeout(timeout) - } - case _ ⇒ ctx.log.warning("withRequestTimeout was used in route however no request-timeout is set!") - } - inner()(ctx) - } - - /** - * Tries to set a new request timeout handler, which produces the timeout response for a - * given request. Note that the handler must produce the response synchronously and shouldn't block! - * - * Due to the inherent raciness it is not guaranteed that the update will be applied before - * the previously set timeout has expired! - * - * @group timeout - */ - def withRequestTimeoutResponse(handler: HttpRequest ⇒ HttpResponse): Directive0 = - Directive { inner ⇒ ctx ⇒ - ctx.request.header[`Timeout-Access`] match { - case Some(t) ⇒ t.timeoutAccess.updateHandler(handler) - case _ ⇒ ctx.log.warning("withRequestTimeoutResponse was used in route however no request-timeout is set!") - } - inner()(ctx) - } - -} - -object TimeoutDirectives extends TimeoutDirectives diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/WebSocketDirectives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/WebSocketDirectives.scala deleted file mode 100644 index de382d8788..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/WebSocketDirectives.scala +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server -package directives - -import scala.collection.immutable - -import akka.http.scaladsl.model.ws.{ UpgradeToWebSocket, Message } -import akka.stream.scaladsl.Flow - -/** - * @groupname websocket WebSocket directives - * @groupprio websocket 230 - */ -trait WebSocketDirectives { - import BasicDirectives._ - import HeaderDirectives._ - import RouteDirectives._ - - /** - * Extract the [[UpgradeToWebSocket]] header if existent. Rejects with an [[ExpectedWebSocketRequestRejection]], otherwise. - * - * @group websocket - */ - def extractUpgradeToWebSocket: Directive1[UpgradeToWebSocket] = - optionalHeaderValueByType[UpgradeToWebSocket](()).flatMap { - case Some(upgrade) ⇒ provide(upgrade) - case None ⇒ reject(ExpectedWebSocketRequestRejection) - } - - /** - * Extract the list of WebSocket subprotocols as offered by the client in the [[Sec-WebSocket-Protocol]] header if - * this is a WebSocket request. Rejects with an [[ExpectedWebSocketRequestRejection]], otherwise. - * - * @group websocket - */ - def extractOfferedWsProtocols: Directive1[immutable.Seq[String]] = extractUpgradeToWebSocket.map(_.requestedProtocols) - - /** - * Handles WebSocket requests with the given handler and rejects other requests with an - * [[ExpectedWebSocketRequestRejection]]. - * - * @group websocket - */ - def handleWebSocketMessages(handler: Flow[Message, Message, Any]): Route = - handleWebSocketMessagesForOptionalProtocol(handler, None) - - /** - * Handles WebSocket requests with the given handler if the given subprotocol is offered in the request and - * rejects other requests with an [[ExpectedWebSocketRequestRejection]] or an [[UnsupportedWebSocketSubprotocolRejection]]. - * - * @group websocket - */ - def handleWebSocketMessagesForProtocol(handler: Flow[Message, Message, Any], subprotocol: String): Route = - handleWebSocketMessagesForOptionalProtocol(handler, Some(subprotocol)) - - /** - * Handles WebSocket requests with the given handler and rejects other requests with an - * [[ExpectedWebSocketRequestRejection]]. - * - * If the `subprotocol` parameter is None any WebSocket request is accepted. If the `subprotocol` parameter is - * `Some(protocol)` a WebSocket request is only accepted if the list of subprotocols supported by the client (as - * announced in the WebSocket request) contains `protocol`. If the client did not offer the protocol in question - * the request is rejected with an [[UnsupportedWebSocketSubprotocolRejection]] rejection. - * - * To support several subprotocols you may chain several `handleWebSocketMessage` Routes. - * - * @group websocket - */ - def handleWebSocketMessagesForOptionalProtocol(handler: Flow[Message, Message, Any], subprotocol: Option[String]): Route = - extractUpgradeToWebSocket { upgrade ⇒ - if (subprotocol.forall(sub ⇒ upgrade.requestedProtocols.exists(_ equalsIgnoreCase sub))) - complete(upgrade.handleMessages(handler, subprotocol)) - else - reject(UnsupportedWebSocketSubprotocolRejection(subprotocol.get)) // None.forall == true - } -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/package.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/package.scala deleted file mode 100644 index 2b82ed0dd9..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/package.scala +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl - -import scala.concurrent.Future - -package object server { - - type Route = RequestContext ⇒ Future[RouteResult] - - type RouteGenerator[T] = T ⇒ Route - type Directive0 = Directive[Unit] - type Directive1[T] = Directive[Tuple1[T]] - type PathMatcher0 = PathMatcher[Unit] - type PathMatcher1[T] = PathMatcher[Tuple1[T]] - - def FIXME = throw new RuntimeException("Not yet implemented") -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/util/ApplyConverter.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/util/ApplyConverter.scala deleted file mode 100644 index f580647c64..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/util/ApplyConverter.scala +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server.util - -import akka.http.scaladsl.server._ - -/** - * ApplyConverter allows generic conversion of functions of type `(T1, T2, ...) => Route` to - * `(TupleX(T1, T2, ...)) => Route`. - */ -abstract class ApplyConverter[L] { - type In - def apply(f: In): L ⇒ Route -} - -object ApplyConverter extends ApplyConverterInstances \ No newline at end of file diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/util/BinaryPolyFunc.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/util/BinaryPolyFunc.scala deleted file mode 100644 index 501f517d4a..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/util/BinaryPolyFunc.scala +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server.util - -/** - * Allows the definition of binary poly-functions (e.g. for folding over tuples). - * - * Note: the poly-function implementation seen here is merely a stripped down version of - * what Miles Sabin made available with his awesome shapeless library. All credit goes to him! - */ -trait BinaryPolyFunc { - def at[A, B] = new CaseBuilder[A, B] - class CaseBuilder[A, B] { - def apply[R](f: (A, B) ⇒ R) = new BinaryPolyFunc.Case[A, B, BinaryPolyFunc.this.type] { - type Out = R - def apply(a: A, b: B) = f(a, b) - } - } -} - -object BinaryPolyFunc { - sealed trait Case[A, B, Op] { - type Out - def apply(a: A, b: B): Out - } -} - diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/util/ClassMagnet.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/util/ClassMagnet.scala deleted file mode 100644 index 6be5c4d252..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/util/ClassMagnet.scala +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server.util - -import scala.reflect.ClassTag - -/** A magnet that wraps a ClassTag */ -trait ClassMagnet[T] { - def classTag: ClassTag[T] - def runtimeClass: Class[T] - - /** - * Returns a partial function that checks if the input value is of runtime type - * T and returns the value if it does. Doesn't take erased information into account. - */ - def extractPF: PartialFunction[Any, T] -} -object ClassMagnet { - implicit def fromClass[T](c: Class[T]): ClassMagnet[T] = ClassMagnet(ClassTag(c)) - implicit def fromUnit[T](u: Unit)(implicit tag: ClassTag[T]): ClassMagnet[T] = ClassMagnet(tag) - - def apply[T](implicit tag: ClassTag[T]): ClassMagnet[T] = - new ClassMagnet[T] { - val classTag: ClassTag[T] = tag - val runtimeClass: Class[T] = tag.runtimeClass.asInstanceOf[Class[T]] - val extractPF: PartialFunction[Any, T] = { - case x: T ⇒ x - } - } -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/util/ConstructFromTuple.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/util/ConstructFromTuple.scala deleted file mode 100644 index 8a454c2e79..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/util/ConstructFromTuple.scala +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server.util - -/** - * Constructor for instances of type `R` which can be created from a tuple of type `T`. - */ -trait ConstructFromTuple[T, R] extends (T ⇒ R) - -object ConstructFromTuple extends ConstructFromTupleInstances diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/util/Tuple.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/util/Tuple.scala deleted file mode 100644 index 167d9d277c..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/util/Tuple.scala +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server.util - -/** - * Phantom type providing implicit evidence that a given type is a Tuple or Unit. - */ -sealed trait Tuple[T] - -object Tuple { - /** - * Used to provide "is-Tuple" evidence where we know that a given value must be a tuple. - */ - def yes[T]: Tuple[T] = null - - implicit def forNothing[A]: Tuple[Nothing] = null - implicit def forUnit[A]: Tuple[Unit] = null - implicit def forTuple1[A]: Tuple[Tuple1[A]] = null - implicit def forTuple2[A, B]: Tuple[(A, B)] = null - implicit def forTuple3[A, B, C]: Tuple[(A, B, C)] = null - implicit def forTuple4[A, B, C, D]: Tuple[(A, B, C, D)] = null - implicit def forTuple5[A, B, C, D, E]: Tuple[(A, B, C, D, E)] = null - implicit def forTuple6[A, B, C, D, E, F]: Tuple[(A, B, C, D, E, F)] = null - implicit def forTuple7[A, B, C, D, E, F, G]: Tuple[(A, B, C, D, E, F, G)] = null - implicit def forTuple8[A, B, C, D, E, F, G, H]: Tuple[(A, B, C, D, E, F, G, H)] = null - implicit def forTuple9[A, B, C, D, E, F, G, H, I]: Tuple[(A, B, C, D, E, F, G, H, I)] = null - implicit def forTuple10[A, B, C, D, E, F, G, H, I, J]: Tuple[(A, B, C, D, E, F, G, H, I, J)] = null - implicit def forTuple11[A, B, C, D, E, F, G, H, I, J, K]: Tuple[(A, B, C, D, E, F, G, H, I, J, K)] = null - implicit def forTuple12[A, B, C, D, E, F, G, H, I, J, K, L]: Tuple[(A, B, C, D, E, F, G, H, I, J, K, L)] = null - implicit def forTuple13[A, B, C, D, E, F, G, H, I, J, K, L, M]: Tuple[(A, B, C, D, E, F, G, H, I, J, K, L, M)] = null - implicit def forTuple14[A, B, C, D, E, F, G, H, I, J, K, L, M, N]: Tuple[(A, B, C, D, E, F, G, H, I, J, K, L, M, N)] = null - implicit def forTuple15[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O]: Tuple[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O)] = null - implicit def forTuple16[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P]: Tuple[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P)] = null - implicit def forTuple17[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q]: Tuple[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q)] = null - implicit def forTuple18[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R]: Tuple[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R)] = null - implicit def forTuple19[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S]: Tuple[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S)] = null - implicit def forTuple20[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T]: Tuple[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T)] = null - implicit def forTuple21[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U]: Tuple[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U)] = null - implicit def forTuple22[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V]: Tuple[(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V)] = null -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/util/TupleOps.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/util/TupleOps.scala deleted file mode 100644 index 3068e0e66c..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/util/TupleOps.scala +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server.util - -class TupleOps[T](val tuple: T) extends AnyVal { - import TupleOps._ - - /** - * Appends the given value to the tuple producing a tuple of arity n + 1. - */ - def append[S](value: S)(implicit ao: AppendOne[T, S]): ao.Out = ao(tuple, value) - - /** - * Left-Folds over the tuple using the given binary poly-function. - */ - def foldLeft[In](zero: In)(op: BinaryPolyFunc)(implicit fold: FoldLeft[In, T, op.type]): fold.Out = fold(zero, tuple) - - /** - * Appends the given tuple to the underlying tuple producing a tuple of arity n + m. - */ - def join[S](suffixTuple: S)(implicit join: Join[T, S]): join.Out = join(tuple, suffixTuple) -} - -object TupleOps { - implicit def enhanceTuple[T: Tuple](tuple: T): TupleOps[T] = new TupleOps(tuple) - - trait AppendOne[P, S] { - type Out - def apply(prefix: P, last: S): Out - } - object AppendOne extends TupleAppendOneInstances - - trait FoldLeft[In, T, Op] { - type Out - def apply(zero: In, tuple: T): Out - } - object FoldLeft extends TupleFoldInstances - - trait Join[P, S] { - type Out - def apply(prefix: P, suffix: S): Out - } - type JoinAux[P, S, O] = Join[P, S] { type Out = O } - object Join extends LowLevelJoinImplicits { - // O(1) shortcut for the Join[Unit, T] case to avoid O(n) runtime in this case - implicit def join0P[T]: JoinAux[Unit, T, T] = - new Join[Unit, T] { - type Out = T - def apply(prefix: Unit, suffix: T): Out = suffix - } - // we implement the join by folding over the suffix with the prefix as growing accumulator - object Fold extends BinaryPolyFunc { - implicit def step[T, A](implicit append: AppendOne[T, A]): BinaryPolyFunc.Case[T, A, Fold.type] { type Out = append.Out } = - at[T, A](append(_, _)) - } - } - sealed abstract class LowLevelJoinImplicits { - implicit def join[P, S](implicit fold: FoldLeft[P, S, Join.Fold.type]): JoinAux[P, S, fold.Out] = - new Join[P, S] { - type Out = fold.Out - def apply(prefix: P, suffix: S): Out = fold(prefix, suffix) - } - } -} \ No newline at end of file diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/util/Tupler.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/util/Tupler.scala deleted file mode 100644 index 51e5e602ac..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/util/Tupler.scala +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.server.util - -/** - * Provides a way to convert a value into an Tuple. - * If the value is already a Tuple then it is returned unchanged, otherwise it's wrapped in a Tuple1 instance. - */ -trait Tupler[T] { - type Out - def OutIsTuple: Tuple[Out] - def apply(value: T): Out -} - -object Tupler extends LowerPriorityTupler { - implicit def forTuple[T: Tuple]: Tupler[T] { type Out = T } = - new Tupler[T] { - type Out = T - def OutIsTuple = implicitly[Tuple[Out]] - def apply(value: T) = value - } -} - -private[server] abstract class LowerPriorityTupler { - implicit def forAnyRef[T]: Tupler[T] { type Out = Tuple1[T] } = - new Tupler[T] { - type Out = Tuple1[T] - def OutIsTuple = implicitly[Tuple[Out]] - def apply(value: T) = Tuple1(value) - } -} \ No newline at end of file diff --git a/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/GenericUnmarshallers.scala b/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/GenericUnmarshallers.scala deleted file mode 100644 index 52e01f15ab..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/GenericUnmarshallers.scala +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.unmarshalling - -import akka.http.scaladsl.unmarshalling.Unmarshaller.EitherUnmarshallingException -import akka.http.scaladsl.util.FastFuture -import akka.stream.impl.ConstantFun - -import scala.concurrent.Future -import scala.reflect.ClassTag - -trait GenericUnmarshallers extends LowerPriorityGenericUnmarshallers { - - implicit def liftToTargetOptionUnmarshaller[A, B](um: Unmarshaller[A, B]): Unmarshaller[A, Option[B]] = - targetOptionUnmarshaller(um) - implicit def targetOptionUnmarshaller[A, B](implicit um: Unmarshaller[A, B]): Unmarshaller[A, Option[B]] = - um map (Some(_)) withDefaultValue None -} - -sealed trait LowerPriorityGenericUnmarshallers { - - implicit def messageUnmarshallerFromEntityUnmarshaller[T](implicit um: FromEntityUnmarshaller[T]): FromMessageUnmarshaller[T] = - Unmarshaller.withMaterializer { implicit ec ⇒ implicit mat ⇒ request ⇒ um(request.entity) } - - implicit def liftToSourceOptionUnmarshaller[A, B](um: Unmarshaller[A, B]): Unmarshaller[Option[A], B] = - sourceOptionUnmarshaller(um) - implicit def sourceOptionUnmarshaller[A, B](implicit um: Unmarshaller[A, B]): Unmarshaller[Option[A], B] = - Unmarshaller.withMaterializer(implicit ec ⇒ implicit mat ⇒ { - case Some(a) ⇒ um(a) - case None ⇒ FastFuture.failed(Unmarshaller.NoContentException) - }) - - /** - * Enables using [[Either]] to encode the following unmarshalling logic: - * Attempt unmarshalling the entity as as `R` first (yielding `R`), - * and if it fails attempt unmarshalling as `L` (yielding `Left`). - * - * Note that the Either's "R" type will be attempted first (as Left is often considered as the "failed case" in Either). - */ - // format: OFF - implicit def eitherUnmarshaller[L, R](implicit ua: FromEntityUnmarshaller[L], rightTag: ClassTag[R], - ub: FromEntityUnmarshaller[R], leftTag: ClassTag[L]): FromEntityUnmarshaller[Either[L, R]] = - Unmarshaller.withMaterializer { implicit ex ⇒ implicit mat ⇒ value ⇒ - import akka.http.scaladsl.util.FastFuture._ - @inline def right = ub(value).fast.map(Right(_)) - @inline def fallbackLeft: PartialFunction[Throwable, Future[Either[L, R]]] = { case rightFirstEx ⇒ - val left = ua(value).fast.map(Left(_)) - - // combine EitherUnmarshallingException by carring both exceptions - left.transform( - s = ConstantFun.scalaIdentityFunction, - f = leftSecondEx => new EitherUnmarshallingException( - rightClass = rightTag.runtimeClass, right = rightFirstEx, - leftClass = leftTag.runtimeClass, left = leftSecondEx) - ) - } - - right.recoverWith(fallbackLeft) - } - // format: ON - -} \ No newline at end of file diff --git a/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/MultipartUnmarshallers.scala b/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/MultipartUnmarshallers.scala deleted file mode 100644 index 380f50fdd6..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/MultipartUnmarshallers.scala +++ /dev/null @@ -1,119 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.unmarshalling - -import akka.http.scaladsl.settings.ParserSettings - -import scala.collection.immutable -import scala.collection.immutable.VectorBuilder -import akka.util.ByteString -import akka.event.{ LoggingAdapter, NoLogging } -import akka.stream.{ ActorMaterializer, ActorMaterializerHelper } -import akka.stream.impl.fusing.IteratorInterpreter -import akka.stream.scaladsl._ -import akka.http.impl.engine.parsing.BodyPartParser -import akka.http.scaladsl.model._ -import akka.http.scaladsl.util.FastFuture -import MediaRanges._ -import MediaTypes._ -import HttpCharsets._ -import akka.stream.impl.fusing.SubSource - -/** - * Provides [[akka.http.scaladsl.model.Multipart]] marshallers. - * It is possible to configure the default parsing mode by providing an implicit [[akka.http.scaladsl.settings.ParserSettings]] instance. - */ -trait MultipartUnmarshallers { - - implicit def defaultMultipartGeneralUnmarshaller(implicit log: LoggingAdapter = NoLogging, parserSettings: ParserSettings = null): FromEntityUnmarshaller[Multipart.General] = - multipartGeneralUnmarshaller(`UTF-8`) - - def multipartGeneralUnmarshaller(defaultCharset: HttpCharset)(implicit log: LoggingAdapter = NoLogging, parserSettings: ParserSettings = null): FromEntityUnmarshaller[Multipart.General] = - multipartUnmarshaller[Multipart.General, Multipart.General.BodyPart, Multipart.General.BodyPart.Strict]( - mediaRange = `multipart/*`, - defaultContentType = MediaTypes.`text/plain` withCharset defaultCharset, - createBodyPart = Multipart.General.BodyPart(_, _), - createStreamed = Multipart.General(_, _), - createStrictBodyPart = Multipart.General.BodyPart.Strict, - createStrict = Multipart.General.Strict) - - implicit def multipartFormDataUnmarshaller(implicit log: LoggingAdapter = NoLogging, parserSettings: ParserSettings = null): FromEntityUnmarshaller[Multipart.FormData] = - multipartUnmarshaller[Multipart.FormData, Multipart.FormData.BodyPart, Multipart.FormData.BodyPart.Strict]( - mediaRange = `multipart/form-data`, - defaultContentType = ContentTypes.`text/plain(UTF-8)`, - createBodyPart = (entity, headers) ⇒ Multipart.General.BodyPart(entity, headers).toFormDataBodyPart.get, - createStreamed = (_, parts) ⇒ Multipart.FormData(parts), - createStrictBodyPart = (entity, headers) ⇒ Multipart.General.BodyPart.Strict(entity, headers).toFormDataBodyPart.get, - createStrict = (_, parts) ⇒ Multipart.FormData.Strict(parts)) - - implicit def defaultMultipartByteRangesUnmarshaller(implicit log: LoggingAdapter = NoLogging, parserSettings: ParserSettings = null): FromEntityUnmarshaller[Multipart.ByteRanges] = - multipartByteRangesUnmarshaller(`UTF-8`) - - def multipartByteRangesUnmarshaller(defaultCharset: HttpCharset)(implicit log: LoggingAdapter = NoLogging, parserSettings: ParserSettings = null): FromEntityUnmarshaller[Multipart.ByteRanges] = - multipartUnmarshaller[Multipart.ByteRanges, Multipart.ByteRanges.BodyPart, Multipart.ByteRanges.BodyPart.Strict]( - mediaRange = `multipart/byteranges`, - defaultContentType = MediaTypes.`text/plain` withCharset defaultCharset, - createBodyPart = (entity, headers) ⇒ Multipart.General.BodyPart(entity, headers).toByteRangesBodyPart.get, - createStreamed = (_, parts) ⇒ Multipart.ByteRanges(parts), - createStrictBodyPart = (entity, headers) ⇒ Multipart.General.BodyPart.Strict(entity, headers).toByteRangesBodyPart.get, - createStrict = (_, parts) ⇒ Multipart.ByteRanges.Strict(parts)) - - def multipartUnmarshaller[T <: Multipart, BP <: Multipart.BodyPart, BPS <: Multipart.BodyPart.Strict]( - mediaRange: MediaRange, - defaultContentType: ContentType, - createBodyPart: (BodyPartEntity, List[HttpHeader]) ⇒ BP, - createStreamed: (MediaType.Multipart, Source[BP, Any]) ⇒ T, - createStrictBodyPart: (HttpEntity.Strict, List[HttpHeader]) ⇒ BPS, - createStrict: (MediaType.Multipart, immutable.Seq[BPS]) ⇒ T)(implicit log: LoggingAdapter = NoLogging, parserSettings: ParserSettings = null): FromEntityUnmarshaller[T] = - Unmarshaller.withMaterializer { implicit ec ⇒ mat ⇒ - entity ⇒ - if (entity.contentType.mediaType.isMultipart && mediaRange.matches(entity.contentType.mediaType)) { - entity.contentType.mediaType.params.get("boundary") match { - case None ⇒ - FastFuture.failed(new RuntimeException("Content-Type with a multipart media type must have a 'boundary' parameter")) - case Some(boundary) ⇒ - import BodyPartParser._ - val effectiveParserSettings = Option(parserSettings).getOrElse(ParserSettings(ActorMaterializerHelper.downcast(mat).system)) - val parser = new BodyPartParser(defaultContentType, boundary, log, effectiveParserSettings) - FastFuture.successful { - entity match { - case HttpEntity.Strict(ContentType(mediaType: MediaType.Multipart, _), data) ⇒ - val builder = new VectorBuilder[BPS]() - val iter = new IteratorInterpreter[ByteString, BodyPartParser.Output]( - Iterator.single(data), List(parser)).iterator - // note that iter.next() will throw exception if stream fails - iter.foreach { - case BodyPartStart(headers, createEntity) ⇒ - val entity = createEntity(Source.empty) match { - case x: HttpEntity.Strict ⇒ x - case x ⇒ throw new IllegalStateException("Unexpected entity type from strict BodyPartParser: " + x) - } - builder += createStrictBodyPart(entity, headers) - case ParseError(errorInfo) ⇒ throw ParsingException(errorInfo) - case x ⇒ throw new IllegalStateException(s"Unexpected BodyPartParser result $x in strict case") - } - createStrict(mediaType, builder.result()) - case _ ⇒ - val bodyParts = entity.dataBytes - .via(parser) - .splitWhen(_.isInstanceOf[PartStart]) - .prefixAndTail(1) - .collect { - case (Seq(BodyPartStart(headers, createEntity)), entityParts) ⇒ - createBodyPart(createEntity(entityParts), headers) - case (Seq(ParseError(errorInfo)), rest) ⇒ - SubSource.kill(rest) - throw ParsingException(errorInfo) - } - .concatSubstreams - createStreamed(entity.contentType.mediaType.asInstanceOf[MediaType.Multipart], bodyParts) - } - } - } - } else FastFuture.failed(Unmarshaller.UnsupportedContentTypeException(mediaRange)) - } -} - -object MultipartUnmarshallers extends MultipartUnmarshallers diff --git a/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/PredefinedFromEntityUnmarshallers.scala b/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/PredefinedFromEntityUnmarshallers.scala deleted file mode 100644 index 43f4cbd420..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/PredefinedFromEntityUnmarshallers.scala +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.unmarshalling - -import akka.util.ByteString -import akka.http.scaladsl.util.FastFuture -import akka.http.scaladsl.model._ - -import scala.concurrent.Future - -trait PredefinedFromEntityUnmarshallers extends MultipartUnmarshallers { - - implicit def byteStringUnmarshaller: FromEntityUnmarshaller[ByteString] = - Unmarshaller.withMaterializer(_ ⇒ implicit mat ⇒ { - case HttpEntity.Strict(_, data) ⇒ FastFuture.successful(data) - case entity ⇒ entity.dataBytes.runFold(ByteString.empty)(_ ++ _) - }) - - implicit def byteArrayUnmarshaller: FromEntityUnmarshaller[Array[Byte]] = - byteStringUnmarshaller.map(_.toArray[Byte]) - - implicit def charArrayUnmarshaller: FromEntityUnmarshaller[Array[Char]] = - byteStringUnmarshaller mapWithInput { (entity, bytes) ⇒ - if (entity.isKnownEmpty) Array.emptyCharArray - else { - val charBuffer = Unmarshaller.bestUnmarshallingCharsetFor(entity).nioCharset.decode(bytes.asByteBuffer) - val array = new Array[Char](charBuffer.length()) - charBuffer.get(array) - array - } - } - - implicit def stringUnmarshaller: FromEntityUnmarshaller[String] = - byteStringUnmarshaller mapWithInput { (entity, bytes) ⇒ - if (entity.isKnownEmpty) "" - else bytes.decodeString(Unmarshaller.bestUnmarshallingCharsetFor(entity).nioCharset) - } - - implicit def defaultUrlEncodedFormDataUnmarshaller: FromEntityUnmarshaller[FormData] = - urlEncodedFormDataUnmarshaller(MediaTypes.`application/x-www-form-urlencoded`) - def urlEncodedFormDataUnmarshaller(ranges: ContentTypeRange*): FromEntityUnmarshaller[FormData] = - stringUnmarshaller.forContentTypes(ranges: _*).mapWithInput { (entity, string) ⇒ - if (entity.isKnownEmpty) FormData.Empty - else { - try FormData(Uri.Query(string, Unmarshaller.bestUnmarshallingCharsetFor(entity).nioCharset)) - catch { - case IllegalUriException(info) ⇒ - throw new IllegalArgumentException(info.formatPretty.replace("Query,", "form content,")) - } - } - } -} - -object PredefinedFromEntityUnmarshallers extends PredefinedFromEntityUnmarshallers diff --git a/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/PredefinedFromStringUnmarshallers.scala b/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/PredefinedFromStringUnmarshallers.scala deleted file mode 100755 index 9f44f67762..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/PredefinedFromStringUnmarshallers.scala +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.unmarshalling - -import scala.collection.immutable -import akka.http.scaladsl.util.FastFuture -import akka.util.ByteString - -trait PredefinedFromStringUnmarshallers { - - implicit def _fromStringUnmarshallerFromByteStringUnmarshaller[T](implicit bsum: FromByteStringUnmarshaller[T]): Unmarshaller[String, T] = { - val bs = Unmarshaller.strict[String, ByteString](s ⇒ ByteString(s)) - bs.flatMap(implicit ec ⇒ implicit mat ⇒ bsum(_)) - } - - implicit val byteFromStringUnmarshaller: Unmarshaller[String, Byte] = - numberUnmarshaller(_.toByte, "8-bit signed integer") - - implicit val shortFromStringUnmarshaller: Unmarshaller[String, Short] = - numberUnmarshaller(_.toShort, "16-bit signed integer") - - implicit val intFromStringUnmarshaller: Unmarshaller[String, Int] = - numberUnmarshaller(_.toInt, "32-bit signed integer") - - implicit val longFromStringUnmarshaller: Unmarshaller[String, Long] = - numberUnmarshaller(_.toLong, "64-bit signed integer") - - implicit val floatFromStringUnmarshaller: Unmarshaller[String, Float] = - numberUnmarshaller(_.toFloat, "32-bit floating point") - - implicit val doubleFromStringUnmarshaller: Unmarshaller[String, Double] = - numberUnmarshaller(_.toDouble, "64-bit floating point") - - implicit val booleanFromStringUnmarshaller: Unmarshaller[String, Boolean] = - Unmarshaller.strict[String, Boolean] { string ⇒ - string.toLowerCase match { - case "true" | "yes" | "on" | "1" ⇒ true - case "false" | "no" | "off" | "0" ⇒ false - case "" ⇒ throw Unmarshaller.NoContentException - case x ⇒ throw new IllegalArgumentException(s"'$x' is not a valid Boolean value") - } - } - - implicit def CsvSeq[T](implicit unmarshaller: Unmarshaller[String, T]): Unmarshaller[String, immutable.Seq[T]] = - Unmarshaller.strict[String, immutable.Seq[String]] { string ⇒ - string.split(",").toList - } flatMap { implicit ec ⇒ implicit mat ⇒ strings ⇒ - FastFuture.sequence(strings.map(unmarshaller(_))) - } - - val HexByte: Unmarshaller[String, Byte] = - numberUnmarshaller(java.lang.Byte.parseByte(_, 16), "8-bit hexadecimal integer") - - val HexShort: Unmarshaller[String, Short] = - numberUnmarshaller(java.lang.Short.parseShort(_, 16), "16-bit hexadecimal integer") - - val HexInt: Unmarshaller[String, Int] = - numberUnmarshaller(java.lang.Integer.parseInt(_, 16), "32-bit hexadecimal integer") - - val HexLong: Unmarshaller[String, Long] = - numberUnmarshaller(java.lang.Long.parseLong(_, 16), "64-bit hexadecimal integer") - - private def numberUnmarshaller[T](f: String ⇒ T, target: String): Unmarshaller[String, T] = - Unmarshaller.strict[String, T] { string ⇒ - try f(string) - catch numberFormatError(string, target) - } - - private def numberFormatError(value: String, target: String): PartialFunction[Throwable, Nothing] = { - case e: NumberFormatException ⇒ - throw if (value.isEmpty) Unmarshaller.NoContentException else new IllegalArgumentException(s"'$value' is not a valid $target value", e) - } -} - -object PredefinedFromStringUnmarshallers extends PredefinedFromStringUnmarshallers diff --git a/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/Unmarshal.scala b/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/Unmarshal.scala deleted file mode 100644 index 9fb8ed1f71..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/Unmarshal.scala +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.unmarshalling - -import akka.stream.Materializer - -import scala.concurrent.{ ExecutionContext, Future } - -object Unmarshal { - def apply[T](value: T): Unmarshal[T] = new Unmarshal(value) -} - -class Unmarshal[A](val value: A) { - /** - * Unmarshals the value to the given Type using the in-scope Unmarshaller. - */ - def to[B](implicit um: Unmarshaller[A, B], ec: ExecutionContext, mat: Materializer): Future[B] = um(value) -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/Unmarshaller.scala b/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/Unmarshaller.scala deleted file mode 100644 index ae372b3162..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/Unmarshaller.scala +++ /dev/null @@ -1,157 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl.unmarshalling - -import akka.event.Logging -import akka.stream.Materializer - -import scala.util.control.{ NoStackTrace, NonFatal } -import scala.concurrent.{ Future, ExecutionContext } -import akka.http.scaladsl.util.FastFuture -import akka.http.scaladsl.util.FastFuture._ -import akka.http.scaladsl.model._ - -trait Unmarshaller[-A, B] extends akka.http.javadsl.unmarshalling.Unmarshaller[A, B] { - - implicit final def asScala: Unmarshaller[A, B] = this - - def apply(value: A)(implicit ec: ExecutionContext, materializer: Materializer): Future[B] - - def transform[C](f: ExecutionContext ⇒ Materializer ⇒ Future[B] ⇒ Future[C]): Unmarshaller[A, C] = - Unmarshaller.withMaterializer { implicit ec ⇒ implicit mat ⇒ a ⇒ f(ec)(mat)(this(a)) } - - def map[C](f: B ⇒ C): Unmarshaller[A, C] = - transform(implicit ec ⇒ _ ⇒ _.fast map f) - - def flatMap[C](f: ExecutionContext ⇒ Materializer ⇒ B ⇒ Future[C]): Unmarshaller[A, C] = - transform(implicit ec ⇒ mat ⇒ _.fast flatMap f(ec)(mat)) - - def recover[C >: B](pf: ExecutionContext ⇒ Materializer ⇒ PartialFunction[Throwable, C]): Unmarshaller[A, C] = - transform(implicit ec ⇒ mat ⇒ _.fast recover pf(ec)(mat)) - - def withDefaultValue[BB >: B](defaultValue: BB): Unmarshaller[A, BB] = - recover(_ ⇒ _ ⇒ { case Unmarshaller.NoContentException ⇒ defaultValue }) -} - -object Unmarshaller - extends GenericUnmarshallers - with PredefinedFromEntityUnmarshallers - with PredefinedFromStringUnmarshallers { - - // format: OFF - - //#unmarshaller-creation - /** - * Creates an `Unmarshaller` from the given function. - */ - def apply[A, B](f: ExecutionContext ⇒ A ⇒ Future[B]): Unmarshaller[A, B] = - withMaterializer(ec => _ => f(ec)) - - def withMaterializer[A, B](f: ExecutionContext ⇒ Materializer => A ⇒ Future[B]): Unmarshaller[A, B] = - new Unmarshaller[A, B] { - def apply(a: A)(implicit ec: ExecutionContext, materializer: Materializer) = - try f(ec)(materializer)(a) - catch { case NonFatal(e) ⇒ FastFuture.failed(e) } - } - - /** - * Helper for creating a synchronous `Unmarshaller` from the given function. - */ - def strict[A, B](f: A ⇒ B): Unmarshaller[A, B] = Unmarshaller(_ => a ⇒ FastFuture.successful(f(a))) - - /** - * Helper for creating a "super-unmarshaller" from a sequence of "sub-unmarshallers", which are tried - * in the given order. The first successful unmarshalling of a "sub-unmarshallers" is the one produced by the - * "super-unmarshaller". - */ - def firstOf[A, B](unmarshallers: Unmarshaller[A, B]*): Unmarshaller[A, B] = //... - //# - Unmarshaller.withMaterializer { implicit ec ⇒ implicit mat => a ⇒ - def rec(ix: Int, supported: Set[ContentTypeRange]): Future[B] = - if (ix < unmarshallers.size) { - unmarshallers(ix)(a).fast.recoverWith { - case Unmarshaller.UnsupportedContentTypeException(supp) ⇒ rec(ix + 1, supported ++ supp) - } - } else FastFuture.failed(Unmarshaller.UnsupportedContentTypeException(supported)) - rec(0, Set.empty) - } - - // format: ON - - implicit def identityUnmarshaller[T]: Unmarshaller[T, T] = Unmarshaller(_ ⇒ FastFuture.successful) - - // we don't define these methods directly on `Unmarshaller` due to variance constraints - implicit class EnhancedUnmarshaller[A, B](val um: Unmarshaller[A, B]) extends AnyVal { - def mapWithInput[C](f: (A, B) ⇒ C): Unmarshaller[A, C] = - Unmarshaller.withMaterializer(implicit ec ⇒ implicit mat ⇒ a ⇒ um(a).fast.map(f(a, _))) - - def flatMapWithInput[C](f: (A, B) ⇒ Future[C]): Unmarshaller[A, C] = - Unmarshaller.withMaterializer(implicit ec ⇒ implicit mat ⇒ a ⇒ um(a).fast.flatMap(f(a, _))) - } - - implicit class EnhancedFromEntityUnmarshaller[A](val underlying: FromEntityUnmarshaller[A]) extends AnyVal { - def mapWithCharset[B](f: (A, HttpCharset) ⇒ B): FromEntityUnmarshaller[B] = - underlying.mapWithInput { (entity, data) ⇒ f(data, Unmarshaller.bestUnmarshallingCharsetFor(entity)) } - - /** - * Modifies the underlying [[Unmarshaller]] to only accept Content-Types matching one of the given ranges. - * Note that you can only restrict to a subset of the Content-Types accepted by the underlying unmarshaller, - * i.e. the given ranges must be completely supported also by the underlying Unmarshaller! - * If a violation of this rule is detected at runtime, i.e. if an entity is encountered whose Content-Type - * is matched by one of the given ranges but rejected by the underlying unmarshaller - * an IllegalStateException will be thrown! - */ - def forContentTypes(ranges: ContentTypeRange*): FromEntityUnmarshaller[A] = - Unmarshaller.withMaterializer { implicit ec ⇒ implicit mat ⇒ - entity ⇒ - if (entity.contentType == ContentTypes.NoContentType || ranges.exists(_ matches entity.contentType)) { - underlying(entity).fast.recover[A](barkAtUnsupportedContentTypeException(ranges, entity.contentType)) - } else FastFuture.failed(UnsupportedContentTypeException(ranges: _*)) - } - - private def barkAtUnsupportedContentTypeException( - ranges: Seq[ContentTypeRange], - newContentType: ContentType): PartialFunction[Throwable, Nothing] = { - case UnsupportedContentTypeException(supported) ⇒ throw new IllegalStateException( - s"Illegal use of `unmarshaller.forContentTypes($ranges)`: $newContentType is not supported by underlying marshaller!") - } - } - - /** - * Returns the best charset for unmarshalling the given entity to a character-based representation. - * Falls back to UTF-8 if no better alternative can be determined. - */ - def bestUnmarshallingCharsetFor(entity: HttpEntity): HttpCharset = - entity.contentType match { - case x: ContentType.NonBinary ⇒ x.charset - case _ ⇒ HttpCharsets.`UTF-8` - } - - /** - * Signals that unmarshalling failed because the entity was unexpectedly empty. - */ - case object NoContentException extends RuntimeException("Message entity must not be empty") with NoStackTrace - - /** - * Signals that unmarshalling failed because the entity content-type did not match one of the supported ranges. - * This error cannot be thrown by custom code, you need to use the `forContentTypes` modifier on a base - * [[akka.http.scaladsl.unmarshalling.Unmarshaller]] instead. - */ - final case class UnsupportedContentTypeException(supported: Set[ContentTypeRange]) - extends RuntimeException(supported.mkString("Unsupported Content-Type, supported: ", ", ", "")) - - /** Order of parameters (`right` first, `left` second) is intentional, since that's the order we evaluate them in. */ - final case class EitherUnmarshallingException( - rightClass: Class[_], right: Throwable, - leftClass: Class[_], left: Throwable) - extends RuntimeException( - s"Failed to unmarshal Either[${Logging.simpleName(leftClass)}, ${Logging.simpleName(rightClass)}] (attempted ${Logging.simpleName(rightClass)} first). " + - s"Right failure: ${right.getMessage}, " + - s"Left failure: ${left.getMessage}") - - object UnsupportedContentTypeException { - def apply(supported: ContentTypeRange*): UnsupportedContentTypeException = UnsupportedContentTypeException(Set(supported: _*)) - } -} diff --git a/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/package.scala b/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/package.scala deleted file mode 100644 index 46dbbce95a..0000000000 --- a/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/package.scala +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (C) 2009-2016 Lightbend Inc. - */ - -package akka.http.scaladsl - -import akka.http.scaladsl.common.StrictForm -import akka.http.scaladsl.model._ -import akka.util.ByteString - -package object unmarshalling { - //# unmarshaller-aliases - type FromEntityUnmarshaller[T] = Unmarshaller[HttpEntity, T] - type FromMessageUnmarshaller[T] = Unmarshaller[HttpMessage, T] - type FromResponseUnmarshaller[T] = Unmarshaller[HttpResponse, T] - type FromRequestUnmarshaller[T] = Unmarshaller[HttpRequest, T] - type FromByteStringUnmarshaller[T] = Unmarshaller[ByteString, T] - type FromStringUnmarshaller[T] = Unmarshaller[String, T] - type FromStrictFormFieldUnmarshaller[T] = Unmarshaller[StrictForm.Field, T] - //# -} diff --git a/project/AkkaBuild.scala b/project/AkkaBuild.scala index ee05c18118..00403ef7d4 100644 --- a/project/AkkaBuild.scala +++ b/project/AkkaBuild.scala @@ -64,17 +64,9 @@ object AkkaBuild extends Build { contrib, distributedData, docs, - http, - httpCore, - httpJackson, - httpSprayJson, - httpTestkit, - httpTests, - httpXml, kernel, multiNodeTestkit, osgi, - parsing, persistence, persistenceQuery, persistenceShared, @@ -134,7 +126,7 @@ object AkkaBuild extends Build { base = file("akka-bench-jmh"), dependencies = Seq( actor, - http, stream, streamTests, + stream, streamTests, persistence, distributedData, testkit ).map(_ % "compile;compile->test;provided->provided") @@ -238,74 +230,6 @@ object AkkaBuild extends Build { dependencies = Seq(persistence % "test->test", testkit % "test->test", remote % "test", protobuf) ) - lazy val httpCore = Project( - id = "akka-http-core", - base = file("akka-http-core"), - dependencies = Seq(stream, parsing, streamTestkit % "test->test") - ) - - lazy val http = Project( - id = "akka-http-experimental", - base = file("akka-http"), - dependencies = Seq(httpCore) - ) - - lazy val httpTestkit = Project( - id = "akka-http-testkit", - base = file("akka-http-testkit"), - dependencies = Seq(http, streamTestkit) - ) - - lazy val httpTests = Project( - id = "akka-http-tests", - base = file("akka-http-tests"), - dependencies = Seq( - httpTestkit % "test", streamTestkit % "test->test", testkit % "test->test", httpSprayJson, httpXml, httpJackson, - multiNodeTestkit, remoteTests % "test->test") // required for multi-node latency/throughput Spec - ).configs(MultiJvm) - - lazy val httpMarshallersScala = Project( - id = "akka-http-marshallers-scala-experimental", - base = file("akka-http-marshallers-scala") - ) - .settings(parentSettings: _*) - .aggregate(httpSprayJson, httpXml) - - lazy val httpXml = - httpMarshallersScalaSubproject("xml") - - lazy val httpSprayJson = - httpMarshallersScalaSubproject("spray-json") - - lazy val httpMarshallersJava = Project( - id = "akka-http-marshallers-java-experimental", - base = file("akka-http-marshallers-java") - ) - .settings(parentSettings: _*) - .aggregate(httpJackson) - - lazy val httpJackson = - httpMarshallersJavaSubproject("jackson") - - def httpMarshallersScalaSubproject(name: String) = - Project( - id = s"akka-http-$name-experimental", - base = file(s"akka-http-marshallers-scala/akka-http-$name"), - dependencies = Seq(http) - ) - - def httpMarshallersJavaSubproject(name: String) = - Project( - id = s"akka-http-$name-experimental", - base = file(s"akka-http-marshallers-java/akka-http-$name"), - dependencies = Seq(http) - ) - - lazy val parsing = Project( - id = "akka-parsing", - base = file("akka-parsing") - ) - lazy val stream = Project( id = "akka-stream", base = file("akka-stream"), @@ -357,9 +281,7 @@ object AkkaBuild extends Build { remote % "compile;test->test", cluster, clusterMetrics, slf4j, agent, camel, osgi, persistence % "compile;provided->provided;test->test", persistenceTck, persistenceQuery, typed % "compile;test->test", distributedData, - stream, streamTestkit % "compile;test->test", - http, httpSprayJson, httpJackson, httpXml, - httpTests % "compile;test->test", httpTestkit % "compile;test->test" + stream, streamTestkit % "compile;test->test" ) ) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 7c578d057b..a3bb67d721 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -63,12 +63,6 @@ object Dependencies { // ssl-config val sslConfigAkka = "com.typesafe" %% "ssl-config-akka" % "0.2.1" // ApacheV2 - // For akka-http spray-json support - val sprayJson = "io.spray" %% "spray-json" % "1.3.2" // ApacheV2 - - // For akka-http-jackson support - val jackson = "com.fasterxml.jackson.core" % "jackson-databind" % "2.7.6" // ApacheV2 - // For akka-http-testkit-java val junit = "junit" % "junit" % junitVersion // Common Public License 1.0 @@ -113,8 +107,6 @@ object Dependencies { val slf4jJul = "org.slf4j" % "jul-to-slf4j" % "1.7.16" % "test" // MIT val slf4jLog4j = "org.slf4j" % "log4j-over-slf4j" % "1.7.16" % "test" // MIT - lazy val sprayJson = Compile.sprayJson % "test" - // reactive streams tck val reactiveStreamsTck = "org.reactivestreams" % "reactive-streams-tck" % "1.0.0" % "test" // CC0 } @@ -177,34 +169,7 @@ object Dependencies { val benchJmh = l ++= Seq(Provided.levelDB, Provided.levelDBNative) - // akka stream & http - - lazy val httpCore = l ++= Seq( - Test.sprayJson, // for WS Autobahn test metadata - Test.junitIntf, Test.junit, Test.scalatest.value) - - lazy val http = l ++= Nil - - // special, since it also includes a compiler plugin - lazy val parsing = Seq( - DependencyHelpers.versionDependentDeps( - Dependencies.Compile.scalaReflect % "provided" - ), - addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.fullMapped(nominalScalaVersion)) - ) - - lazy val httpTestkit = l ++= Seq( - Test.junit, Test.junitIntf, Compile.junit % "provided", Test.scalatest.value.copy(configurations = Some("provided; test"))) - - // TODO collapse those - lazy val httpTests = l ++= Seq(Test.junit, Test.scalatest.value, Test.junitIntf) - lazy val httpTestsJava8 = l ++= Seq(Test.junit, Test.junitIntf) - - lazy val httpXml = versionDependentDeps(scalaXml) - - lazy val httpSprayJson = versionDependentDeps(sprayJson) - - lazy val httpJackson = l ++= Seq(jackson) + // akka stream lazy val stream = l ++= Seq[sbt.ModuleID]( sslConfigAkka, diff --git a/project/Doc.scala b/project/Doc.scala index 2294bd2315..56c00c6883 100644 --- a/project/Doc.scala +++ b/project/Doc.scala @@ -120,7 +120,7 @@ object UnidocRoot extends AutoPlugin { override lazy val projectSettings = CliOptions.genjavadocEnabled.ifTrue(scalaJavaUnidocSettings).getOrElse(scalaUnidocSettings) ++ - settings(Seq(AkkaBuild.samples), Seq(AkkaBuild.remoteTests, AkkaBuild.benchJmh, AkkaBuild.parsing, AkkaBuild.protobuf, AkkaBuild.osgiDiningHakkersSampleMavenTest, AkkaBuild.akkaScalaNightly)) + settings(Seq(AkkaBuild.samples), Seq(AkkaBuild.remoteTests, AkkaBuild.benchJmh, AkkaBuild.protobuf, AkkaBuild.osgiDiningHakkersSampleMavenTest, AkkaBuild.akkaScalaNightly)) } /**