Skip to content

Commit

Permalink
+htp akka#18929 add withSizeLimit directive (akka#20760)
Browse files Browse the repository at this point in the history
withSizeLimit and withoutSizeLimit directives added
  • Loading branch information
note authored and ktoso committed Jun 28, 2016
1 parent e00a862 commit 72f8544
Show file tree
Hide file tree
Showing 15 changed files with 487 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright (C) 2016-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl.server.directives;

import akka.http.javadsl.model.HttpRequest;
import akka.http.javadsl.model.StatusCodes;
import akka.http.javadsl.server.Route;
import akka.http.javadsl.server.Unmarshaller;
import akka.http.javadsl.testkit.JUnitRouteTest;
import org.junit.Test;

import java.util.Arrays;
import java.util.function.Function;

public class MiscDirectivesExamplesTest extends JUnitRouteTest {

@Test
public void testWithSizeLimit() {
//#withSizeLimitExample
final Route route = withSizeLimit(500, () ->
entity(Unmarshaller.entityToString(), (entity) ->
complete("ok")
)
);

Function<Integer, HttpRequest> withEntityOfSize = (sizeLimit) -> {
char[] charArray = new char[sizeLimit];
Arrays.fill(charArray, '0');
return HttpRequest.POST("/").withEntity(new String(charArray));
};

// tests:
testRoute(route).run(withEntityOfSize.apply(500))
.assertStatusCode(StatusCodes.OK);

testRoute(route).run(withEntityOfSize.apply(501))
.assertStatusCode(StatusCodes.BAD_REQUEST);
//#withSizeLimitExample
}

@Test
public void testWithoutSizeLimit() {
//#withoutSizeLimitExample
final Route route = withoutSizeLimit(() ->
entity(Unmarshaller.entityToString(), (entity) ->
complete("ok")
)
);

Function<Integer, HttpRequest> withEntityOfSize = (sizeLimit) -> {
char[] charArray = new char[sizeLimit];
Arrays.fill(charArray, '0');
return HttpRequest.POST("/").withEntity(new String(charArray));
};

// tests:
// will work even if you have configured akka.http.parsing.max-content-length = 500
testRoute(route).run(withEntityOfSize.apply(501))
.assertStatusCode(StatusCodes.OK);
//#withoutSizeLimitExample
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,14 @@ Directive Description
:ref:`-uploadedFile-java-` Streams one uploaded file from a multipart request to a file on disk
:ref:`-validate-java-` Checks a given condition before running its inner route
:ref:`-withoutRequestTimeout-java-` Disables :ref:`request timeouts <request-timeout-java>` for a given route.
:ref:`-withoutSizeLimit-java-` Skips request entity size check
:ref:`-withExecutionContext-java-` Runs its inner route with the given alternative ``ExecutionContext``
:ref:`-withMaterializer-java-` Runs its inner route with the given alternative ``Materializer``
:ref:`-withLog-java-` Runs its inner route with the given alternative ``LoggingAdapter``
:ref:`-withRangeSupport-java-` Adds ``Accept-Ranges: bytes`` to responses to GET requests, produces partial responses if the initial request contained a valid ``Range`` header
:ref:`-withRequestTimeout-java-` Configures the :ref:`request timeouts <request-timeout-java>` for a given route.
:ref:`-withRequestTimeoutResponse-java-` Prepares the ``HttpResponse`` that is emitted if a request timeout is triggered. ``RequestContext => RequestContext`` function
:ref:`-withSettings-java-` Runs its inner route with the given alternative ``RoutingSettings``
:ref:`-withSizeLimit-java-` Applies request entity size check
================================================ ============================================================================

Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ MiscDirectives
requestEntityPresent
selectPreferredLanguage
validate
withoutSizeLimit
withSizeLimit
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.. _-withSizeLimit-java-:

withSizeLimit
===============

Description
-----------
Fails the stream with ``EntityStreamSizeException`` if its request entity size exceeds given limit. Limit given
as parameter overrides limit configured with ``akka.http.parsing.max-content-length``.

The whole mechanism of entity size checking is intended to prevent certain Denial-of-Service attacks.
So suggested setup is to have ``akka.http.parsing.max-content-length`` relatively low and use ``withSizeLimit``
directive for endpoints which expects bigger entities.

See also :ref:`-withoutSizeLimit-java-` for skipping request entity size check.

Example
-------

.. includecode:: ../../../../code/docs/http/javadsl/server/directives/MiscDirectivesExamplesTest.java#withSizeLimitExample
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.. _-withoutSizeLimit-java-:

withoutSizeLimit
================

Description
-----------
Skips request entity size verification.

The whole mechanism of entity size checking is intended to prevent certain Denial-of-Service attacks.
So suggested setup is to have ``akka.http.parsing.max-content-length`` relatively low and use ``withoutSizeLimit``
directive just for endpoints for which size verification should not be performed.

See also :ref:`-withSizeLimit-java-` for setting request entity size limit.

Example
-------

.. includecode:: ../../../../code/docs/http/javadsl/server/directives/MiscDirectivesExamplesTest.java#withSizeLimitExample
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class MiscDirectivesExamplesSpec extends RoutingSpec {
responseAs[String] shouldEqual "Client's ip is 192.168.3.12"
}
}

"rejectEmptyResponse-example" in {
val route = rejectEmptyResponse {
path("even" / IntNumber) { i =>
Expand All @@ -42,6 +43,7 @@ class MiscDirectivesExamplesSpec extends RoutingSpec {
responseAs[String] shouldEqual "Number 28 is even."
}
}

"requestEntityEmptyPresent-example" in {
val route =
requestEntityEmpty {
Expand All @@ -59,6 +61,7 @@ class MiscDirectivesExamplesSpec extends RoutingSpec {
responseAs[String] shouldEqual "request entity empty"
}
}

"selectPreferredLanguage-example" in {
val request = Get() ~> `Accept-Language`(
Language("en-US"),
Expand All @@ -78,6 +81,7 @@ class MiscDirectivesExamplesSpec extends RoutingSpec {
}
} ~> check { responseAs[String] shouldEqual "de-DE" }
}

"validate-example" in {
val route =
extractUri { uri =>
Expand All @@ -94,4 +98,84 @@ class MiscDirectivesExamplesSpec extends RoutingSpec {
rejection shouldEqual ValidationRejection("Path too long: '/abcdefghijkl'", None)
}
}

"withSizeLimit-example" in {
val route = withSizeLimit(500) {
entity(as[String]) { _
complete(HttpResponse())
}
}

// tests:
def entityOfSize(size: Int) =
HttpEntity(ContentTypes.`text/plain(UTF-8)`, "0" * size)

Post("/abc", entityOfSize(500)) ~> route ~> check {
status shouldEqual StatusCodes.OK
}

Post("/abc", entityOfSize(501)) ~> Route.seal(route) ~> check {
status shouldEqual StatusCodes.BadRequest
}

}

"withSizeLimit-execution-moment-example" in {
val route = withSizeLimit(500) {
complete(HttpResponse())
}

// tests:
def entityOfSize(size: Int) =
HttpEntity(ContentTypes.`text/plain(UTF-8)`, "0" * size)

Post("/abc", entityOfSize(500)) ~> route ~> check {
status shouldEqual StatusCodes.OK
}

Post("/abc", entityOfSize(501)) ~> route ~> check {
status shouldEqual StatusCodes.OK
}
}

"withSizeLimit-nested-example" in {
val route =
withSizeLimit(500) {
withSizeLimit(800) {
entity(as[String]) { _
complete(HttpResponse())
}
}
}

// tests:
def entityOfSize(size: Int) =
HttpEntity(ContentTypes.`text/plain(UTF-8)`, "0" * size)
Post("/abc", entityOfSize(800)) ~> route ~> check {
status shouldEqual StatusCodes.OK
}

Post("/abc", entityOfSize(801)) ~> Route.seal(route) ~> check {
status shouldEqual StatusCodes.BadRequest
}
}

"withoutSizeLimit-example" in {
val route =
withoutSizeLimit {
entity(as[String]) { _
complete(HttpResponse())
}
}

// tests:
def entityOfSize(size: Int) =
HttpEntity(ContentTypes.`text/plain(UTF-8)`, "0" * size)

// will work even if you have configured akka.http.parsing.max-content-length = 500
Post("/abc", entityOfSize(501)) ~> route ~> check {
status shouldEqual StatusCodes.OK
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ Directive Description
:ref:`-uploadedFile-` Streams one uploaded file from a multipart request to a file on disk
:ref:`-validate-` Checks a given condition before running its inner route
:ref:`-withoutRequestTimeout-` Disables :ref:`request timeouts <request-timeout-scala>` for a given route.
:ref:`-withoutSizeLimit-` Skips request entity size check
:ref:`-withExecutionContext-` Runs its inner route with the given alternative ``ExecutionContext``
:ref:`-withMaterializer-` Runs its inner route with the given alternative ``Materializer``
:ref:`-withLog-` Runs its inner route with the given alternative ``LoggingAdapter``
Expand All @@ -227,4 +228,5 @@ Directive Description
:ref:`-withRequestTimeoutResponse-` Prepares the ``HttpResponse`` that is emitted if a request timeout is triggered.
``RequestContext => RequestContext`` function
:ref:`-withSettings-` Runs its inner route with the given alternative ``RoutingSettings``
:ref:`-withSizeLimit-` Applies request entity size check
=========================================== ============================================================================
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ MiscDirectives
requestEntityEmpty
requestEntityPresent
selectPreferredLanguage
validate
validate
withoutSizeLimit
withSizeLimit
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
.. _-withSizeLimit-:

withSizeLimit
===============

Signature
---------

.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/MiscDirectives.scala
:snippet: withSizeLimit

Description
-----------
Fails the stream with ``EntityStreamSizeException`` if its request entity size exceeds given limit. Limit given
as parameter overrides limit configured with ``akka.http.parsing.max-content-length``.

The whole mechanism of entity size checking is intended to prevent certain Denial-of-Service attacks.
So suggested setup is to have ``akka.http.parsing.max-content-length`` relatively low and use ``withSizeLimit``
directive for endpoints which expects bigger entities.

See also :ref:`-withoutSizeLimit-` for skipping request entity size check.

Examples
--------

.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MiscDirectivesExamplesSpec.scala
:snippet: withSizeLimit-example

Beware that request entity size check is executed when entity is consumed. Therefore in the following example
even request with entity greater than argument to ``withSizeLimit`` will succeed (because this route
does not consume entity):

.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MiscDirectivesExamplesSpec.scala
:snippet: withSizeLimit-execution-moment-example

Directive ``withSizeLimit`` is implemented in terms of ``HttpEntity.withSizeLimit`` which means that in case of
nested ``withSizeLimit`` directives the innermost is applied:

.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MiscDirectivesExamplesSpec.scala
:snippet: withSizeLimit-nested-example
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.. _-withoutSizeLimit-:

withoutSizeLimit
================

Signature
---------

.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/MiscDirectives.scala
:snippet: withoutSizeLimit

Description
-----------
Skips request entity size verification.

The whole mechanism of entity size checking is intended to prevent certain Denial-of-Service attacks.
So suggested setup is to have ``akka.http.parsing.max-content-length`` relatively low and use ``withoutSizeLimit``
directive just for endpoints for which size verification should not be performed.

See also :ref:`-withSizeLimit-` for setting request entity size limit.

Example
-------

.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/MiscDirectivesExamplesSpec.scala
:snippet: withoutSizeLimit-example
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@
import akka.http.javadsl.model.headers.RawHeader;
import akka.http.javadsl.model.headers.XForwardedFor;
import akka.http.javadsl.model.headers.XRealIp;
import akka.http.javadsl.server.Unmarshaller;
import akka.http.javadsl.testkit.JUnitRouteTest;
import akka.http.javadsl.testkit.TestRoute;
import org.junit.Test;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;

public class MiscDirectivesTest extends JUnitRouteTest {

Expand Down Expand Up @@ -73,4 +75,26 @@ public void testClientIpExtraction() throws UnknownHostException {
.assertStatusCode(StatusCodes.NOT_FOUND);
}

@Test
public void testWithSizeLimit() {
TestRoute route = testRoute(withSizeLimit(500, () ->
entity(Unmarshaller.entityToString(), (entity) -> complete("ok"))
));

route
.run(withEntityOfSize(500))
.assertStatusCode(StatusCodes.OK);

route
.run(withEntityOfSize(501))
.assertStatusCode(StatusCodes.BAD_REQUEST);

}

private HttpRequest withEntityOfSize(int sizeLimit) {
char[] charArray = new char[sizeLimit];
Arrays.fill(charArray, '0');
return HttpRequest.POST("/").withEntity(new String(charArray));
}

}
Loading

0 comments on commit 72f8544

Please sign in to comment.