diff --git a/akka-docs-dev/rst/java/code/docs/http/javadsl/server/directives/HostDirectivesExamplesTest.java b/akka-docs-dev/rst/java/code/docs/http/javadsl/server/directives/HostDirectivesExamplesTest.java new file mode 100644 index 0000000000..a192ed46cb --- /dev/null +++ b/akka-docs-dev/rst/java/code/docs/http/javadsl/server/directives/HostDirectivesExamplesTest.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2015 Typesafe Inc. + */ + +package docs.http.javadsl.server.directives; + +import java.util.Arrays; +import java.util.regex.Pattern; + +import akka.http.javadsl.model.HttpMethod; +import akka.http.javadsl.model.HttpRequest; +import akka.http.javadsl.model.StatusCodes; +import akka.http.javadsl.model.headers.Host; +import akka.http.javadsl.server.*; +import akka.http.javadsl.testkit.JUnitRouteTest; + +import org.junit.Test; + +public class HostDirectivesExamplesTest extends JUnitRouteTest { + + //FIXME The GET requests should work with HttpRequest.GET("/").addHeader(Host.create + // instead of absolute paths. That is tracked by issue: https://github.com/akka/akka/issues/18661 + + @Test + public void testListOfHost() { + //#host1 + final Route matchListOfHosts = host( + Arrays.asList("api.company.com", "rest.company.com"), + completeWithStatus(StatusCodes.OK)); + + testRoute(matchListOfHosts).run(HttpRequest.GET("http://api.company.com/")) + .assertStatusCode(StatusCodes.OK); + //#host1 + } + + @Test + public void testHostPredicate() { + //#host2 + final Route shortOnly = host(hostname -> hostname.length() < 10, + completeWithStatus(StatusCodes.OK)); + + testRoute(shortOnly).run(HttpRequest.GET("http://short.com/")) + .assertStatusCode(StatusCodes.OK); + + testRoute(shortOnly).run(HttpRequest.GET("http://verylonghostname.com/")) + .assertStatusCode(StatusCodes.NOT_FOUND); + //#host2 + } + + @Test + public void testExtractHost() { + //#extractHostname + final RequestVal host = RequestVals.host(); + + final Route route = handleWith1(host, + (ctx, hn) -> ctx.complete("Hostname: " + hn)); + + testRoute(route).run(HttpRequest.GET("http://company.com:9090/")) + .assertEntity("Hostname: company.com"); + //#extractHostname + } + + @Test + public void testMatchAndExtractHost() { + //#matchAndExtractHost + final RequestVal hostPrefix = RequestVals + .matchAndExtractHost(Pattern.compile("api|rest")); + + final Route hostPrefixRoute = handleWith1(hostPrefix, + (ctx, prefix) -> ctx.complete("Extracted prefix: " + prefix)); + + final RequestVal hostPart = RequestVals.matchAndExtractHost(Pattern + .compile("public.(my|your)company.com")); + + final Route hostPartRoute = handleWith1( + hostPart, + (ctx, captured) -> ctx.complete("You came through " + captured + + " company")); + + final Route route = route(hostPrefixRoute, hostPartRoute); + + testRoute(route).run(HttpRequest.GET("http://api.company.com/")) + .assertStatusCode(StatusCodes.OK).assertEntity("Extracted prefix: api"); + + testRoute(route).run(HttpRequest.GET("http://public.mycompany.com/")) + .assertStatusCode(StatusCodes.OK) + .assertEntity("You came through my company"); + //#matchAndExtractHost + } + + @SuppressWarnings("unused") + @Test(expected = IllegalArgumentException.class) + public void testFailingMatchAndExtractHost() { + //#failing-matchAndExtractHost + // this will throw IllegalArgumentException + final RequestVal hostRegex = RequestVals + .matchAndExtractHost(Pattern + .compile("server-([0-9]).company.(com|net|org)")); + //#failing-matchAndExtractHost + } + +} diff --git a/akka-docs-dev/rst/java/code/docs/http/javadsl/server/directives/MethodDirectivesExamplesTest.java b/akka-docs-dev/rst/java/code/docs/http/javadsl/server/directives/MethodDirectivesExamplesTest.java new file mode 100644 index 0000000000..eea0be97a2 --- /dev/null +++ b/akka-docs-dev/rst/java/code/docs/http/javadsl/server/directives/MethodDirectivesExamplesTest.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2015 Typesafe Inc. + */ + +package docs.http.javadsl.server.directives; + +import akka.http.javadsl.model.HttpMethod; +import akka.http.javadsl.model.HttpMethods; +import akka.http.javadsl.model.HttpRequest; +import akka.http.javadsl.model.StatusCodes; +import akka.http.javadsl.server.*; +import akka.http.javadsl.testkit.JUnitRouteTest; + +import org.junit.Test; + +public class MethodDirectivesExamplesTest extends JUnitRouteTest { + @Test + public void testDelete() { + //#delete + final Route route = delete(complete("This is a DELETE request.")); + + testRoute(route).run(HttpRequest.DELETE("/")).assertEntity( + "This is a DELETE request."); + //#delete + } + + @Test + public void testGet() { + //#get + final Route route = get(complete("This is a GET request.")); + + testRoute(route).run(HttpRequest.GET("/")).assertEntity( + "This is a GET request."); + //#get + } + + @Test + public void testHead() { + //#head + final Route route = head(complete("This is a HEAD request.")); + + testRoute(route).run(HttpRequest.HEAD("/")).assertEntity( + "This is a HEAD request."); + //#head + } + + @Test + public void testOptions() { + //#options + final Route route = options(complete("This is a OPTIONS request.")); + + testRoute(route).run(HttpRequest.OPTIONS("/")).assertEntity( + "This is a OPTIONS request."); + //#options + } + + @Test + public void testPatch() { + //#patch + final Route route = patch(complete("This is a PATCH request.")); + + testRoute(route).run(HttpRequest.PATCH("/").withEntity("patch content")) + .assertEntity("This is a PATCH request."); + //#patch + } + + @Test + public void testPost() { + //#post + final Route route = post(complete("This is a POST request.")); + + testRoute(route).run(HttpRequest.POST("/").withEntity("post content")) + .assertEntity("This is a POST request."); + //#post + } + + @Test + public void testPut() { + //#put + final Route route = put(complete("This is a PUT request.")); + + testRoute(route).run(HttpRequest.PUT("/").withEntity("put content")) + .assertEntity("This is a PUT request."); + //#put + } + + @Test + public void testMethodExample() { + //#method-example + final Route route = method(HttpMethods.PUT, + complete("This is a PUT request.")); + + testRoute(route).run(HttpRequest.PUT("/").withEntity("put content")) + .assertEntity("This is a PUT request."); + + testRoute(route).run(HttpRequest.GET("/")).assertStatusCode( + StatusCodes.METHOD_NOT_ALLOWED); + //#method-example + } + + @Test + public void testExtractMethodExample() { + //#extractMethod + final RequestVal requestMethod = RequestVals.requestMethod(); + + final Route otherMethod = handleWith1( + requestMethod, + (ctx, method) -> ctx.complete("This " + method.value() + + " request, clearly is not a GET!")); + + final Route route = route(get(complete("This is a GET request.")), + otherMethod); + + testRoute(route).run(HttpRequest.GET("/")).assertEntity( + "This is a GET request."); + + testRoute(route).run(HttpRequest.PUT("/").withEntity("put content")) + .assertEntity("This PUT request, clearly is not a GET!"); + + testRoute(route).run(HttpRequest.HEAD("/")).assertEntity( + "This HEAD request, clearly is not a GET!"); + //#extractMethod + } +} diff --git a/akka-docs-dev/rst/java/http/routing-dsl/directives/host-directives/extractHost.rst b/akka-docs-dev/rst/java/http/routing-dsl/directives/host-directives/extractHost.rst new file mode 100644 index 0000000000..b01e058889 --- /dev/null +++ b/akka-docs-dev/rst/java/http/routing-dsl/directives/host-directives/extractHost.rst @@ -0,0 +1,13 @@ +.. _-extractHost-java-: + +RequestVals.host +================ + +Extract the hostname part of the ``Host`` request header and expose it as a ``String`` extraction +to its inner route. + + +Example +------- + +.. includecode:: ../../../../code/docs/http/javadsl/server/directives/HostDirectivesExamplesTest.java#extractHostname diff --git a/akka-docs-dev/rst/java/http/routing-dsl/directives/host-directives/host.rst b/akka-docs-dev/rst/java/http/routing-dsl/directives/host-directives/host.rst new file mode 100644 index 0000000000..1a9aaff0ae --- /dev/null +++ b/akka-docs-dev/rst/java/http/routing-dsl/directives/host-directives/host.rst @@ -0,0 +1,44 @@ +.. _-host-java-: + +host +==== + +Filter requests matching conditions against the hostname part of the Host header value +in the request. + +There are a few variants: + +* reject all requests with a hostname different from the given ones +* reject all requests for which the hostname does not satisfy the given predicate +* reject all requests for which the hostname does not satisfy the given regular expression + +The regular expression matching works a little bit different: it rejects all requests with a hostname +that doesn't have a prefix matching the given regular expression and also extracts a ``String`` to its +inner route following this rules: + + * For all matching requests the prefix string matching the regex is extracted and passed to the inner route. + * If the regex contains a capturing group only the string matched by this group is extracted. + * If the regex contains more than one capturing group an ``IllegalArgumentException`` is thrown. + +Example +------- + +Matching a list of hosts: + +.. includecode:: ../../../../code/docs/http/javadsl/server/directives/HostDirectivesExamplesTest.java#host1 + +Making sure the host satisfies the given predicate + +.. includecode:: ../../../../code/docs/http/javadsl/server/directives/HostDirectivesExamplesTest.java#host2 + +Using a regular expressions: + +.. includecode:: ../../../../code/docs/http/javadsl/server/directives/HostDirectivesExamplesTest.java#matchAndExtractHost + +Beware that in the case of introducing multiple capturing groups in the regex such as in the case bellow, the +directive will fail at runtime, at the moment the route tree is evaluated for the first time. This might cause +your http handler actor to enter in a fail/restart loop depending on your supervision strategy. + +.. includecode:: ../../../../code/docs/http/javadsl/server/directives/HostDirectivesExamplesTest.java#failing-matchAndExtractHost + + diff --git a/akka-docs-dev/rst/java/http/routing-dsl/directives/host-directives/index.rst b/akka-docs-dev/rst/java/http/routing-dsl/directives/host-directives/index.rst new file mode 100644 index 0000000000..0212747027 --- /dev/null +++ b/akka-docs-dev/rst/java/http/routing-dsl/directives/host-directives/index.rst @@ -0,0 +1,15 @@ +.. _HostDirectives-java: + +HostDirectives +============== + +HostDirectives allow you to filter requests based on the hostname part of the ``Host`` header +contained in incoming requests as well as extracting its value for usage in inner routes. + +.. toctree:: + :maxdepth: 1 + + host + extractHost + + \ No newline at end of file diff --git a/akka-docs-dev/rst/java/http/routing-dsl/directives/index.rst b/akka-docs-dev/rst/java/http/routing-dsl/directives/index.rst index 99e55c27ce..5d9f2f893d 100644 --- a/akka-docs-dev/rst/java/http/routing-dsl/directives/index.rst +++ b/akka-docs-dev/rst/java/http/routing-dsl/directives/index.rst @@ -58,9 +58,13 @@ SchemeDirectives WebsocketDirectives Contains directives to support answering Websocket requests. +TODO this page should be rewritten as the corresponding Scala page + .. toctree:: :maxdepth: 1 path-directives + method-directives/index + host-directives/index .. _`RFC 7234`: http://tools.ietf.org/html/rfc7234 \ No newline at end of file diff --git a/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/delete.rst b/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/delete.rst new file mode 100644 index 0000000000..f9bd200bd9 --- /dev/null +++ b/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/delete.rst @@ -0,0 +1,17 @@ +.. _-delete-java-: + +delete +====== + +Matches requests with HTTP method ``DELETE``. + +This directive filters an incoming request by its HTTP method. Only requests with +method ``DELETE`` are passed on to the inner route. All others are rejected with a +``MethodRejection``, which is translated into a ``405 Method Not Allowed`` response +by the default ``RejectionHandler``. + + +Example +------- + +.. includecode:: ../../../../code/docs/http/javadsl/server/directives/MethodDirectivesExamplesTest.java#delete diff --git a/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/extractMethod.rst b/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/extractMethod.rst new file mode 100644 index 0000000000..b0d9abb1f6 --- /dev/null +++ b/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/extractMethod.rst @@ -0,0 +1,16 @@ +.. _-extractMethod-java-: + +extractMethod +============= + +Extracts the :class:`HttpMethod` from the request context and provides it for use for other directives explicitly. + +Example +------- + +In the below example our route first matches all ``GET`` requests, and if an incoming request wasn't a ``GET``, +the matching continues and the extractMethod route will be applied which we can use to programatically +print what type of request it was - independent of what actual HttpMethod it was: + +.. includecode:: ../../../../code/docs/http/javadsl/server/directives/MethodDirectivesExamplesTest.java#extractMethod + diff --git a/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/get.rst b/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/get.rst new file mode 100644 index 0000000000..c843d53644 --- /dev/null +++ b/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/get.rst @@ -0,0 +1,17 @@ +.. _-get-java-: + +get +=== + +Matches requests with HTTP method ``GET``. + +This directive filters the incoming request by its HTTP method. Only requests with +method ``GET`` are passed on to the inner route. All others are rejected with a +``MethodRejection``, which is translated into a ``405 Method Not Allowed`` response +by the default ``RejectionHandler``. + + +Example +------- + +.. includecode:: ../../../../code/docs/http/javadsl/server/directives/MethodDirectivesExamplesTest.java#get diff --git a/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/head.rst b/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/head.rst new file mode 100644 index 0000000000..3c0361f0dc --- /dev/null +++ b/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/head.rst @@ -0,0 +1,20 @@ +.. _-head-java-: + +head +==== + +Matches requests with HTTP method ``HEAD``. + +This directive filters the incoming request by its HTTP method. Only requests with +method ``HEAD`` are passed on to the inner route. All others are rejected with a +``MethodRejection``, which is translated into a ``405 Method Not Allowed`` response +by the default ``RejectionHandler``. + +.. note:: By default, akka-http handles HEAD-requests transparently by dispatching a GET-request to the handler and + stripping of the result body. See the ``akka.http.server.transparent-head-requests`` setting for how to disable + this behavior. + +Example +------- + +.. includecode:: ../../../../code/docs/http/javadsl/server/directives/MethodDirectivesExamplesTest.java#head \ No newline at end of file diff --git a/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/index.rst b/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/index.rst new file mode 100644 index 0000000000..e5241a42f2 --- /dev/null +++ b/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/index.rst @@ -0,0 +1,18 @@ +.. _MethodDirectives: + +MethodDirectives +================ + +.. toctree:: + :maxdepth: 1 + + delete + extractMethod + get + head + method + options + overrideMethodWithParameter + patch + post + put diff --git a/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/method.rst b/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/method.rst new file mode 100644 index 0000000000..6ce7afd472 --- /dev/null +++ b/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/method.rst @@ -0,0 +1,19 @@ +.. _-method-java-: + +method +====== + +Matches HTTP requests based on their method. + +Description +----------- + +This directive filters the incoming request by its HTTP method. Only requests with +the specified method are passed on to the inner route. All others are rejected with a +``MethodRejection``, which is translated into a ``405 Method Not Allowed`` response +by the default ``RejectionHandler``. + +Example +------- + +.. includecode:: ../../../../code/docs/http/javadsl/server/directives/MethodDirectivesExamplesTest.java#method-example diff --git a/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/options.rst b/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/options.rst new file mode 100644 index 0000000000..8603620464 --- /dev/null +++ b/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/options.rst @@ -0,0 +1,16 @@ +.. _-options-java-: + +options +======= + +Matches requests with HTTP method ``OPTIONS``. + +This directive filters the incoming request by its HTTP method. Only requests with +method ``OPTIONS`` are passed on to the inner route. All others are rejected with a +``MethodRejection``, which is translated into a ``405 Method Not Allowed`` response +by the default ``RejectionHandler``. + +Example +------- + +.. includecode:: ../../../../code/docs/http/javadsl/server/directives/MethodDirectivesExamplesTest.java#options diff --git a/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/overrideMethodWithParameter.rst b/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/overrideMethodWithParameter.rst new file mode 100644 index 0000000000..86811021ba --- /dev/null +++ b/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/overrideMethodWithParameter.rst @@ -0,0 +1,11 @@ +.. _-overrideMethodWithParameter-java-: + +overrideMethodWithParameter +=========================== + +TODO ... + +Example +------- + +TODO sample is missing, also in Scala documentation diff --git a/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/patch.rst b/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/patch.rst new file mode 100644 index 0000000000..112d7b1b61 --- /dev/null +++ b/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/patch.rst @@ -0,0 +1,17 @@ +.. _-patch-java-: + +patch +===== + +Matches requests with HTTP method ``PATCH``. + +This directive filters the incoming request by its HTTP method. Only requests with +method ``PATCH`` are passed on to the inner route. All others are rejected with a +``MethodRejection``, which is translated into a ``405 Method Not Allowed`` response +by the default ``RejectionHandler``. + + +Example +------- + +.. includecode:: ../../../../code/docs/http/javadsl/server/directives/MethodDirectivesExamplesTest.java#patch diff --git a/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/post.rst b/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/post.rst new file mode 100644 index 0000000000..1243fa5072 --- /dev/null +++ b/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/post.rst @@ -0,0 +1,17 @@ +.. _-post-java-: + +post +==== + +Matches requests with HTTP method ``POST``. + +This directive filters the incoming request by its HTTP method. Only requests with +method ``POST`` are passed on to the inner route. All others are rejected with a +``MethodRejection``, which is translated into a ``405 Method Not Allowed`` response +by the default ``RejectionHandler``. + + +Example +------- + +.. includecode:: ../../../../code/docs/http/javadsl/server/directives/MethodDirectivesExamplesTest.java#post diff --git a/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/put.rst b/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/put.rst new file mode 100644 index 0000000000..26b628aa38 --- /dev/null +++ b/akka-docs-dev/rst/java/http/routing-dsl/directives/method-directives/put.rst @@ -0,0 +1,16 @@ +.. _-put-java-: + +put +=== + +Matches requests with HTTP method ``PUT``. + +This directive filters the incoming request by its HTTP method. Only requests with +method ``PUT`` are passed on to the inner route. All others are rejected with a +``MethodRejection``, which is translated into a ``405 Method Not Allowed`` response +by the default ``RejectionHandler``. + +Example +------- + +.. includecode:: ../../../../code/docs/http/javadsl/server/directives/MethodDirectivesExamplesTest.java#put diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpRequest.java b/akka-http-core/src/main/java/akka/http/javadsl/model/HttpRequest.java index 1121e8ddd7..a9fd6f52ca 100644 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/HttpRequest.java +++ b/akka-http-core/src/main/java/akka/http/javadsl/model/HttpRequest.java @@ -86,11 +86,25 @@ public abstract class HttpRequest implements HttpMessage, HttpMessage.MessageTra public static HttpRequest DELETE(String uri) { return create(uri).withMethod(HttpMethods.DELETE); } - + /** * A default HEAD request to be modified using the `withX` methods. */ public static HttpRequest HEAD(String uri) { return create(uri).withMethod(HttpMethods.HEAD); } + + /** + * A default PATCH request to be modified using the `withX` methods. + */ + public static HttpRequest PATCH(String uri) { + return create(uri).withMethod(HttpMethods.PATCH); + } + + /** + * A default OPTIONS request to be modified using the `withX` methods. + */ + public static HttpRequest OPTIONS(String uri) { + return create(uri).withMethod(HttpMethods.OPTIONS); + } } diff --git a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Host.java b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Host.java index 7e80f807da..fff8b2673d 100644 --- a/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Host.java +++ b/akka-http-core/src/main/java/akka/http/javadsl/model/headers/Host.java @@ -4,7 +4,22 @@ package akka.http.javadsl.model.headers; +import java.net.InetSocketAddress; + public abstract class Host extends akka.http.scaladsl.model.HttpHeader { + + public static Host create(InetSocketAddress address) { + return akka.http.scaladsl.model.headers.Host.apply(address); + } + + public static Host create(String host) { + return akka.http.scaladsl.model.headers.Host.apply(host); + } + + public static Host create(String host, int port) { + return akka.http.scaladsl.model.headers.Host.apply(host, port); + } + public abstract akka.http.javadsl.model.Host host(); public abstract int port(); } diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/RequestVals.scala b/akka-http/src/main/scala/akka/http/javadsl/server/RequestVals.scala index 62fab13016..67c3ef687a 100644 --- a/akka-http/src/main/scala/akka/http/javadsl/server/RequestVals.scala +++ b/akka-http/src/main/scala/akka/http/javadsl/server/RequestVals.scala @@ -76,7 +76,9 @@ object RequestVals { */ def matchAndExtractHost(regex: Pattern): RequestVal[String] = new StandaloneExtractionImpl[String] { - def directive: Directive1[String] = HostDirectives.host(regex.pattern().r) + // important to use a val here so that invalid patterns are + // detected at construction and `IllegalArgumentException` is thrown + override val directive: Directive1[String] = HostDirectives.host(regex.pattern().r) } /**