diff --git a/akka-docs-dev/_sphinx/exts/includecode2.py b/akka-docs-dev/_sphinx/exts/includecode2.py new file mode 100644 index 0000000000..1c67f23015 --- /dev/null +++ b/akka-docs-dev/_sphinx/exts/includecode2.py @@ -0,0 +1,109 @@ +import os +import codecs +import re +from os import path + +from docutils import nodes +from docutils.parsers.rst import Directive, directives + +class IncludeCode2(Directive): + """ + Include a code example from a file with sections delimited with special comments. + """ + + has_content = False + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = False + option_spec = { + 'snippet': directives.unchanged_required + } + + def run(self): + document = self.state.document + arg0 = self.arguments[0] + (filename, sep, section) = arg0.partition('#') + + if not document.settings.file_insertion_enabled: + return [document.reporter.warning('File insertion disabled', line=self.lineno)] + env = document.settings.env + if filename.startswith('/') or filename.startswith(os.sep): + rel_fn = filename[1:] + else: + docdir = path.dirname(env.doc2path(env.docname, base=None)) + rel_fn = path.join(docdir, filename) + try: + fn = path.join(env.srcdir, rel_fn) + except UnicodeDecodeError: + # the source directory is a bytestring with non-ASCII characters; + # let's try to encode the rel_fn in the file system encoding + rel_fn = rel_fn.encode(sys.getfilesystemencoding()) + fn = path.join(env.srcdir, rel_fn) + + encoding = self.options.get('encoding', env.config.source_encoding) + codec_info = codecs.lookup(encoding) + try: + f = codecs.StreamReaderWriter(open(fn, 'U'), + codec_info[2], codec_info[3], 'strict') + lines = f.readlines() + f.close() + except (IOError, OSError): + return [document.reporter.warning( + 'Include file %r not found or reading it failed' % fn, + line=self.lineno)] + except UnicodeError: + return [document.reporter.warning( + 'Encoding %r used for reading included file %r seems to ' + 'be wrong, try giving an :encoding: option' % + (encoding, fn))] + + snippet = self.options.get('snippet') + current_snippets = "" + res = [] + for line in lines: + comment = line.rstrip().split("//", 1)[1] if line.find("//") >= 0 else "" + if comment.startswith("#") and len(comment) > 1: + current_snippets = comment + indent = line.find("//") + elif len(line) > 2 and line[2] == '"' and not current_snippets.startswith("#"): + current_snippets = line[2:] + indent = 4 + elif comment == "#" and current_snippets.startswith("#"): + current_snippets = "" + elif len(line) > 2 and line[2] == '}' and not current_snippets.startswith("#"): + current_snippets = "" + elif current_snippets.find(snippet) >= 0 and comment.find("hide") == -1: + res.append(line[indent:].rstrip() + '\n') + elif comment.find(snippet) >= 0: + array = line.split("//", 1) + l = array[0].rstrip() if array[1].startswith("/") > 0 else array[0].strip() + res.append(l + '\n') + elif re.search("(def|val) "+re.escape(snippet)+"(?=[:(\[])", line): + # match signature line from `def ` but without trailing `=` + start = line.find("def") + if start == -1: start = line.find("val") + + # include `implicit` if definition is prefixed with it + implicitstart = line.find("implicit") + if implicitstart != -1 and implicitstart < start: start = implicitstart + + end = line.rfind("=") + if end == -1: current_snippets = "matching_signature" + res.append(line[start:end] + '\n') + elif current_snippets == "matching_signature": + end = line.rfind("=") + if end != -1: current_snippets = "" + res.append(line[start:end] + '\n') + + text = ''.join(res) + + if text == "": + return [document.reporter.warning('Snippet "' + snippet + '" not found!', line=self.lineno)] + + retnode = nodes.literal_block(text, text, source=fn) + document.settings.env.note_dependency(rel_fn) + return [retnode] + +def setup(app): + app.require_sphinx('1.0') + app.add_directive('includecode2', IncludeCode2) diff --git a/akka-docs-dev/rst/conf.py b/akka-docs-dev/rst/conf.py index 865efbd21e..593c9d421c 100644 --- a/akka-docs-dev/rst/conf.py +++ b/akka-docs-dev/rst/conf.py @@ -8,7 +8,7 @@ import sys, os # -- General configuration ----------------------------------------------------- sys.path.append(os.path.abspath('../_sphinx/exts')) -extensions = ['sphinx.ext.todo', 'includecode'] +extensions = ['sphinx.ext.todo', 'includecode', 'includecode2'] templates_path = ['_templates'] source_suffix = '.rst' diff --git a/akka-docs-dev/rst/scala/code/docs/http/HttpServerExampleSpec.scala b/akka-docs-dev/rst/scala/code/docs/http/HttpServerExampleSpec.scala index 10e84f1f1e..8f9fd2b5b3 100644 --- a/akka-docs-dev/rst/scala/code/docs/http/HttpServerExampleSpec.scala +++ b/akka-docs-dev/rst/scala/code/docs/http/HttpServerExampleSpec.scala @@ -22,9 +22,8 @@ class HttpServerExampleSpec implicit val materializer = FlowMaterializer() val serverBinding = Http(system).bind(interface = "localhost", port = 8080) - for (connection <- serverBinding.connections) { + serverBinding.connections.foreach { connection ⇒ // foreach materializes the source println("Accepted new connection from " + connection.remoteAddress) - // handle connection here } //#bind-example } @@ -56,7 +55,9 @@ class HttpServerExampleSpec serverBinding.connections foreach { connection => println("Accepted new connection from " + connection.remoteAddress) - connection handleWith { Flow[HttpRequest] map requestHandler } + connection handleWithSyncHandler requestHandler + // this is equivalent to + // connection handleWith { Flow[HttpRequest] map requestHandler } } //#full-server-example } diff --git a/akka-docs-dev/rst/scala/code/docs/http/server/DirectiveExamplesSpec.scala b/akka-docs-dev/rst/scala/code/docs/http/server/DirectiveExamplesSpec.scala new file mode 100644 index 0000000000..13e0cfc2db --- /dev/null +++ b/akka-docs-dev/rst/scala/code/docs/http/server/DirectiveExamplesSpec.scala @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2009-2014 Typesafe Inc. + */ + +package docs.http.server + +import akka.http.server._ +import Directives._ +import akka.http.testkit.ScalatestRouteTest +import org.scalatest._ + +class DirectiveExamplesSpec extends RoutingSpec { + + "example-1, example-2" 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-3" 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-4" in { + val route = + path("order" / IntNumber) { id => + (get | put) { ctx => + ctx.complete("Received " + ctx.request.method.name + " request for order " + id) + } + } + verify(route) // hide + } + + "example-5" in { + val getOrPut = get | put + val route = + path("order" / IntNumber) { id => + getOrPut { ctx => + ctx.complete("Received " + ctx.request.method.name + " request for order " + id) + } + } + verify(route) // hide + } + + "example-6" in { + val getOrPut = get | put + val route = + (path("order" / IntNumber) & getOrPut) { id => + ctx => + ctx.complete("Received " + ctx.request.method.name + " request for order " + id) + } + verify(route) // hide + } + + "example-7" in { + val orderGetOrPut = path("order" / IntNumber) & (get | put) + val route = + orderGetOrPut { id => + ctx => + ctx.complete("Received " + ctx.request.method.name + " request for order " + id) + } + verify(route) // hide + } + + "example-8" in { + val orderGetOrPut = path("order" / IntNumber) & (get | put) + val requestMethod = extract(_.request.method) + val route = + orderGetOrPut { id => + requestMethod { m => + complete("Received " + m.name + " request for order " + id) + } + } + verify(route) // hide + } + + "example-9" in { + val orderGetOrPut = path("order" / IntNumber) & (get | put) + val requestMethod = extract(_.request.method) + val route = + (orderGetOrPut & requestMethod) { (id, m) => + complete("Received " + m.name + " request for order " + id) + } + verify(route) // hide + } + + "example-A" in { + val orderGetOrPutMethod = + path("order" / IntNumber) & (get | put) & extract(_.request.method) + val route = + orderGetOrPutMethod { (id, m) => + complete("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-dev/rst/scala/code/docs/http/server/ExceptionHandlerExamplesSpec.scala b/akka-docs-dev/rst/scala/code/docs/http/server/ExceptionHandlerExamplesSpec.scala new file mode 100644 index 0000000000..4ccbf5d277 --- /dev/null +++ b/akka-docs-dev/rst/scala/code/docs/http/server/ExceptionHandlerExamplesSpec.scala @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2009-2014 Typesafe Inc. + */ + +package docs.http.server + +import akka.actor.ActorSystem +import akka.http.server.Route +import akka.stream.FlowMaterializer + +object MyHandler { + //# example-1 + import akka.http.model.HttpResponse + import akka.http.model.StatusCodes._ + import akka.http.server._ + import Directives._ + + implicit def myExceptionHandler = + ExceptionHandler { + case e: ArithmeticException => + extractUri { uri => + logWarning(s"Request to $uri could not be handled normally") + complete(HttpResponse(InternalServerError, entity = "Bad numbers, bad result!!!")) + } + } + + object MyApp { + implicit val system = ActorSystem() + import system.dispatcher + implicit val materializer = FlowMaterializer() + + def handler = Route.handlerFlow(``) + } + //# + + def ``: Route = null + def logWarning(str: String): Unit = {} +} + +class ExceptionHandlerExamplesSpec extends RoutingSpec { + import MyHandler._ + + "example" in { + Get() ~> Route.seal(ctx => ctx.complete((1 / 0).toString)) ~> check { + responseAs[String] === "Bad numbers, bad result!!!" + } + } +} diff --git a/akka-docs-dev/rst/scala/code/docs/http/server/RejectionHandlerExamplesSpec.scala b/akka-docs-dev/rst/scala/code/docs/http/server/RejectionHandlerExamplesSpec.scala new file mode 100644 index 0000000000..5c77a82888 --- /dev/null +++ b/akka-docs-dev/rst/scala/code/docs/http/server/RejectionHandlerExamplesSpec.scala @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2009-2014 Typesafe Inc. + */ + +package docs.http.server + +import akka.actor.ActorSystem +import akka.stream.FlowMaterializer + +import akka.http.server.{ Route, MissingCookieRejection } + +import scala.concurrent.ExecutionContext + +object MyRejectionHandler { + //# example-1 + import akka.http.model._ + import akka.http.server._ + import StatusCodes._ + import Directives._ + + implicit val myRejectionHandler = RejectionHandler { + case MissingCookieRejection(cookieName) :: _ => + complete(HttpResponse(BadRequest, entity = "No cookies, no service!!!")) + } + + object MyApp { + implicit val system = ActorSystem() + import system.dispatcher + implicit val materializer = FlowMaterializer() + + def handler = Route.handlerFlow(``) + } + //# + + def ``: Route = null +} + +class RejectionHandlerExamplesSpec extends RoutingSpec { + import MyRejectionHandler._ + + "example" in { + Get() ~> Route.seal(reject(MissingCookieRejection("abc"))) ~> check { + responseAs[String] === "No cookies, no service!!!" + } + } + + "example-2" in { + import akka.http.coding.Gzip + + val route = + path("order") { + get { + complete("Received GET") + } ~ + post { + decodeRequest(Gzip) { + complete("Received POST") + } + } + } + } +} diff --git a/akka-docs-dev/rst/scala/code/docs/http/server/RoutingSpec.scala b/akka-docs-dev/rst/scala/code/docs/http/server/RoutingSpec.scala new file mode 100644 index 0000000000..2aad96e0d0 --- /dev/null +++ b/akka-docs-dev/rst/scala/code/docs/http/server/RoutingSpec.scala @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2009-2014 Typesafe Inc. + */ + +package docs.http.server + +import akka.http.server.Directives +import akka.http.testkit.ScalatestRouteTest +import org.scalatest.{ Matchers, WordSpec } + +abstract class RoutingSpec extends WordSpec with Matchers with Directives with ScalatestRouteTest diff --git a/akka-docs-dev/rst/scala/code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala b/akka-docs-dev/rst/scala/code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala new file mode 100644 index 0000000000..50b4575b7b --- /dev/null +++ b/akka-docs-dev/rst/scala/code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2009-2014 Typesafe Inc. + */ + +package docs.http.server +package directives + +import akka.http.model.headers.RawHeader +import akka.http.server.RouteResult.Rejected +import akka.http.server._ +import akka.util.ByteString + +import scala.util.control.NonFatal + +import akka.http.model._ + +class BasicDirectivesExamplesSpec extends RoutingSpec { + "0extract" in { + val uriLength = extract(_.request.uri.toString.length) + val route = + uriLength { len => + complete(s"The length of the request URI is $len") + } + + Get("/abcdef") ~> route ~> check { + responseAs[String] shouldEqual "The length of the request URI is 25" + } + } + "textract" in { + 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") + } + + Get("/abcdef?ghi=12") ~> route ~> check { + responseAs[String] shouldEqual "The path is /abcdef and the query is ghi=12" + } + } + "tprovide" in { + def provideStringAndLength(value: String) = tprovide((value, value.length)) + val route = + provideStringAndLength("test") { (value, len) => + complete(s"Value is $value and its length is $len") + } + Get("/") ~> route ~> check { + responseAs[String] shouldEqual "Value is test and its length is 4" + } + } + "0mapResponse" in { + def overwriteResultStatus(response: HttpResponse): HttpResponse = + response.copy(status = StatusCodes.BadGateway) + val route = mapResponse(overwriteResultStatus)(complete("abc")) + + Get("/abcdef?ghi=12") ~> route ~> check { + status shouldEqual StatusCodes.BadGateway + } + } + "mapResponseEntity" in { + 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")) + + Get("/") ~> route ~> check { + responseAs[String] shouldEqual "testabc" + } + } + "mapResponseHeaders" in { + // 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") + } + } + + Get("/") ~> RawHeader("id", "12345") ~> RawHeader("id2", "67890") ~> route ~> check { + header("id") shouldEqual None + header("id2").get.value shouldEqual "67890" + } + } + "mapInnerRoute" in { + 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")) + } + + Get("/") ~> route ~> check { + responseAs[String] shouldEqual "Got IllegalArgumentException 'BLIP! BLOP! Everything broke'" + } + } + "mapRejections" in { + // ignore any rejections and replace them by AuthorizationFailedRejection + val replaceByAuthorizationFailed = mapRejections(_ => List(AuthorizationFailedRejection)) + val route = + replaceByAuthorizationFailed { + path("abc")(complete("abc")) + } + + Get("/") ~> route ~> check { + rejection shouldEqual AuthorizationFailedRejection + } + } + "0mapRequest" in { + 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 { + val replaceRequest = + mapRequestContext(_.withRequest(HttpRequest(HttpMethods.POST))) + + val route = + replaceRequest { + extractRequest { req => + complete(req.method.value) + } + } + + Get("/abc/def/ghi") ~> route ~> check { + responseAs[String] shouldEqual "POST" + } + } + "0mapRouteResponse" in { + val rejectAll = // not particularly useful directive + mapRouteResult { + case _ => Rejected(List(AuthorizationFailedRejection)) + } + val route = + rejectAll { + complete("abc") + } + + Get("/") ~> route ~> check { + rejections.nonEmpty shouldEqual true + } + } + "mapRouteResponsePF" in { + case object MyCustomRejection extends Rejection + val rejectRejections = // not particularly useful directive + mapRouteResultPF { + case Rejected(_) => Rejected(List(AuthorizationFailedRejection)) + } + val route = + rejectRejections { + reject(MyCustomRejection) + } + + Get("/") ~> route ~> check { + rejection shouldEqual AuthorizationFailedRejection + } + } + "pass" in { + Get("/") ~> pass(complete("abc")) ~> check { + responseAs[String] shouldEqual "abc" + } + } + "0provide" in { + def providePrefixedString(value: String): Directive1[String] = provide("prefix:" + value) + val route = + providePrefixedString("test") { value => + complete(value) + } + Get("/") ~> route ~> check { + responseAs[String] shouldEqual "prefix:test" + } + } +} diff --git a/akka-docs-dev/rst/scala/code/docs/http/server/directives/CodingDirectivesExamplesSpec.scala b/akka-docs-dev/rst/scala/code/docs/http/server/directives/CodingDirectivesExamplesSpec.scala new file mode 100644 index 0000000000..563db83d01 --- /dev/null +++ b/akka-docs-dev/rst/scala/code/docs/http/server/directives/CodingDirectivesExamplesSpec.scala @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2009-2014 Typesafe Inc. + */ + +package docs.http.server +package directives + +import akka.http.coding._ +import akka.http.model.{ HttpResponse, StatusCodes } +import akka.http.model.headers.{ HttpEncodings, HttpEncoding, `Accept-Encoding`, `Content-Encoding` } +import akka.http.model.headers.HttpEncodings._ +import akka.http.server._ +import akka.util.ByteString +import org.scalatest.matchers.Matcher + +class CodingDirectivesExamplesSpec extends RoutingSpec { + "compressResponse-0" in { + val route = compressResponse() { complete("content") } + + Get("/") ~> route ~> check { + response should haveContentEncoding(gzip) + } + 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 { + status shouldEqual StatusCodes.OK + response should haveContentEncoding(identity) + responseAs[String] shouldEqual "content" + } + } + "compressResponse-1" in { + val route = compressResponse(Gzip) { complete("content") } + + 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) + } + } + "compressResponseIfRequested" in { + val route = compressResponseIfRequested() { complete("content") } + + 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) + } + } + "encodeResponse" in { + val route = encodeResponse(Gzip) { complete("content") } + + 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(Gzip) { + entity(as[String]) { content: String => + complete(s"Request content: '$content'") + } + } + + 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) + } + } + "decompressRequest-0" in { + val route = + decompressRequest() { + entity(as[String]) { content: String => + complete(s"Request content: '$content'") + } + } + + 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'" + } + } + "decompressRequest-1" in { + val route = + decompressRequest(Gzip, NoCoding) { + entity(as[String]) { content: String => + complete(s"Request content: '$content'") + } + } + + 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-dev/rst/scala/code/docs/http/server/directives/CookieDirectivesExamplesSpec.scala b/akka-docs-dev/rst/scala/code/docs/http/server/directives/CookieDirectivesExamplesSpec.scala new file mode 100644 index 0000000000..57535ec4ec --- /dev/null +++ b/akka-docs-dev/rst/scala/code/docs/http/server/directives/CookieDirectivesExamplesSpec.scala @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2009-2014 Typesafe Inc. + */ + +package docs.http.server +package directives + +import akka.http.server._ +import akka.http.model.headers.{ HttpCookie, Cookie, `Set-Cookie` } +import akka.http.util.DateTime + +class CookieDirectivesExamplesSpec extends RoutingSpec { + "cookie" in { + val route = + cookie("userName") { nameCookie => + complete(s"The logged in user is '${nameCookie.content}'") + } + + Get("/") ~> Cookie(HttpCookie("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.content}'") + case None => complete("No user logged in") + } + + Get("/") ~> Cookie(HttpCookie("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") + } + + Get("/") ~> route ~> check { + responseAs[String] shouldEqual "The user was logged out" + header[`Set-Cookie`] shouldEqual Some(`Set-Cookie`(HttpCookie("userName", content = "deleted", expires = Some(DateTime.MinValue)))) + } + } + "setCookie" in { + val route = + setCookie(HttpCookie("userName", content = "paul")) { + complete("The user was logged in") + } + + Get("/") ~> route ~> check { + responseAs[String] shouldEqual "The user was logged in" + header[`Set-Cookie`] shouldEqual Some(`Set-Cookie`(HttpCookie("userName", content = "paul"))) + } + } +} diff --git a/akka-docs-dev/rst/scala/code/docs/http/server/directives/DebuggingDirectivesExamplesSpec.scala b/akka-docs-dev/rst/scala/code/docs/http/server/directives/DebuggingDirectivesExamplesSpec.scala new file mode 100644 index 0000000000..593efeb35f --- /dev/null +++ b/akka-docs-dev/rst/scala/code/docs/http/server/directives/DebuggingDirectivesExamplesSpec.scala @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2009-2014 Typesafe Inc. + */ + +package docs.http.server +package directives + +import akka.http.model.{ HttpResponse, HttpRequest } +import akka.http.server._ + +import akka.event.Logging +import akka.http.server.directives.{ LoggingMagnet, LogEntry, DebuggingDirectives } + +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.toString + DebuggingDirectives.logRequest(requestMethod _) + + // logs just the request method at info level + def requestMethodAsInfo(req: HttpRequest): LogEntry = LogEntry(req.method.toString, 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) + val logRequestPrintln = DebuggingDirectives.logRequest(LoggingMagnet(_ => printRequestMethod)) + + 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): Any => Option[LogEntry] = { + case res: HttpResponse => Some(LogEntry(req.method + ":" + res.status, Logging.InfoLevel)) + case _ => None // other kind of responses + } + DebuggingDirectives.logRequestResult(requestMethodAndResponseStatusAsInfo _) + + // This one doesn't use the implicit LoggingContext but uses `println` for logging + def printRequestMethodAndResponseStatus(req: HttpRequest)(res: Any): Unit = + println(requestMethodAndResponseStatusAsInfo(req)(res).map(_.obj.toString).getOrElse("")) + val logRequestResultPrintln = DebuggingDirectives.logRequestResult(LoggingMagnet(_ => printRequestMethodAndResponseStatus)) + + 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: Any): String = res match { + case x: HttpResponse => x.status.toString + case _ => "unknown response part" + } + DebuggingDirectives.logResult(responseStatus _) + + // logs just the response status at info level + def responseStatusAsInfo(res: Any): 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: Any): Unit = println(responseStatus(res)) + val logResultPrintln = DebuggingDirectives.logResult(LoggingMagnet(_ => printResponseStatus)) + + Get("/") ~> logResultPrintln(complete("logged")) ~> check { + responseAs[String] shouldEqual "logged" + } + } +} diff --git a/akka-docs-dev/rst/scala/code/docs/http/server/directives/ExecutionDirectivesExamplesSpec.scala b/akka-docs-dev/rst/scala/code/docs/http/server/directives/ExecutionDirectivesExamplesSpec.scala new file mode 100644 index 0000000000..3c9883de0e --- /dev/null +++ b/akka-docs-dev/rst/scala/code/docs/http/server/directives/ExecutionDirectivesExamplesSpec.scala @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2009-2014 Typesafe Inc. + */ + +package docs.http.server +package directives + +import akka.http.model.StatusCodes +import akka.http.server._ + +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}") + } + } + + 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 { + case Nil /* secret code for path not found */ => + complete(StatusCodes.NotFound, "Oh man, what you are looking for is long gone.") + } + val route = + pathPrefix("handled") { + handleRejections(totallyMissingHandler) { + path("existing")(complete("This path exists")) + } + } + + 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." + } + } +} diff --git a/akka-docs-dev/rst/scala/code/docs/http/server/directives/PathDirectivesExamplesSpec.scala b/akka-docs-dev/rst/scala/code/docs/http/server/directives/PathDirectivesExamplesSpec.scala new file mode 100644 index 0000000000..b8c3c770a8 --- /dev/null +++ b/akka-docs-dev/rst/scala/code/docs/http/server/directives/PathDirectivesExamplesSpec.scala @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2009-2014 Typesafe Inc. + */ + +package docs.http.server +package directives + +import akka.http.server._ + +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") + } + } + + 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") + } + } + + 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") + } + } + + 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") + } + } + + 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 } + } + + 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") + } + } + + 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 + } + } + + 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") + + 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 } + } + + 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 + } + } + + Get("/foobar") ~> route ~> check { + responseAs[String] shouldEqual "bar" + } + + Get("/foobaz") ~> route ~> check { + handled shouldEqual false + } + } +} diff --git a/akka-docs-dev/rst/scala/http-core/https.rst b/akka-docs-dev/rst/scala/http-core/https.rst index ec501c0a62..3683882a58 100644 --- a/akka-docs-dev/rst/scala/http-core/https.rst +++ b/akka-docs-dev/rst/scala/http-core/https.rst @@ -1,4 +1,6 @@ HTTPS ===== +Is not yet supported. + (todo) \ No newline at end of file diff --git a/akka-docs-dev/rst/scala/http-core/model.rst b/akka-docs-dev/rst/scala/http-core/model.rst index b0b20aeee5..4729991149 100644 --- a/akka-docs-dev/rst/scala/http-core/model.rst +++ b/akka-docs-dev/rst/scala/http-core/model.rst @@ -108,6 +108,9 @@ HttpEntity.CloseDelimited A streaming entity of unspecified length that is delimited by closing the connection ("Connection: close"). Note, that this entity type can only be used in an ``HttpResponse``. +HttpEntity.IndefiniteLength + A streaming entity of unspecified length that can be used as a ``BodyPart`` entity. + 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. @@ -130,7 +133,9 @@ which allows access to the data of an entity regardless of its concrete subtype. - Use Strict if the amount of data is small and it is already in the heap (or even available as a ``ByteString``) - Use Default if the data is generated by a streaming data source and the size of the data is fixed - Use Chunked to support a data stream of unknown length - - Use CloseDelimited as an alternative to Chunked e.g. if chunked transfer encoding isn't supported by a peer. + - Use CloseDelimited for a response as an alternative to Chunked e.g. if chunked transfer encoding isn't supported + by a client. + - Use IndefiniteLength instead of CloseDelimited in a BodyPart. Header model ------------ diff --git a/akka-docs-dev/rst/scala/http-core/server.rst b/akka-docs-dev/rst/scala/http-core/server.rst index af29624f33..9756fe784c 100644 --- a/akka-docs-dev/rst/scala/http-core/server.rst +++ b/akka-docs-dev/rst/scala/http-core/server.rst @@ -8,11 +8,9 @@ HTTP/1.1 server implemented on top of `Akka Streams`_. (todo: fix link) It sports the following features: -- Low per-connection overhead for supporting many thousand concurrent connections -- Efficient message parsing and processing logic for high throughput applications - 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 +- Full support for asynchronous HTTP streaming including "chunked" transfer encoding accessible through an idiomatic reactive streams API - Optional SSL/TLS encryption @@ -27,7 +25,7 @@ Design Philosophy Akka HTTP server is scoped with a clear focus on the essential functionality of an HTTP/1.1 server: - Connection management -- Message parsing and header separation +- Parsing messages and headers - Timeout management (for requests and connections) - Response ordering (for transparent pipelining support) @@ -44,8 +42,8 @@ Akka HTTP server is implemented on top of Akka streams and makes heavy use of th implementation and also on all levels of its API. On the connection level Akka HTTP supports basically the same interface as Akka streams IO: A socket binding is -represented as a stream of incoming connections. Each connection itself is composed of an input stream of requests and -an output consumer of responses. The application has to provide the handler to "translate" requests into responses. +represented as a stream of incoming connections. The application needs to provide a ``Flow[HttpRequest, HttpResponse]`` +to "translate" requests into responses. Streaming is also supported for single message entities itself. Particular kinds of ``HttpEntity`` subclasses provide support for fixed or streamed message entities. @@ -54,26 +52,27 @@ subclasses provide support for fixed or streamed message entities. Starting and Stopping --------------------- -An Akka HTTP server is started by sending an ``Http.Bind`` command to the `akka.http.Http`_ extension: +An Akka HTTP server is bound by invoking the ``bind`` method of the `akka.http.Http`_ extension: .. includecode:: ../code/docs/http/HttpServerExampleSpec.scala :include: bind-example -With the ``Http.Bind`` command you specify the interface and port to bind to and register interest in handling incoming -HTTP connections. Additionally the ``Http.Bind`` command also allows you to define socket options as well as a larger number +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 you to define socket options as well as a larger number of settings for configuring the server according to your needs. -The sender of the ``Http.Bind`` command (e.g. an actor you have written) will receive an ``Http.ServerBinding`` reply -after the HTTP layer has successfully started the server at the respective endpoint. In case the bind fails (e.g. -because the port is already busy) a ``Failure`` message is dispatched instead. As shown in the above example this works -well with the ask pattern and Future operations. +The result of the ``bind`` method is a ``Http.ServerBinding`` which is immediately returned. The ``ServerBinding.connections`` +returns a ``Source[IncomingConnection]`` which is used to handle incoming connections. The actual binding is only done when this +source is materialized as part of a bigger processing pipeline. In case the bind fails (e.g. because the port is already +busy) the error will be reported by flagging an error on the ``IncomingConnection`` stream. -The ``Http.ServerBinding`` informs the binder of the actual local address of the bound socket and it contains a -stream of incoming connections of type ``Producer[Http.IncomingConnection]``. Connections are handled by subscribing -to the connection stream and handling the incoming connections. +After materialization ``ServerBinding.localAddress`` returns the actual local address of the bound socket. -The binding is released and the underlying listening socket is closed when all subscribers of the -``Http.ServerBinding.connectionStream`` have cancelled their subscriptions. +Connections are handled by materializing a pipeline which uses the ``Source[IncomingConnection]`` returned by +``ServerBinding.connections``. The binding is released and the underlying listening socket is closed when all +subscribers of the ``connections`` source have cancelled their subscription. + +(todo: explain even lower level serverFlowToTransport API) .. _akka.http.Http: @github@/akka-http-core/src/main/scala/akka/http/Http.scala @@ -81,23 +80,28 @@ The binding is released and the underlying listening socket is closed when all s Request-Response Cycle ---------------------- -When a new connection has been accepted it will be published by the ``Http.ServerBinding.connectionStream`` as an -``Http.IncomingConnection`` which consists of the remote address, a ``requestProducer``, and a ``responseConsumer``. +When a new connection has been accepted it will be published by by the ``ServerBinding.connections`` source 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. -Handling requests in this model means connecting the ``requestProducer`` stream with an application-defined component that -maps requests to responses which then feeds into the ``responseConsumer``: +Requests are handled by calling one of the ``IncomingConnection.handleWithX`` methods with a handler, which can either be + + - a ``Flow[HttpRequest, HttpResponse]`` for ``handleWith``, + - a function ``HttpRequest => HttpResponse`` for ``handleWithSyncHandler``, + - or a function ``HttpRequest => Future[HttpResponse]`` for ``handleWithAsyncHandler``. .. includecode:: ../code/docs/http/HttpServerExampleSpec.scala :include: full-server-example In this case, a request is handled by transforming the request stream with a function ``HttpRequest => HttpResponse`` -using Akka stream's ``map`` operator. Depending on the use case, arbitrary other ways of connecting are conceivable using -Akka stream's operators (e.g using ``mapFuture`` to allow parallel processing of several requests when HTTP pipelining is -enabled). +using ``handleWithSyncHandler`` (or equivalently, Akka stream's ``map`` operator). Depending on the use case, arbitrary +other ways of connecting are conceivable using Akka stream's combinators. -It's the application's responsibility to feed responses into the ``responseConsumer`` in the same order as the respective -requests have come in. Also, each request must result in exactly one response. Using stream operators like ``map`` or -``mapFuture`` will automatically fulfill this requirement. +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). Using ``handleWithSyncHandler`` +or ``handleWithAsyncHandler`` or, instead, if using stream operators like ``map`` or ``mapFuture`` this requirement +will automatically be fulfilled. Streaming request/response entities ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -112,8 +116,8 @@ a description of the alternatives. Closing a connection ~~~~~~~~~~~~~~~~~~~~ -The HTTP connection will be closed when the ``responseConsumer`` gets completed or when the ``requestProducer``'s -subscription was cancelled and no more responses are pending. +The HTTP connection will be closed when the handling ``Flow`` cancel its upstream subscription or the peer closes the +connection. You can also use the value of the ``Connection`` header of a response as described below to give a hint to the implementation to close the connection after the completion of the response. @@ -139,7 +143,7 @@ field of an HTTP message: entities don't need to define a length. - ``Server``: The ``Server`` header is usually added automatically and it's value can be configured. An application can decide to provide a custom ``Server`` header by including an explicit instance in the response. -- ``Date``: The ``Date`` header is added automatically and will be ignored if supplied manually. +- ``Date``: The ``Date`` header is added automatically and can be overridden by supplying it manually. - ``Connection``: When sending out responses the connection actor watches for a ``Connection`` header set by the application and acts accordingly, i.e. you can force the connection actor to close the connection after having sent the response by including a ``Connection("close")`` header. To unconditionally force a connection keep-alive you can diff --git a/akka-docs-dev/rst/scala/http/custom-directives.rst b/akka-docs-dev/rst/scala/http/custom-directives.rst deleted file mode 100644 index e6ab3366b7..0000000000 --- a/akka-docs-dev/rst/scala/http/custom-directives.rst +++ /dev/null @@ -1,4 +0,0 @@ -Custom Directives -================= - -(todo) \ No newline at end of file diff --git a/akka-docs-dev/rst/scala/http/directives.rst b/akka-docs-dev/rst/scala/http/directives.rst new file mode 100644 index 0000000000..113854efcf --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives.rst @@ -0,0 +1,138 @@ +.. _Directives: + +Directives +========== + +A "Directive" is a small building block to construct arbitrarily complex route structures. +Here is a simple example of a route built from directives: + +.. includecode2:: ../code/docs/http/server/DirectiveExamplesSpec.scala + :snippet: example-1 + +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. 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 +* Filter the ``RequestContext`` according to some logic, i.e. only pass on certain requests and reject all others +* Extract values from the ``RequestContext`` and make them available to its inner Route as "extractions" +* Complete the request + +The first point deserves some more discussion. The ``RequestContext`` is the central object that is passed on through a +route structure (also see :ref:`RequestContext`). When a directive (or better the Route it built) receives a ``RequestContext`` +it can decide to pass this instance on unchanged to its inner Route or it can create a copy of the ``RequestContext`` instance, +with one or more changes, and pass on this copy to its inner Route. Typically this is good for two things: + +* Transforming the ``HttpRequest`` instance +* "Hooking in" a response transformation function that changes the ``RouteResponse`` (and therefore the response). + +This means a ``Directive`` completely wraps the functionality of its inner routes and can apply arbitrarily complex +transformations, both (or either) on the request and on the response side. + +Composing Directives +-------------------- + +As you have seen from the examples presented so far the "normal" way of composing directives is nesting. Let's take +another look at the example from above: + +.. includecode2:: ../code/docs/http/server/DirectiveExamplesSpec.scala + :snippet: example-2 + +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/server/DirectiveExamplesSpec.scala + :snippet: example-3 + +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/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/server/DirectiveExamplesSpec.scala + :snippet: example-5 + +As an alternative to nesting you can also use the `&` operator: + +.. includecode2:: ../code/docs/http/server/DirectiveExamplesSpec.scala + :snippet: example-6 + +And once again, you can factor things out if you want: + +.. includecode2:: ../code/docs/http/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. + +There is one more "ugly" thing remaining in our snippet: we have to fall back to the lowest-level route definition, +directly manipulating the ``RequestContext``, in order to get to the request method. It'd be nicer if we could somehow +"extract" the method name in a special directive, so that we can express our inner-most route with a simple +``complete``. As it turns out this is easy with the ``extract`` directive: + +.. includecode2:: ../code/docs/http/server/DirectiveExamplesSpec.scala + :snippet: example-8 + +Or differently: + +.. includecode2:: ../code/docs/http/server/DirectiveExamplesSpec.scala + :snippet: example-9 + +Now, pushing the "factoring out" of directive configurations to its extreme, we end up with this: + +.. includecode2:: ../code/docs/http/server/DirectiveExamplesSpec.scala + :snippet: example-A + +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 very easily create :ref:`Custom Directives`. diff --git a/akka-docs-dev/rst/scala/http/directives/alphabetically.rst b/akka-docs-dev/rst/scala/http/directives/alphabetically.rst new file mode 100644 index 0000000000..787df8fde9 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/alphabetically.rst @@ -0,0 +1,161 @@ +.. _Predefined Directives: + +Predefined Directives (alphabetically) +====================================== + +.. rst-class:: table table-striped + +====================================== ================================================================================= +Directive Description +====================================== ================================================================================= +:ref:`-cancelAllRejections-` Adds a ``TransformationRejection`` to rejections from its inner Route, which + cancels other rejections according to a predicate function +:ref:`-cancelRejection-` Adds a ``TransformationRejection`` cancelling all rejections equal to a given one +:ref:`-clientIP-` Extracts the IP address of the client from either the ``X-Forwarded-For``, + ``Remote-Address`` or ``X-Real-IP`` request header +:ref:`-complete-` Completes the request with a given response, several overloads +:ref:`-compressResponse-` Compresses responses coming back from its inner Route using either ``Gzip`` or + ``Deflate`` unless the request explicitly sets ``Accept-Encoding`` to ``identity``. +:ref:`-compressResponseIfRequested-` Compresses responses coming back from its inner Route using either ``Gzip`` or + ``Deflate``, but only when the request explicitly accepts one of them. +:ref:`-conditional-` Depending on the given ETag and Last-Modified values responds with + ``304 Not Modified`` if the request comes with the respective conditional headers. +:ref:`-cookie-` Extracts an ``HttpCookie`` with a given name or rejects if no such cookie is + present in the request +:ref:`-decodeRequest-` Decompresses incoming requests using a given Decoder +:ref:`-decompressRequest-` Decompresses incoming requests using either ``Gzip``, ``Deflate``, or ``NoEncoding`` +:ref:`-delete-` Rejects all non-DELETE requests +:ref:`-deleteCookie-` Adds a ``Set-Cookie`` header expiring the given cookie to all ``HttpResponse`` + replies of its inner Route +:ref:`-encodeResponse-` Compresses responses coming back from its inner Route using a given Encoder +:ref:`-entity-` Unmarshalls the requests entity according to a given definition, rejects in + case of problems +:ref:`-extract-` Extracts a single value from the ``RequestContext`` using a function + ``RequestContext => T`` +:ref:`-extractRequest-` Extracts the complete request +: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:`-formField-` Extracts the value of an HTTP form field, rejects if the request doesn't come + with a field matching the definition +:ref:`-formFields-` Same as :ref:`-formField-`, except for several fields at once +:ref:`-get-` Rejects all non-GET requests +:ref:`-getFromBrowseableDirectories-` Same as :ref:`-getFromBrowseableDirectory-`, but allows for serving the "union" + of several directories as one single "virtual" one +:ref:`-getFromBrowseableDirectory-` Completes GET requests with the content of a file underneath a given directory, + renders directory contents as browsable listings +:ref:`-getFromDirectory-` Completes GET requests with the content of a file underneath a given directory +:ref:`-getFromFile-` Completes GET requests with the content of a given file +:ref:`-getFromResource-` Completes GET requests with the content of a given resource +:ref:`-getFromResourceDirectory-` Same as :ref:`-getFromDirectory-` except that the file is not fetched from the + file system but rather from a "resource directory" +:ref:`-handleExceptions-` Converts exceptions thrown during evaluation of its inner Route into + ``HttpResponse`` replies using a given ExceptionHandler +:ref:`-handleRejections-` Converts rejections produced by its inner Route into ``HttpResponse`` replies + using a given RejectionHandler +:ref:`-handleWith-` Completes the request using a given function. Uses the in-scope ``Unmarshaller`` + and ``Marshaller`` for converting to and from the function +:ref:`-head-` Rejects all non-HEAD requests +:ref:`-headerValue-` Extracts an HTTP header value using a given function, rejects if no value can + be extracted +:ref:`-headerValueByName-` Extracts an HTTP header value by selecting a header by name +:ref:`-headerValueByType-` Extracts an HTTP header value by selecting a header by type +:ref:`-headerValuePF-` Same as :ref:`-headerValue-`, but with a ``PartialFunction`` +:ref:`-host-` Rejects all requests with a hostname different from a given definition, + can extract the hostname using a regex pattern +:ref:`-hostName-` Extracts the hostname part of the requests ``Host`` header value +:ref:`-listDirectoryContents-` Completes GET requests with a unified listing of the contents of one or more + given directories +:ref:`-logRequest-` Produces a log entry for every incoming request +:ref:`-logRequestResult-` Produces a log entry for every response or rejection coming back from its inner + route, allowing for coalescing with the corresponding request +:ref:`-logResult-` Produces a log entry for every response or rejection coming back from its inner + route +:ref:`-mapResponse-` Transforms the ``HttpResponse`` coming back from its inner Route +:ref:`-mapResponseEntity-` Transforms the entity of the ``HttpResponse`` coming back from its inner Route +:ref:`-mapResponseHeaders-` Transforms the headers of the ``HttpResponse`` coming back from its inner Route +:ref:`-mapInnerRoute-` Transforms its inner Route with a ``Route => Route`` function +:ref:`-mapRejections-` Transforms all rejections coming back from its inner Route +:ref:`-mapRequest-` Transforms the incoming ``HttpRequest`` +:ref:`-mapRequestContext-` Transforms the ``RequestContext`` +:ref:`-mapRouteResult-` Transforms all responses coming back from its inner Route with a ``Any => Any`` + function +:ref:`-mapRouteResultPF-` Same as :ref:`-mapRouteResult-`, but with a ``PartialFunction`` +:ref:`-method-` Rejects if the request method does not match a given one +:ref:`-overrideMethodWithParameter-` Changes the HTTP method of the request to the value of the specified query string + parameter +:ref:`-onComplete-` "Unwraps" a ``Future[T]`` and runs its inner route after future completion with + the future's value as an extraction of type ``Try[T]`` +:ref:`-onFailure-` "Unwraps" a ``Future[T]`` and runs its inner route when the future has failed + with the future's failure exception as an extraction of type ``Throwable`` +:ref:`-onSuccess-` "Unwraps" a ``Future[T]`` and runs its inner route after future completion with + the future's value as an extraction of type ``T`` +:ref:`-optionalCookie-` Extracts an ``HttpCookie`` with a given name, if the cookie is not present in the + request extracts ``None`` +:ref:`-optionalHeaderValue-` Extracts an optional HTTP header value using a given function +:ref:`-optionalHeaderValueByName-` Extracts an optional HTTP header value by selecting a header by name +:ref:`-optionalHeaderValueByType-` Extracts an optional HTTP header value by selecting a header by type +:ref:`-optionalHeaderValuePF-` Extracts an optional HTTP header value using a given partial function +:ref:`-options-` Rejects all non-OPTIONS requests +:ref:`-parameter-` Extracts the value of a request query parameter, rejects if the request doesn't + come with a parameter matching the definition +:ref:`-parameterMap-` Extracts the requests query parameters as a ``Map[String, String]`` +:ref:`-parameterMultiMap-` Extracts the requests query parameters as a ``Map[String, List[String]]`` +:ref:`-parameters-` Same as :ref:`-parameter-`, except for several parameters at once +:ref:`-parameterSeq-` Extracts the requests query parameters as a ``Seq[(String, String)]`` +:ref:`-pass-` Does nothing, i.e. passes the ``RequestContext`` unchanged to its inner Route +:ref:`-patch-` Rejects all non-PATCH requests +:ref:`-path-` Extracts zero+ values from the ``unmatchedPath`` of the ``RequestContext`` + according to a given ``PathMatcher``, rejects if no match +:ref:`-pathEnd-` Only passes on the request to its inner route if the request path has been + matched completely, rejects otherwise +: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, rejects otherwise +:ref:`-pathPrefix-` Same as :ref:`-path-`, but also matches (and consumes) prefixes of the unmatched + path (rather than only the complete unmatched path at once) +:ref:`-pathPrefixTest-` Like :ref:`-pathPrefix-` but without "consumption" of the matched path (prefix). +:ref:`-pathSingleSlash-` Only passes on the request to its inner route if the request path consists of + exactly one remaining slash +:ref:`-pathSuffix-` Like as :ref:`-pathPrefix-`, but for suffixes rather than prefixed of the + unmatched path +:ref:`-pathSuffixTest-` Like :ref:`-pathSuffix-` but without "consumption" of the matched path (suffix). +:ref:`-post-` Rejects all non-POST requests +:ref:`-produce-` Uses the in-scope marshaller to extract a function that can be used for + completing the request with an instance of a custom type +:ref:`-provide-` Injects a single value into a directive, which provides it as an extraction +:ref:`-put-` Rejects all non-PUT requests +:ref:`-rawPathPrefix-` Applies a given ``PathMatcher`` directly to the unmatched path of the + ``RequestContext``, i.e. without implicitly consuming a leading slash +:ref:`-rawPathPrefixTest-` Checks whether the unmatchedPath of the ``RequestContext`` has a prefix matched + by a ``PathMatcher`` +:ref:`-redirect-` Completes the request with redirection response of the given type to a given URI +:ref:`-reject-` Rejects the request with a given set of rejections +:ref:`-rejectEmptyResponse-` Converts responses with an empty entity into a rejection +:ref:`-requestEncodedWith-` Rejects the request if its encoding doesn't match a given one +:ref:`-requestEntityEmpty-` Rejects the request if its entity is not empty +:ref:`-requestEntityPresent-` Rejects the request if its entity is empty +:ref:`-requestUri-` Extracts the complete request URI +:ref:`-respondWithHeader-` Adds a given response header to all ``HttpResponse`` replies from its inner + Route +:ref:`-respondWithHeaders-` Same as :ref:`-respondWithHeader-`, but for several headers at once +:ref:`-respondWithMediaType-` Overrides the media-type of all ``HttpResponse`` replies from its inner Route, + rejects if the media-type is not accepted by the client +:ref:`-respondWithSingletonHeader-` Adds a given response header to all ``HttpResponse`` replies from its inner + Route, if a header with the same name is not yet present +:ref:`-respondWithSingletonHeaders-` Same as :ref:`-respondWithSingletonHeader-`, but for several headers at once +:ref:`-respondWithStatus-` Overrides the response status of all ``HttpResponse`` replies coming back from + its inner Route +:ref:`-responseEncodingAccepted-` Rejects the request if the client doesn't accept a given encoding for the + response +:ref:`-mapUnmatchedPath-` Transforms the ``unmatchedPath`` of the ``RequestContext`` using a given function +:ref:`-scheme-` Rejects a request if its Uri scheme does not match a given one +:ref:`-schemeName-` Extracts the request Uri scheme +:ref:`-setCookie-` Adds a ``Set-Cookie`` header to all ``HttpResponse`` replies of its inner Route +:ref:`-textract-` Extracts a ``TupleN`` of values from the ``RequestContext`` using a function +:ref:`-hprovide-` Injects a ``TupleN`` of values into a directive, which provides them as + extractions +:ref:`-unmatchedPath-` Extracts the unmatched path from the RequestContext +:ref:`-validate-` Passes or rejects the request depending on evaluation of a given conditional + expression +:ref:`-withRangeSupport-` 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. +====================================== ================================================================================= diff --git a/akka-docs-dev/rst/scala/http/directives/basic-directives/extract.rst b/akka-docs-dev/rst/scala/http/directives/basic-directives/extract.rst new file mode 100644 index 0000000000..a8cbb2a056 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/basic-directives/extract.rst @@ -0,0 +1,27 @@ +.. _-extract-: + +extract +======= + +Calculates a value from the request context and provides the value to the inner route. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/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/server/directives/BasicDirectivesExamplesSpec.scala + :snippet: 0extract diff --git a/akka-docs-dev/rst/scala/http/directives/basic-directives/index.rst b/akka-docs-dev/rst/scala/http/directives/basic-directives/index.rst new file mode 100644 index 0000000000..cf2c3e309f --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/basic-directives/index.rst @@ -0,0 +1,80 @@ +.. _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: + +Directives to provide values to inner routes +-------------------------------------------- + +These directives allow to provide 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 an HList of values. + + * :ref:`-extract-` + * :ref:`-textract-` + * :ref:`-provide-` + * :ref:`-tprovide-` + +.. _Request Transforming Directives: + +Directives transforming the request +----------------------------------- + + * :ref:`-mapRequestContext-` + * :ref:`-mapRequest-` + +.. _Response Transforming Directives: + +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-` + * :ref:`-mapRejections-` + + +.. _Result Transformation Directives: + +Directives transforming the RouteResult +--------------------------------------- + +These directives allow to transform the RouteResult of the inner route. + + * :ref:`-mapRouteResult-` + * :ref:`-mapRouteResponsePF-` + +Directives changing the execution of the inner route +---------------------------------------------------- + + * :ref:`-mapInnerRoute-` + +Directives alphabetically +------------------------- + +.. toctree:: + :maxdepth: 1 + + extract + mapInnerRoute + mapRejections + mapRequest + mapRequestContext + mapResponse + mapResponseEntity + mapResponseHeaders + mapRouteResult + mapRouteResultPF + noop + pass + provide + textract + tprovide diff --git a/akka-docs-dev/rst/scala/http/directives/basic-directives/mapInnerRoute.rst b/akka-docs-dev/rst/scala/http/directives/basic-directives/mapInnerRoute.rst new file mode 100644 index 0000000000..8c98c93349 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/basic-directives/mapInnerRoute.rst @@ -0,0 +1,24 @@ +.. _-mapInnerRoute-: + +mapInnerRoute +============= + +Changes the execution model of the inner route by wrapping it with arbitrary logic. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala + :snippet: mapInnerRoute + +Description +----------- + +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/server/directives/BasicDirectivesExamplesSpec.scala + :snippet: mapInnerRoute diff --git a/akka-docs-dev/rst/scala/http/directives/basic-directives/mapRejections.rst b/akka-docs-dev/rst/scala/http/directives/basic-directives/mapRejections.rst new file mode 100644 index 0000000000..56a9c6ebbe --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/basic-directives/mapRejections.rst @@ -0,0 +1,26 @@ +.. _-mapRejections-: + +mapRejections +============= + +Transforms the list of rejections the inner route produced. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala + :snippet: mapRejections + +Description +----------- + +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/server/directives/BasicDirectivesExamplesSpec.scala + :snippet: mapRejections diff --git a/akka-docs-dev/rst/scala/http/directives/basic-directives/mapRequest.rst b/akka-docs-dev/rst/scala/http/directives/basic-directives/mapRequest.rst new file mode 100644 index 0000000000..f8fd0ba5fa --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/basic-directives/mapRequest.rst @@ -0,0 +1,28 @@ +.. _-mapRequest-: + +mapRequest +========== + +Transforms the request before it is handled by the inner route. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala + :snippet: mapRequest + +Description +----------- + +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/server/directives/BasicDirectivesExamplesSpec.scala + :snippet: 0mapRequest diff --git a/akka-docs-dev/rst/scala/http/directives/basic-directives/mapRequestContext.rst b/akka-docs-dev/rst/scala/http/directives/basic-directives/mapRequestContext.rst new file mode 100644 index 0000000000..ef431a7854 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/basic-directives/mapRequestContext.rst @@ -0,0 +1,27 @@ +.. _-mapRequestContext-: + +mapRequestContext +================= + +Transforms the ``RequestContext`` before it is passed to the inner route. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala + :snippet: mapRequestContext + +Description +----------- + +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/server/directives/BasicDirectivesExamplesSpec.scala + :snippet: mapRequestContext diff --git a/akka-docs-dev/rst/scala/http/directives/basic-directives/mapResponse.rst b/akka-docs-dev/rst/scala/http/directives/basic-directives/mapResponse.rst new file mode 100644 index 0000000000..91b20760c7 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/basic-directives/mapResponse.rst @@ -0,0 +1,27 @@ +.. _-mapResponse-: + +mapResponse +=============== + +Changes the response that was generated by the inner route. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/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 only complete responses. Use :ref:`-mapHttpResponsePart-`, +instead, to transform parts of chunked responses as well. + +See :ref:`Response Transforming Directives` for similar directives. + +Example +------- + +.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala + :snippet: 0mapResponse diff --git a/akka-docs-dev/rst/scala/http/directives/basic-directives/mapResponseEntity.rst b/akka-docs-dev/rst/scala/http/directives/basic-directives/mapResponseEntity.rst new file mode 100644 index 0000000000..279f463285 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/basic-directives/mapResponseEntity.rst @@ -0,0 +1,26 @@ +.. _-mapResponseEntity-: + +mapResponseEntity +===================== + +Changes the response entity that was generated by the inner route. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/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/server/directives/BasicDirectivesExamplesSpec.scala + :snippet: mapResponseEntity diff --git a/akka-docs-dev/rst/scala/http/directives/basic-directives/mapResponseHeaders.rst b/akka-docs-dev/rst/scala/http/directives/basic-directives/mapResponseHeaders.rst new file mode 100644 index 0000000000..975592cb81 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/basic-directives/mapResponseHeaders.rst @@ -0,0 +1,26 @@ +.. _-mapResponseHeaders-: + +mapResponseHeaders +====================== + +Changes the list of response headers that was generated by the inner route. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala + :snippet: mapResponseHeaders + +Description +----------- + +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/server/directives/BasicDirectivesExamplesSpec.scala + :snippet: mapResponseHeaders diff --git a/akka-docs-dev/rst/scala/http/directives/basic-directives/mapRouteResult.rst b/akka-docs-dev/rst/scala/http/directives/basic-directives/mapRouteResult.rst new file mode 100644 index 0000000000..86024af9f7 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/basic-directives/mapRouteResult.rst @@ -0,0 +1,26 @@ +.. _-mapRouteResult-: + +mapRouteResult +================ + +Changes the message the inner route sends to the responder. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala + :snippet: mapRouteResult + +Description +----------- + +The ``mapRouteResult`` directive is used as a building block for :ref:`Custom Directives` to transform what +the inner route sends to the responder (see :ref:`The Responder Chain`). + +See :ref:`Result Transformation Directives` for similar directives. + +Example +------- + +.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala + :snippet: 0mapRouteResult diff --git a/akka-docs-dev/rst/scala/http/directives/basic-directives/mapRouteResultPF.rst b/akka-docs-dev/rst/scala/http/directives/basic-directives/mapRouteResultPF.rst new file mode 100644 index 0000000000..e9229a0b36 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/basic-directives/mapRouteResultPF.rst @@ -0,0 +1,27 @@ +.. _-mapRouteResponsePF-: + +mapRouteResponsePF +================== + +Changes the message the inner route sends to the responder. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala + :snippet: mapRouteResponsePF + +Description +----------- + +The ``mapRouteResponsePF`` directive is used as a building block for :ref:`Custom Directives` to transform what +the inner route sends to the responder (see :ref:`The Responder Chain`). It's similar to the :ref:`-mapRouteResult-` +directive but allows to specify a partial function that doesn't have to handle all the incoming response messages. + +See :ref:`Result Transformation Directives` for similar directives. + +Example +------- + +.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala + :snippet: mapRouteResultPF diff --git a/akka-docs-dev/rst/scala/http/directives/basic-directives/pass.rst b/akka-docs-dev/rst/scala/http/directives/basic-directives/pass.rst new file mode 100644 index 0000000000..ac4a9c84ab --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/basic-directives/pass.rst @@ -0,0 +1,25 @@ +.. _-pass-: + +pass +==== + +A directive that passes the request unchanged to its inner route. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala + :snippet: pass + +Description +----------- + +The directive is usually used as a "neutral element" when combining directives generically. + + +Example +------- + +.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala + :snippet: pass + diff --git a/akka-docs-dev/rst/scala/http/directives/basic-directives/provide.rst b/akka-docs-dev/rst/scala/http/directives/basic-directives/provide.rst new file mode 100644 index 0000000000..5b0a986eee --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/basic-directives/provide.rst @@ -0,0 +1,26 @@ +.. _-provide-: + +provide +======= + +Provides a constant value to the inner route. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala + :snippet: provide + +Description +----------- + +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/server/directives/BasicDirectivesExamplesSpec.scala + :snippet: 0provide diff --git a/akka-docs-dev/rst/scala/http/directives/basic-directives/textract.rst b/akka-docs-dev/rst/scala/http/directives/basic-directives/textract.rst new file mode 100644 index 0000000000..118a0e7505 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/basic-directives/textract.rst @@ -0,0 +1,28 @@ +.. _-textract-: + +textract +======== + +Calculates a Tuple of values from the request context and provides them to the inner route. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala + :snippet: textract + +Description +----------- + +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. + + +Example +------- + +.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala + :snippet: textract diff --git a/akka-docs-dev/rst/scala/http/directives/basic-directives/tprovide.rst b/akka-docs-dev/rst/scala/http/directives/basic-directives/tprovide.rst new file mode 100644 index 0000000000..555deb9c63 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/basic-directives/tprovide.rst @@ -0,0 +1,28 @@ +.. _-tprovide-: + +tprovide +======== + +Provides an HList of values to the inner route. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala + :snippet: tprovide + +Description +----------- + +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. + + +Example +------- + +.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala + :snippet: tprovide diff --git a/akka-docs-dev/rst/scala/http/directives/by-trait.rst b/akka-docs-dev/rst/scala/http/directives/by-trait.rst new file mode 100644 index 0000000000..494aeec2bd --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/by-trait.rst @@ -0,0 +1,106 @@ +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. + + +.. _Response Directives: + +Directives creating or transforming the response +------------------------------------------------ + +:ref:`CacheConditionDirectives` + Support for conditional requests (``304 Not Modified`` responses). + +:ref:`ChunkingDirectives` + Automatically break a response into chunks. + +: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. + + +List of predefined directives by trait +-------------------------------------- + +.. toctree:: + :maxdepth: 1 + + basic-directives/index + cache-condition-directives/index + caching-directives/index + chunking-directives/index + coding-directives/index + cookie-directives/index + debugging-directives/index + execution-directives/index + file-and-resource-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 diff --git a/akka-docs-dev/rst/scala/http/directives/coding-directives/compressResponse.rst b/akka-docs-dev/rst/scala/http/directives/coding-directives/compressResponse.rst new file mode 100644 index 0000000000..1a5d74dfaa --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/coding-directives/compressResponse.rst @@ -0,0 +1,46 @@ +.. _-compressResponse-: + +compressResponse +================ + +Uses the first of a given number of encodings that the client accepts. If none are accepted the request +is rejected with an ``UnacceptedResponseEncodingRejection``. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/CodingDirectives.scala + :snippet: compressResponse + +Description +----------- + +The ``compressResponse`` directive allows to specify zero to three encoders to try in the specified order. +If none are specified the tried list is ``Gzip``, ``Deflate``, and then ``NoEncoding``. + +The ``compressResponse()`` directive (without an explicit list of encoders given) will therefore behave as follows: + +========================================= =============================== +``Accept-Encoding`` header resulting response +========================================= =============================== +``Accept-Encoding: gzip`` compressed with ``Gzip`` +``Accept-Encoding: deflate`` compressed with ``Deflate`` +``Accept-Encoding: deflate, gzip`` compressed with ``Gzip`` +``Accept-Encoding: identity`` uncompressed +no ``Accept-Encoding`` header present compressed with ``Gzip`` +========================================= =============================== + +For an overview of the different ``compressResponse`` directives see :ref:`WhenToUseWhichCompressResponseDirective`. + +Example +------- + +This example shows the behavior of ``compressResponse`` without any encoders specified: + +.. includecode2:: ../../../code/docs/http/server/directives/CodingDirectivesExamplesSpec.scala + :snippet: compressResponse-0 + +This example shows the behaviour of ``compressResponse(Gzip)``: + +.. includecode2:: ../../../code/docs/http/server/directives/CodingDirectivesExamplesSpec.scala + :snippet: compressResponse-1 diff --git a/akka-docs-dev/rst/scala/http/directives/coding-directives/compressResponseIfRequested.rst b/akka-docs-dev/rst/scala/http/directives/coding-directives/compressResponseIfRequested.rst new file mode 100644 index 0000000000..782842c0e5 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/coding-directives/compressResponseIfRequested.rst @@ -0,0 +1,37 @@ +.. _-compressResponseIfRequested-: + +compressResponseIfRequested +=========================== + +Only compresses the response when specifically requested by the ``Accept-Encoding`` request header +(i.e. the default is "no compression"). + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/CodingDirectives.scala + :snippet: compressResponseIfRequested + +Description +----------- + +The ``compressResponseIfRequested`` directive is an alias for ``compressResponse(NoEncoding, Gzip, Deflate)`` and will +behave as follows: + +========================================= =============================== +``Accept-Encoding`` header resulting response +========================================= =============================== +``Accept-Encoding: gzip`` compressed with ``Gzip`` +``Accept-Encoding: deflate`` compressed with ``Deflate`` +``Accept-Encoding: deflate, gzip`` compressed with ``Gzip`` +``Accept-Encoding: identity`` uncompressed +no ``Accept-Encoding`` header present uncompressed +========================================= =============================== + +For an overview of the different ``compressResponse`` directives see :ref:`WhenToUseWhichCompressResponseDirective`. + +Example +------- + +.. includecode2:: ../../../code/docs/http/server/directives/CodingDirectivesExamplesSpec.scala + :snippet: compressResponseIfRequested diff --git a/akka-docs-dev/rst/scala/http/directives/coding-directives/decodeRequest.rst b/akka-docs-dev/rst/scala/http/directives/coding-directives/decodeRequest.rst new file mode 100644 index 0000000000..b7af31d17d --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/coding-directives/decodeRequest.rst @@ -0,0 +1,30 @@ +.. _-decodeRequest-: + +decodeRequest +============= + +Tries to decode the request with the specified ``Decoder`` or rejects the request with an +``UnacceptedRequestEncodingRejection(supportedEncoding)``. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/CodingDirectives.scala + :snippet: decodeRequest + +Description +----------- + +The ``decodeRequest`` directive is the building block for the ``decompressRequest`` directive. + +``decodeRequest`` and ``decompressRequest`` are related like this:: + + decompressRequest(Gzip) = decodeRequest(Gzip) + decompressRequest(a, b, c) = decodeRequest(a) | decodeRequest(b) | decodeRequest(c) + decompressRequest() = decodeRequest(Gzip) | decodeRequest(Deflate) | decodeRequest(NoEncoding) + +Example +------- + +.. includecode2:: ../../../code/docs/http/server/directives/CodingDirectivesExamplesSpec.scala + :snippet: decodeRequest diff --git a/akka-docs-dev/rst/scala/http/directives/coding-directives/decompressRequest.rst b/akka-docs-dev/rst/scala/http/directives/coding-directives/decompressRequest.rst new file mode 100644 index 0000000000..051d5d8309 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/coding-directives/decompressRequest.rst @@ -0,0 +1,46 @@ +.. _-decompressRequest-: + +decompressRequest +================= + +Decompresses the request if it is can be decoded with one of the given decoders. Otherwise, +the request is rejected with an ``UnsupportedRequestEncodingRejection(supportedEncoding)``. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/CodingDirectives.scala + :snippet: decompressRequest + +Description +----------- + +The ``decompressRequest`` directive allows either to specify a list of decoders or none at all. If +no ``Decoder`` is specified ``Gzip``, ``Deflate``, or ``NoEncoding`` will be tried. + +The ``decompressRequest`` directive will behave as follows: + +========================================= =============================== +``Content-Encoding`` header resulting request +========================================= =============================== +``Content-Encoding: gzip`` decompressed +``Content-Encoding: deflate`` decompressed +``Content-Encoding: identity`` unchanged +no ``Content-Encoding`` header present unchanged +========================================= =============================== + +For an overview of the different ``decompressRequest`` directives and which one to use when, +see :ref:`WhenToUseWhichDecompressRequestDirective`. + +Example +------- + +This example shows the behavior of ``decompressRequest()`` without any decoders specified: + +.. includecode2:: ../../../code/docs/http/server/directives/CodingDirectivesExamplesSpec.scala + :snippet: decompressRequest-0 + +This example shows the behaviour of ``decompressRequest(Gzip, NoEncoding)``: + +.. includecode2:: ../../../code/docs/http/server/directives/CodingDirectivesExamplesSpec.scala + :snippet: decompressRequest-1 diff --git a/akka-docs-dev/rst/scala/http/directives/coding-directives/encodeResponse.rst b/akka-docs-dev/rst/scala/http/directives/coding-directives/encodeResponse.rst new file mode 100644 index 0000000000..9fa09027f0 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/coding-directives/encodeResponse.rst @@ -0,0 +1,38 @@ +.. _-encodeResponse-: + +encodeResponse +============== + +Tries to encode the response with the specified ``Encoder`` or rejects the request with an +``UnacceptedResponseEncodingRejection(supportedEncodings)``. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/CodingDirectives.scala + :snippet: encodeResponse + +Description +----------- + +The directive automatically applies the ``autoChunkFileBytes`` directive as well to avoid having to load +an entire file into JVM heap. + +The parameter to the directive is either just an ``Encoder`` or all of an ``Encoder``, a threshold, and a +chunk size to configure the automatically applied ``autoChunkFileBytes`` directive. + +The ``encodeResponse`` directive is the building block for the ``compressResponse`` and +``compressResponseIfRequested`` directives. + +``encodeResponse``, ``compressResponse``, and ``compressResponseIfRequested`` are related like this:: + + compressResponse(Gzip) = encodeResponse(Gzip) + compressResponse(a, b, c) = encodeResponse(a) | encodeResponse(b) | encodeResponse(c) + compressResponse() = encodeResponse(Gzip) | encodeResponse(Deflate) | encodeResponse(NoEncoding) + compressResponseIfRequested() = encodeResponse(NoEncoding) | encodeResponse(Gzip) | encodeResponse(Deflate) + +Example +------- + +.. includecode2:: ../../../code/docs/http/server/directives/CodingDirectivesExamplesSpec.scala + :snippet: encodeResponse diff --git a/akka-docs-dev/rst/scala/http/directives/coding-directives/index.rst b/akka-docs-dev/rst/scala/http/directives/coding-directives/index.rst new file mode 100644 index 0000000000..abdd9923f3 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/coding-directives/index.rst @@ -0,0 +1,64 @@ +.. _CodingDirectives: + +CodingDirectives +================ + +.. toctree:: + :maxdepth: 1 + + compressResponse + compressResponseIfRequested + decodeRequest + decompressRequest + encodeResponse + requestEncodedWith + responseEncodingAccepted + +.. _WhenToUseWhichCompressResponseDirective: + +When to use which compression directive? +---------------------------------------- + +There are three different directives for performing response compressing with slightly different behavior: + +:ref:`-encodeResponse-` + Always compresses the response with the one given encoding, rejects the request with an + ``UnacceptedResponseEncodingRejection`` if the client doesn't accept the given encoding. The other + compression directives are built upon this one. See its description for an overview how they + relate exactly. + +:ref:`-compressResponse-` + Uses the first of a given number of encodings that the client accepts. If none are accepted the request + is rejected. + +:ref:`-compressResponseIfRequested-` + Only compresses the response when specifically requested by the + ``Accept-Encoding`` request header (i.e. the default is "no compression"). + +See the individual directives for more detailed usage examples. + +.. _WhenToUseWhichDecompressRequestDirective: + +When to use which decompression directive? +------------------------------------------ + +There are two different directives for performing request decompressing with slightly different behavior: + +:ref:`-decodeRequest-` + Attempts to decompress the request using **the one given decoder**, rejects the request with an + ``UnsupportedRequestEncodingRejection`` if the request is not encoded with the given encoder. + +:ref:`-decompressRequest-` + Decompresses the request if it is encoded with **one of the given encoders**. + If the request's encoding doesn't match one of the given encoders it is rejected. + + +Combining compression and decompression +--------------------------------------- + +As with all Spray directives, the above single directives can be combined +using ``&`` to produce compound directives that will decompress requests and +compress responses in whatever combination required. Some examples: + +.. includecode2:: /../spray-routing-tests/src/test/scala/spray/routing/EncodingDirectivesSpec.scala + :snippet: decompress-compress-combination-example diff --git a/akka-docs-dev/rst/scala/http/directives/coding-directives/requestEncodedWith.rst b/akka-docs-dev/rst/scala/http/directives/coding-directives/requestEncodedWith.rst new file mode 100644 index 0000000000..7a544b89fe --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/coding-directives/requestEncodedWith.rst @@ -0,0 +1,18 @@ +.. _-requestEncodedWith-: + +requestEncodedWith +================== + +Passes the request to the inner route if the request is encoded with the argument encoding. Otherwise, +rejects the request with an ``UnacceptedRequestEncodingRejection(encoding)``. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/CodingDirectives.scala + :snippet: requestEncodedWith + +Description +----------- + +This directive is the building block for ``decodeRequest`` to reject unsupported encodings. diff --git a/akka-docs-dev/rst/scala/http/directives/coding-directives/responseEncodingAccepted.rst b/akka-docs-dev/rst/scala/http/directives/coding-directives/responseEncodingAccepted.rst new file mode 100644 index 0000000000..bf23437289 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/coding-directives/responseEncodingAccepted.rst @@ -0,0 +1,18 @@ +.. _-responseEncodingAccepted-: + +responseEncodingAccepted +======================== + +Passes the request to the inner route if the request accepts the argument encoding. Otherwise, +rejects the request with an ``UnacceptedResponseEncodingRejection(encoding)``. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/CodingDirectives.scala + :snippet: responseEncodingAccepted + +Description +----------- + +This directive is the building block for ``encodeResponse`` to reject unsupported encodings. diff --git a/akka-docs-dev/rst/scala/http/directives/cookie-directives/cookie.rst b/akka-docs-dev/rst/scala/http/directives/cookie-directives/cookie.rst new file mode 100644 index 0000000000..914d690775 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/cookie-directives/cookie.rst @@ -0,0 +1,25 @@ +.. _-cookie-: + +cookie +====== + +Extracts a cookie with a given name from a request or otherwise rejects the request with a ``MissingCookieRejection`` if +the cookie is missing. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/CookieDirectives.scala + :snippet: cookie + +Description +----------- + +Use the :ref:`-optionalCookie-` directive instead if you want to support missing cookies in your inner route. + + +Example +------- + +.. includecode2:: ../../../code/docs/http/server/directives/CookieDirectivesExamplesSpec.scala + :snippet: cookie diff --git a/akka-docs-dev/rst/scala/http/directives/cookie-directives/deleteCookie.rst b/akka-docs-dev/rst/scala/http/directives/cookie-directives/deleteCookie.rst new file mode 100644 index 0000000000..089786f0c7 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/cookie-directives/deleteCookie.rst @@ -0,0 +1,23 @@ +.. _-deleteCookie-: + +deleteCookie +============ + +Adds a header to the response to request the removal of the cookie with the given name on the client. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/CookieDirectives.scala + :snippet: deleteCookie + +Description +----------- + +Use the :ref:`-setCookie-` directive to update a cookie. + +Example +------- + +.. includecode2:: ../../../code/docs/http/server/directives/CookieDirectivesExamplesSpec.scala + :snippet: deleteCookie diff --git a/akka-docs-dev/rst/scala/http/directives/cookie-directives/index.rst b/akka-docs-dev/rst/scala/http/directives/cookie-directives/index.rst new file mode 100644 index 0000000000..7ca1aba3fd --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/cookie-directives/index.rst @@ -0,0 +1,12 @@ +.. _CookieDirectives: + +CookieDirectives +================ + +.. toctree:: + :maxdepth: 1 + + cookie + deleteCookie + optionalCookie + setCookie diff --git a/akka-docs-dev/rst/scala/http/directives/cookie-directives/optionalCookie.rst b/akka-docs-dev/rst/scala/http/directives/cookie-directives/optionalCookie.rst new file mode 100644 index 0000000000..150df7f963 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/cookie-directives/optionalCookie.rst @@ -0,0 +1,24 @@ +.. _-optionalCookie-: + +optionalCookie +============== + +Extracts an optional cookie with a given name from a request. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/CookieDirectives.scala + :snippet: optionalCookie + +Description +----------- + +Use the :ref:`-cookie-` directive instead if the inner route does not handle a missing cookie. + + +Example +------- + +.. includecode2:: ../../../code/docs/http/server/directives/CookieDirectivesExamplesSpec.scala + :snippet: optionalCookie diff --git a/akka-docs-dev/rst/scala/http/directives/cookie-directives/setCookie.rst b/akka-docs-dev/rst/scala/http/directives/cookie-directives/setCookie.rst new file mode 100644 index 0000000000..1b6e840fbe --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/cookie-directives/setCookie.rst @@ -0,0 +1,24 @@ +.. _-setCookie-: + +setCookie +========= + +Adds a header to the response to request the update of the cookie with the given name on the client. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/CookieDirectives.scala + :snippet: setCookie + +Description +----------- + +Use the :ref:`-deleteCookie-` directive to delete a cookie. + + +Example +------- + +.. includecode2:: ../../../code/docs/http/server/directives/CookieDirectivesExamplesSpec.scala + :snippet: setCookie diff --git a/akka-docs-dev/rst/scala/http/directives/debugging-directives/index.rst b/akka-docs-dev/rst/scala/http/directives/debugging-directives/index.rst new file mode 100644 index 0000000000..cec8a165e4 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/debugging-directives/index.rst @@ -0,0 +1,11 @@ +.. _DebuggingDirectives: + +DebuggingDirectives +=================== + +.. toctree:: + :maxdepth: 1 + + logRequest + logRequestResult + logResult diff --git a/akka-docs-dev/rst/scala/http/directives/debugging-directives/logRequest.rst b/akka-docs-dev/rst/scala/http/directives/debugging-directives/logRequest.rst new file mode 100644 index 0000000000..954340c36d --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/debugging-directives/logRequest.rst @@ -0,0 +1,46 @@ +.. _-logRequest-: + +logRequest +========== + +Logs the request. + +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`: /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/server/directives/DebuggingDirectivesExamplesSpec.scala + :snippet: logRequest-0 diff --git a/akka-docs-dev/rst/scala/http/directives/debugging-directives/logRequestResult.rst b/akka-docs-dev/rst/scala/http/directives/debugging-directives/logRequestResult.rst new file mode 100644 index 0000000000..4ff072501b --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/debugging-directives/logRequestResult.rst @@ -0,0 +1,34 @@ +.. _-logRequestResult-: + +logRequestResult +================== + +Logs request and response. + +Signature +--------- + +:: + + def logRequestResult(marker: String)(implicit log: LoggingContext): Directive0 + def logRequestResult(marker: String, level: LogLevel)(implicit log: LoggingContext): Directive0 + def logRequestResult(show: HttpRequest ⇒ HttpResponsePart ⇒ Option[LogEntry]) + (implicit log: LoggingContext): Directive0 + def logRequestResult(show: HttpRequest ⇒ Any ⇒ 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`: /blog/2012-12-13-the-magnet-pattern/ + +Description +----------- + +This directive is a combination of ``logRequest`` and ``logResult``. See ``logRequest`` for the general description +how these directives work. + +Example +------- + +.. includecode2:: ../../../code/docs/http/server/directives/DebuggingDirectivesExamplesSpec.scala + :snippet: logRequestResult diff --git a/akka-docs-dev/rst/scala/http/directives/debugging-directives/logResult.rst b/akka-docs-dev/rst/scala/http/directives/debugging-directives/logResult.rst new file mode 100644 index 0000000000..775b16a804 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/debugging-directives/logResult.rst @@ -0,0 +1,38 @@ +.. _-logResult-: + +logResult +=========== + +Logs the response. + +Signature +--------- + +:: + + def logResult(marker: String)(implicit log: LoggingContext): Directive0 + def logResult(marker: String, level: LogLevel)(implicit log: LoggingContext): Directive0 + def logResult(show: Any => String)(implicit log: LoggingContext): Directive0 + def logResult(show: Any => LogEntry)(implicit log: LoggingContext): Directive0 + def logResult(magnet: LoggingMagnet[Any => 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`: /blog/2012-12-13-the-magnet-pattern/ + +Description +----------- + +See ``logRequest`` for the general description how these directives work. This directive is different +as it requires a ``LoggingMagnet[Any => Unit]``. Instead of just logging ``HttpResponses``, ``logResult`` is able to +log anything passing through :ref:`The Responder Chain` (which can either be a ``HttpResponsePart`` or a ``Rejected`` +message reporting rejections). + +Use ``logRequest`` for logging the request, or ``logRequestResult`` for logging both. + +Example +------- + +.. includecode2:: ../../../code/docs/http/server/directives/DebuggingDirectivesExamplesSpec.scala + :snippet: logResult diff --git a/akka-docs-dev/rst/scala/http/directives/execution-directives/handleExceptions.rst b/akka-docs-dev/rst/scala/http/directives/execution-directives/handleExceptions.rst new file mode 100644 index 0000000000..8d973df767 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/execution-directives/handleExceptions.rst @@ -0,0 +1,26 @@ +.. _-handleExceptions-: + +handleExceptions +================ + +Catches exceptions thrown by the inner route and handles them using the specified ``ExceptionHandler``. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/ExecutionDirectives.scala + :snippet: handleExceptions + +Description +----------- + +Using this directive is an alternative to using a global implicitly defined ``ExceptionHandler`` that +applies to the complete route. + +See :ref:`Exception Handling` for general information about options for handling exceptions. + +Example +------- + +.. includecode2:: ../../../code/docs/http/server/directives/ExecutionDirectivesExamplesSpec.scala + :snippet: handleExceptions diff --git a/akka-docs-dev/rst/scala/http/directives/execution-directives/handleRejections.rst b/akka-docs-dev/rst/scala/http/directives/execution-directives/handleRejections.rst new file mode 100644 index 0000000000..4494b360cd --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/execution-directives/handleRejections.rst @@ -0,0 +1,26 @@ +.. _-handleRejections-: + +handleRejections +================ + +Handles rejections produced by the inner route and handles them using the specified ``RejectionHandler``. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/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` for general information about options for handling rejections. + +Example +------- + +.. includecode2:: ../../../code/docs/http/server/directives/ExecutionDirectivesExamplesSpec.scala + :snippet: handleRejections diff --git a/akka-docs-dev/rst/scala/http/directives/execution-directives/index.rst b/akka-docs-dev/rst/scala/http/directives/execution-directives/index.rst new file mode 100644 index 0000000000..facc98d2c4 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/execution-directives/index.rst @@ -0,0 +1,10 @@ +.. _ExecutionDirectives: + +ExecutionDirectives +=================== + +.. toctree:: + :maxdepth: 1 + + handleExceptions + handleRejections diff --git a/akka-docs-dev/rst/scala/http/directives/file-and-resource-directives/getFromBrowseableDirectories.rst b/akka-docs-dev/rst/scala/http/directives/file-and-resource-directives/getFromBrowseableDirectories.rst new file mode 100644 index 0000000000..50e931116d --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/file-and-resource-directives/getFromBrowseableDirectories.rst @@ -0,0 +1,23 @@ +.. _-getFromBrowseableDirectories-: + +getFromBrowseableDirectories +============================ + +Serves the content of the given directories as a file system browser, i.e. files are sent and directories +served as browsable listings. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/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. diff --git a/akka-docs-dev/rst/scala/http/directives/file-and-resource-directives/getFromBrowseableDirectory.rst b/akka-docs-dev/rst/scala/http/directives/file-and-resource-directives/getFromBrowseableDirectory.rst new file mode 100644 index 0000000000..447870c416 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/file-and-resource-directives/getFromBrowseableDirectory.rst @@ -0,0 +1,13 @@ +.. _-getFromBrowseableDirectory-: + +getFromBrowseableDirectory +========================== + +The single-directory variant of :ref:`-getFromBrowseableDirectories-`. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/FileAndResourceDirectives.scala + :snippet: getFromBrowseableDirectory + diff --git a/akka-docs-dev/rst/scala/http/directives/file-and-resource-directives/getFromDirectory.rst b/akka-docs-dev/rst/scala/http/directives/file-and-resource-directives/getFromDirectory.rst new file mode 100644 index 0000000000..b2ce609b96 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/file-and-resource-directives/getFromDirectory.rst @@ -0,0 +1,26 @@ +.. _-getFromDirectory-: + +getFromDirectory +================ + +Completes GET requests with the content of a file underneath the given directory. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/FileAndResourceDirectives.scala + :snippet: getFromDirectory + +Description +----------- + +The ``unmatchedPath`` 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. + +The actual I/O operation is running detached in a `Future`, so it doesn't block the current thread. If the file cannot +be read the route rejects the request. + +To serve a single file use ``getFromFile``. To serve browsable directory listings use ``getFromBrowseableDirectories``. +To serve files from a classpath directory use ``getFromResourceDirectory`` instead. + +Note that it's not required to wrap this directive with ``get`` as this directive will only respond to ``GET`` requests. diff --git a/akka-docs-dev/rst/scala/http/directives/file-and-resource-directives/getFromFile.rst b/akka-docs-dev/rst/scala/http/directives/file-and-resource-directives/getFromFile.rst new file mode 100644 index 0000000000..a207e8aa18 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/file-and-resource-directives/getFromFile.rst @@ -0,0 +1,23 @@ +.. _-getFromFile-: + +getFromFile +=========== + +Completes GET requests with the content of the given file. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/FileAndResourceDirectives.scala + :snippet: getFromFile + +Description +----------- + +The actual I/O operation is running detached in a `Future`, so it doesn't block the current thread (but potentially +some other thread !). If the file cannot be found or read the request is rejected. + +To serve files from a directory use ``getFromDirectory``, instead. To serve a file from a classpath resource +use ``getFromResource`` instead. + +Note that it's not required to wrap this directive with ``get`` as this directive will only respond to ``GET`` requests. \ No newline at end of file diff --git a/akka-docs-dev/rst/scala/http/directives/file-and-resource-directives/getFromResource.rst b/akka-docs-dev/rst/scala/http/directives/file-and-resource-directives/getFromResource.rst new file mode 100644 index 0000000000..05dc013553 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/file-and-resource-directives/getFromResource.rst @@ -0,0 +1,23 @@ +.. _-getFromResource-: + +getFromResource +=============== + +Completes GET requests with the content of the given classpath resource. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/FileAndResourceDirectives.scala + :snippet: getFromResource + +Description +----------- + +The actual I/O operation is running detached in a `Future`, so it doesn't block the current thread (but potentially +some other thread !). If the file cannot be found or read the request is rejected. + +To serve files from a classpath directory use ``getFromResourceDirectory`` instead. To serve files from a filesystem +directory use ``getFromDirectory``, instead. + +Note that it's not required to wrap this directive with ``get`` as this directive will only respond to ``GET`` requests. \ No newline at end of file diff --git a/akka-docs-dev/rst/scala/http/directives/file-and-resource-directives/getFromResourceDirectory.rst b/akka-docs-dev/rst/scala/http/directives/file-and-resource-directives/getFromResourceDirectory.rst new file mode 100644 index 0000000000..e6d0cc9a71 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/file-and-resource-directives/getFromResourceDirectory.rst @@ -0,0 +1,23 @@ +.. _-getFromResourceDirectory-: + +getFromResourceDirectory +======================== + +Completes GET requests with the content of the given classpath resource directory. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/FileAndResourceDirectives.scala + :snippet: getFromResourceDirectory + +Description +----------- + +The actual I/O operation is running detached in a `Future`, so it doesn't block the current thread (but potentially +some other thread !). If the file cannot be found or read the request is rejected. + +To serve a single resource use ``getFromResource``, instead. To server files from a filesystem directory use +``getFromDirectory`` instead. + +Note that it's not required to wrap this directive with ``get`` as this directive will only respond to ``GET`` requests. \ No newline at end of file diff --git a/akka-docs-dev/rst/scala/http/directives/file-and-resource-directives/index.rst b/akka-docs-dev/rst/scala/http/directives/file-and-resource-directives/index.rst new file mode 100644 index 0000000000..fc4b27927c --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/file-and-resource-directives/index.rst @@ -0,0 +1,24 @@ +.. _FileAndResourceDirectives: + +FileAndResourceDirectives +========================= + +Like the :ref:`RouteDirectives` the ``FileAndResourceDirectives`` are somewhat special in spray'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-dev/rst/scala/http/directives/file-and-resource-directives/listDirectoryContents.rst b/akka-docs-dev/rst/scala/http/directives/file-and-resource-directives/listDirectoryContents.rst new file mode 100644 index 0000000000..a7987ed69b --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/file-and-resource-directives/listDirectoryContents.rst @@ -0,0 +1,24 @@ +.. _-listDirectoryContents-: + +listDirectoryContents +===================== + +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]`. + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/FileAndResourceDirectives.scala + :snippet: listDirectoryContents + +Description +----------- + +The ``listDirectoryContents`` directive renders a response only for directories. To just serve files use +``getFromDirectory``. To serve files and provide a browseable directory listing use ``getFromBrowsableDirectories`` +instead. + +The rendering can be overridden by providing a custom ``Marshaller[DirectoryListing]``. + +Note that it's not required to wrap this directive with ``get`` as this directive will only respond to ``GET`` requests. \ No newline at end of file diff --git a/akka-docs-dev/rst/scala/http/directives/path-directives/index.rst b/akka-docs-dev/rst/scala/http/directives/path-directives/index.rst new file mode 100644 index 0000000000..443f710c96 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/path-directives/index.rst @@ -0,0 +1,20 @@ +.. _PathDirectives: + +PathDirectives +============== + +.. toctree:: + :maxdepth: 1 + + path + pathEnd + pathEndOrSingleSlash + pathPrefix + pathPrefixTest + pathSingleSlash + pathSuffix + pathSuffixTest + rawPathPrefix + rawPathPrefixTest + + ../../path-matchers diff --git a/akka-docs-dev/rst/scala/http/directives/path-directives/path.rst b/akka-docs-dev/rst/scala/http/directives/path-directives/path.rst new file mode 100644 index 0000000000..98e3b505ca --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/path-directives/path.rst @@ -0,0 +1,41 @@ +.. _-path-: + +path +==== + +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). + + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/PathDirectives.scala + :snippet: path + + +Description +----------- + +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 `. + + +Example +------- + +.. includecode2:: ../../../code/docs/http/server/directives/PathDirectivesExamplesSpec.scala + :snippet: path-example diff --git a/akka-docs-dev/rst/scala/http/directives/path-directives/pathEnd.rst b/akka-docs-dev/rst/scala/http/directives/path-directives/pathEnd.rst new file mode 100644 index 0000000000..984784f61e --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/path-directives/pathEnd.rst @@ -0,0 +1,28 @@ +.. _-pathEnd-: + +pathEnd +======= + +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. + + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/PathDirectives.scala + :snippet: pathEnd + + +Description +----------- + +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/server/directives/PathDirectivesExamplesSpec.scala + :snippet: pathEnd- \ No newline at end of file diff --git a/akka-docs-dev/rst/scala/http/directives/path-directives/pathEndOrSingleSlash.rst b/akka-docs-dev/rst/scala/http/directives/path-directives/pathEndOrSingleSlash.rst new file mode 100644 index 0000000000..5101afbeec --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/path-directives/pathEndOrSingleSlash.rst @@ -0,0 +1,30 @@ +.. _-pathEndOrSingleSlash-: + +pathEndOrSingleSlash +==================== + +Only passes the request to its inner route if the unmatched path of the ``RequestContext`` is either empty +or contains only one single slash. + + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/PathDirectives.scala + :snippet: pathEndOrSingleSlash + + +Description +----------- + +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/server/directives/PathDirectivesExamplesSpec.scala + :snippet: pathEndOrSingleSlash- \ No newline at end of file diff --git a/akka-docs-dev/rst/scala/http/directives/path-directives/pathPrefix.rst b/akka-docs-dev/rst/scala/http/directives/path-directives/pathPrefix.rst new file mode 100644 index 0000000000..02f0a40f4e --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/path-directives/pathPrefix.rst @@ -0,0 +1,35 @@ +.. _-pathPrefix-: + +pathPrefix +========== + +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). + + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/PathDirectives.scala + :snippet: pathPrefix + + +Description +----------- + +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 `. + + +Example +------- + +.. includecode2:: ../../../code/docs/http/server/directives/PathDirectivesExamplesSpec.scala + :snippet: pathPrefix- \ No newline at end of file diff --git a/akka-docs-dev/rst/scala/http/directives/path-directives/pathPrefixTest.rst b/akka-docs-dev/rst/scala/http/directives/path-directives/pathPrefixTest.rst new file mode 100644 index 0000000000..e8b8018e4d --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/path-directives/pathPrefixTest.rst @@ -0,0 +1,38 @@ +.. _-pathPrefixTest-: + +pathPrefixTest +============== + +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. + + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/PathDirectives.scala + :snippet: pathPrefixTest + + +Description +----------- + +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/server/directives/PathDirectivesExamplesSpec.scala + :snippet: pathPrefixTest- \ No newline at end of file diff --git a/akka-docs-dev/rst/scala/http/directives/path-directives/pathSingleSlash.rst b/akka-docs-dev/rst/scala/http/directives/path-directives/pathSingleSlash.rst new file mode 100644 index 0000000000..552f4709ea --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/path-directives/pathSingleSlash.rst @@ -0,0 +1,28 @@ +.. _-pathSingleSlash-: + +pathSingleSlash +=============== + +Only passes the request to its inner route if the unmatched path of the ``RequestContext`` +contains exactly one single slash. + + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/PathDirectives.scala + :snippet: pathSingleSlash + + +Description +----------- + +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/server/directives/PathDirectivesExamplesSpec.scala + :snippet: pathSingleSlash- \ No newline at end of file diff --git a/akka-docs-dev/rst/scala/http/directives/path-directives/pathSuffix.rst b/akka-docs-dev/rst/scala/http/directives/path-directives/pathSuffix.rst new file mode 100644 index 0000000000..5b435214f9 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/path-directives/pathSuffix.rst @@ -0,0 +1,38 @@ +.. _-pathSuffix-: + +pathSuffix +========== + +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). + + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/PathDirectives.scala + :snippet: pathSuffix + + +Description +----------- + +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/server/directives/PathDirectivesExamplesSpec.scala + :snippet: pathSuffix- \ No newline at end of file diff --git a/akka-docs-dev/rst/scala/http/directives/path-directives/pathSuffixTest.rst b/akka-docs-dev/rst/scala/http/directives/path-directives/pathSuffixTest.rst new file mode 100644 index 0000000000..93c169aebf --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/path-directives/pathSuffixTest.rst @@ -0,0 +1,39 @@ +.. _-pathSuffixTest-: + +pathSuffixTest +============== + +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. + + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/PathDirectives.scala + :snippet: pathSuffixTest + + +Description +----------- + +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/server/directives/PathDirectivesExamplesSpec.scala + :snippet: pathSuffixTest- \ No newline at end of file diff --git a/akka-docs-dev/rst/scala/http/directives/path-directives/rawPathPrefix.rst b/akka-docs-dev/rst/scala/http/directives/path-directives/rawPathPrefix.rst new file mode 100644 index 0000000000..3b04a5fdd9 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/path-directives/rawPathPrefix.rst @@ -0,0 +1,35 @@ +.. _-rawPathPrefix-: + +rawPathPrefix +============= + +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). + + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/PathDirectives.scala + :snippet: rawPathPrefix + + +Description +----------- + +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/server/directives/PathDirectivesExamplesSpec.scala + :snippet: rawPathPrefix- \ No newline at end of file diff --git a/akka-docs-dev/rst/scala/http/directives/path-directives/rawPathPrefixTest.rst b/akka-docs-dev/rst/scala/http/directives/path-directives/rawPathPrefixTest.rst new file mode 100644 index 0000000000..2bfa51190b --- /dev/null +++ b/akka-docs-dev/rst/scala/http/directives/path-directives/rawPathPrefixTest.rst @@ -0,0 +1,38 @@ +.. _-rawPathPrefixTest-: + +rawPathPrefixTest +================= + +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. + + +Signature +--------- + +.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/PathDirectives.scala + :snippet: rawPathPrefixTest + + +Description +----------- + +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/server/directives/PathDirectivesExamplesSpec.scala + :snippet: rawPathPrefixTest- \ No newline at end of file diff --git a/akka-docs-dev/rst/scala/http/exception-handling.rst b/akka-docs-dev/rst/scala/http/exception-handling.rst new file mode 100644 index 0000000000..80269bd24c --- /dev/null +++ b/akka-docs-dev/rst/scala/http/exception-handling.rst @@ -0,0 +1,31 @@ +.. _Exception Handling: + +Exception Handling +================== + +Exceptions thrown during route execution bubble up through the route structure to the next enclosing +:ref:`-handleExceptions-` directive, ``Route.seal`` or the ``onFailure`` callback of a +future created by ``detach``. + +Similarly to the way that :ref:`Rejections` are handled the :ref:`-handleExceptions-` directive delegates the actual job of +converting a list of rejections to its argument, an ExceptionHandler__, which is defined like this:: + + trait ExceptionHandler extends PartialFunction[Throwable, Route] + +__ @github@/akka-http/src/main/scala/akka/http/server/ExceptionHandler.scala + +:ref:`runRoute` defined in :ref:`HttpService` does the same but gets its ``ExceptionHandler`` instance +implicitly. + +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. The top-most +``ExceptionHandler`` applied by :ref:`runRoute` will handle *all* exceptions that reach it. + +So, if you'd like to customize the way certain exceptions are handled simply bring a custom ``ExceptionHandler`` into +implicit scope of :ref:`runRoute` or pass it to an explicit :ref:`-handleExceptions-` directive that you +have put somewhere into your route structure. + +Here is an example: + +.. includecode2:: ../code/docs/http/server/ExceptionHandlerExamplesSpec.scala + :snippet: example-1 diff --git a/akka-docs-dev/rst/scala/http/index.rst b/akka-docs-dev/rst/scala/http/index.rst index d46a5c6dab..30ec9e2fd6 100644 --- a/akka-docs-dev/rst/scala/http/index.rst +++ b/akka-docs-dev/rst/scala/http/index.rst @@ -6,9 +6,12 @@ High-level HTTP API quick-start routing + directives + rejections + exception-handling + path-matchers marshalling - custom-directives - predefined-directives-alphabetically - predefined-directives-by-trait + directives/alphabetically + directives/by-trait diff --git a/akka-docs-dev/rst/scala/http/path-matchers.rst b/akka-docs-dev/rst/scala/http/path-matchers.rst new file mode 100644 index 0000000000..7ac98c2cb2 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/path-matchers.rst @@ -0,0 +1,200 @@ +.. _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-`/: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/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. + +``Rest: 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 ``RestPath`` instead. + +``RestPath: 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 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 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: + + .. rst-class:: table table-striped + + =========================== ============================= + 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: + + .. rst-class:: table table-striped + + =========================== ======================================= + 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/server/directives/PathDirectivesExamplesSpec.scala + :snippet: path-dsl \ No newline at end of file diff --git a/akka-docs-dev/rst/scala/http/predefined-directives-alphabetically.rst b/akka-docs-dev/rst/scala/http/predefined-directives-alphabetically.rst deleted file mode 100644 index 45627cfb53..0000000000 --- a/akka-docs-dev/rst/scala/http/predefined-directives-alphabetically.rst +++ /dev/null @@ -1,4 +0,0 @@ -Predefined Directives (alphabetically) -====================================== - -(todo) \ No newline at end of file diff --git a/akka-docs-dev/rst/scala/http/predefined-directives-by-trait.rst b/akka-docs-dev/rst/scala/http/predefined-directives-by-trait.rst deleted file mode 100644 index 9b03057e89..0000000000 --- a/akka-docs-dev/rst/scala/http/predefined-directives-by-trait.rst +++ /dev/null @@ -1,4 +0,0 @@ -Predefined Directives (by trait) -================================ - -(todo) \ No newline at end of file diff --git a/akka-docs-dev/rst/scala/http/rejections.rst b/akka-docs-dev/rst/scala/http/rejections.rst new file mode 100644 index 0000000000..6d8cbebec8 --- /dev/null +++ b/akka-docs-dev/rst/scala/http/rejections.rst @@ -0,0 +1,100 @@ +.. _Rejections: + +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 various +: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/server/Rejection.scala + + +.. _RejectionHandler: + +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 PartialFunction[List[Rejection], Route] + +__ @github@/akka-http/src/main/scala/akka/http/server/RejectionHandler.scala + +Since a ``RejectionHandler`` is a partial function it can choose, which rejections it would like to handle and +which not. Unhandled rejections will simply continue to flow through the route structure. The top-most +``RejectionHandler`` applied by :ref:`runRoute` will handle *all* rejections that reach it. + +So, if you'd like to customize the way certain rejections are handled simply bring a custom ``RejectionHandler`` into +implicit scope of :ref:`runRoute` or pass it to an explicit :ref:`-handleRejections-` directive that you +have put somewhere into your route structure. + +Here is an example: + +.. includecode2:: ../code/docs/http/server/RejectionHandlerExamplesSpec.scala + :snippet: example-1 + + +Rejection Cancellation +---------------------- + +As you can see from its definition above the ``RejectionHandler`` handles not 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/server/RejectionHandlerExamplesSpec.scala + :snippet: example-2 + +For uncompressed POST requests this route structure could 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:`-decodeRequest-` directive (which only accepts + gzip-compressed requests) + +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 lists 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. diff --git a/akka-docs-dev/rst/scala/http/routing.rst b/akka-docs-dev/rst/scala/http/routing.rst index 64754a3677..f2e32f05e1 100644 --- a/akka-docs-dev/rst/scala/http/routing.rst +++ b/akka-docs-dev/rst/scala/http/routing.rst @@ -1,6 +1,105 @@ .. _http-routing-scala: -Routing DSL -=========== +The Routing DSL +=============== + +.. _Routes: + +Routes +------ + +The "Route" is the central concept of the routing DSL since all structures you can build with it are instances of +a ``Route``. The type Route is defined like this:: + + type Route = RequestContext ⇒ Future[RouteResult] + +It's a simple alias for a function taking a ``RequestContext`` as parameter and returning 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`) +- Fail the request by returning the value of ``requestContext.fail(...)`` or by just throwing an exception (see :ref:`Exception Handling`) +- Do any kind of asynchronous processing and instantly return a ``Future[RouteResult]`` to be eventually completed later on + +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`` and by supplying a ``RejectionHandler`` and an ``ExceptionHandler`` which +converts rejection results and exceptional results into appropriate HTTP responses for the peer. + +A ``Route`` can be lifted into a handler to be used with the http-core API using ``Route.handlerFlow`` or +``Route.asyncHandler``. + +.. _RequestContext: + +RequestContext +-------------- + +The request context wraps a request together with additional contextual information to be passed through the route tree. +Also, it provides the only way of creating a ``RouteResult`` by calling one of the above methods. + +In addition to the request it contains the ``unmatchedPath``, a value that describes how much of the request URI has not +yet been matched and instances of several configuration instances like a ``LoggingAdapter``, an ``ExecutionContext``, and +``RoutingSettings``, so that they don't have to be passed around explicitly. + +The ``RequestContext`` itself is immutable but contains several helper methods to create updated versions. + +Composing Routes +---------------- + +There are three basic operations we need for building more complex routes from simpler ones: + +.. rst-class:: wide + +- 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 provided by an implicit in +``RouteConcatenation``. 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. + +.. rst-class:: wide + +- 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. + -(todo) \ No newline at end of file