withSizeLimit and withoutSizeLimit directives added
This commit is contained in:
parent
e00a86271a
commit
72f8544efd
15 changed files with 487 additions and 1 deletions
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -139,6 +139,7 @@ Directive Description
|
||||||
:ref:`-uploadedFile-java-` Streams one uploaded file from a multipart request to a file on disk
|
: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:`-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:`-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:`-withExecutionContext-java-` Runs its inner route with the given alternative ``ExecutionContext``
|
||||||
:ref:`-withMaterializer-java-` Runs its inner route with the given alternative ``Materializer``
|
: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:`-withLog-java-` Runs its inner route with the given alternative ``LoggingAdapter``
|
||||||
|
|
@ -146,5 +147,6 @@ Directive Description
|
||||||
:ref:`-withRequestTimeout-java-` Configures the :ref:`request timeouts <request-timeout-java>` for a given route.
|
: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:`-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:`-withSettings-java-` Runs its inner route with the given alternative ``RoutingSettings``
|
||||||
|
:ref:`-withSizeLimit-java-` Applies request entity size check
|
||||||
================================================ ============================================================================
|
================================================ ============================================================================
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,3 +12,5 @@ MiscDirectives
|
||||||
requestEntityPresent
|
requestEntityPresent
|
||||||
selectPreferredLanguage
|
selectPreferredLanguage
|
||||||
validate
|
validate
|
||||||
|
withoutSizeLimit
|
||||||
|
withSizeLimit
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -22,6 +22,7 @@ class MiscDirectivesExamplesSpec extends RoutingSpec {
|
||||||
responseAs[String] shouldEqual "Client's ip is 192.168.3.12"
|
responseAs[String] shouldEqual "Client's ip is 192.168.3.12"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"rejectEmptyResponse-example" in {
|
"rejectEmptyResponse-example" in {
|
||||||
val route = rejectEmptyResponse {
|
val route = rejectEmptyResponse {
|
||||||
path("even" / IntNumber) { i =>
|
path("even" / IntNumber) { i =>
|
||||||
|
|
@ -42,6 +43,7 @@ class MiscDirectivesExamplesSpec extends RoutingSpec {
|
||||||
responseAs[String] shouldEqual "Number 28 is even."
|
responseAs[String] shouldEqual "Number 28 is even."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"requestEntityEmptyPresent-example" in {
|
"requestEntityEmptyPresent-example" in {
|
||||||
val route =
|
val route =
|
||||||
requestEntityEmpty {
|
requestEntityEmpty {
|
||||||
|
|
@ -59,6 +61,7 @@ class MiscDirectivesExamplesSpec extends RoutingSpec {
|
||||||
responseAs[String] shouldEqual "request entity empty"
|
responseAs[String] shouldEqual "request entity empty"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"selectPreferredLanguage-example" in {
|
"selectPreferredLanguage-example" in {
|
||||||
val request = Get() ~> `Accept-Language`(
|
val request = Get() ~> `Accept-Language`(
|
||||||
Language("en-US"),
|
Language("en-US"),
|
||||||
|
|
@ -78,6 +81,7 @@ class MiscDirectivesExamplesSpec extends RoutingSpec {
|
||||||
}
|
}
|
||||||
} ~> check { responseAs[String] shouldEqual "de-DE" }
|
} ~> check { responseAs[String] shouldEqual "de-DE" }
|
||||||
}
|
}
|
||||||
|
|
||||||
"validate-example" in {
|
"validate-example" in {
|
||||||
val route =
|
val route =
|
||||||
extractUri { uri =>
|
extractUri { uri =>
|
||||||
|
|
@ -94,4 +98,84 @@ class MiscDirectivesExamplesSpec extends RoutingSpec {
|
||||||
rejection shouldEqual ValidationRejection("Path too long: '/abcdefghijkl'", None)
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -218,6 +218,7 @@ Directive Description
|
||||||
:ref:`-uploadedFile-` Streams one uploaded file from a multipart request to a file on disk
|
: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:`-validate-` Checks a given condition before running its inner route
|
||||||
:ref:`-withoutRequestTimeout-` Disables :ref:`request timeouts <request-timeout-scala>` for a given 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:`-withExecutionContext-` Runs its inner route with the given alternative ``ExecutionContext``
|
||||||
:ref:`-withMaterializer-` Runs its inner route with the given alternative ``Materializer``
|
:ref:`-withMaterializer-` Runs its inner route with the given alternative ``Materializer``
|
||||||
:ref:`-withLog-` Runs its inner route with the given alternative ``LoggingAdapter``
|
:ref:`-withLog-` Runs its inner route with the given alternative ``LoggingAdapter``
|
||||||
|
|
@ -227,4 +228,5 @@ Directive Description
|
||||||
:ref:`-withRequestTimeoutResponse-` Prepares the ``HttpResponse`` that is emitted if a request timeout is triggered.
|
:ref:`-withRequestTimeoutResponse-` Prepares the ``HttpResponse`` that is emitted if a request timeout is triggered.
|
||||||
``RequestContext => RequestContext`` function
|
``RequestContext => RequestContext`` function
|
||||||
:ref:`-withSettings-` Runs its inner route with the given alternative ``RoutingSettings``
|
:ref:`-withSettings-` Runs its inner route with the given alternative ``RoutingSettings``
|
||||||
|
:ref:`-withSizeLimit-` Applies request entity size check
|
||||||
=========================================== ============================================================================
|
=========================================== ============================================================================
|
||||||
|
|
|
||||||
|
|
@ -11,4 +11,6 @@ MiscDirectives
|
||||||
requestEntityEmpty
|
requestEntityEmpty
|
||||||
requestEntityPresent
|
requestEntityPresent
|
||||||
selectPreferredLanguage
|
selectPreferredLanguage
|
||||||
validate
|
validate
|
||||||
|
withoutSizeLimit
|
||||||
|
withSizeLimit
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -11,12 +11,14 @@ import akka.http.javadsl.model.Uri;
|
||||||
import akka.http.javadsl.model.headers.RawHeader;
|
import akka.http.javadsl.model.headers.RawHeader;
|
||||||
import akka.http.javadsl.model.headers.XForwardedFor;
|
import akka.http.javadsl.model.headers.XForwardedFor;
|
||||||
import akka.http.javadsl.model.headers.XRealIp;
|
import akka.http.javadsl.model.headers.XRealIp;
|
||||||
|
import akka.http.javadsl.server.Unmarshaller;
|
||||||
import akka.http.javadsl.testkit.JUnitRouteTest;
|
import akka.http.javadsl.testkit.JUnitRouteTest;
|
||||||
import akka.http.javadsl.testkit.TestRoute;
|
import akka.http.javadsl.testkit.TestRoute;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
public class MiscDirectivesTest extends JUnitRouteTest {
|
public class MiscDirectivesTest extends JUnitRouteTest {
|
||||||
|
|
||||||
|
|
@ -73,4 +75,26 @@ public class MiscDirectivesTest extends JUnitRouteTest {
|
||||||
.assertStatusCode(StatusCodes.NOT_FOUND);
|
.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));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2009-2016 Lightbend Inc. <http://www.lightbend.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
package akka.http.scaladsl.server
|
||||||
|
|
||||||
|
import akka.actor.ActorSystem
|
||||||
|
import akka.http.scaladsl.client.RequestBuilding
|
||||||
|
import akka.http.scaladsl.model._
|
||||||
|
import akka.http.scaladsl.server.Directives._
|
||||||
|
import akka.http.scaladsl.{ Http, TestUtils }
|
||||||
|
import akka.stream.ActorMaterializer
|
||||||
|
import com.typesafe.config.{ Config, ConfigFactory }
|
||||||
|
import org.scalatest.{ BeforeAndAfterAll, Matchers, WordSpec }
|
||||||
|
|
||||||
|
import scala.concurrent.Await
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
|
class WithoutSizeLimitSpec extends WordSpec with Matchers with RequestBuilding with BeforeAndAfterAll {
|
||||||
|
val testConf: Config = ConfigFactory.parseString("""
|
||||||
|
akka.loggers = ["akka.testkit.TestEventListener"]
|
||||||
|
akka.loglevel = ERROR
|
||||||
|
akka.stdout-loglevel = ERROR
|
||||||
|
akka.http.parsing.max-content-length = 800""")
|
||||||
|
implicit val system = ActorSystem(getClass.getSimpleName, testConf)
|
||||||
|
import system.dispatcher
|
||||||
|
implicit val materializer = ActorMaterializer()
|
||||||
|
|
||||||
|
"the withoutSizeLimit directive" should {
|
||||||
|
"accept entities bigger than configured with akka.http.parsing.max-content-length" in {
|
||||||
|
val route =
|
||||||
|
path("noDirective") {
|
||||||
|
post {
|
||||||
|
entity(as[String]) { _ ⇒
|
||||||
|
complete(HttpEntity(ContentTypes.`text/html(UTF-8)`, "<h1>Say hello to akka-http</h1>"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} ~
|
||||||
|
path("withoutSizeLimit") {
|
||||||
|
post {
|
||||||
|
withoutSizeLimit {
|
||||||
|
entity(as[String]) { _ ⇒
|
||||||
|
complete(HttpEntity(ContentTypes.`text/html(UTF-8)`, "<h1>Say hello to akka-http</h1>"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val (_, hostName, port) = TestUtils.temporaryServerHostnameAndPort()
|
||||||
|
|
||||||
|
val future = for {
|
||||||
|
_ ← Http().bindAndHandle(route, hostName, port)
|
||||||
|
|
||||||
|
requestToNoDirective = Post(s"http://$hostName:$port/noDirective", entityOfSize(801))
|
||||||
|
responseWithoutDirective ← Http().singleRequest(requestToNoDirective)
|
||||||
|
_ = responseWithoutDirective.status shouldEqual StatusCodes.BadRequest
|
||||||
|
|
||||||
|
requestToDirective = Post(s"http://$hostName:$port/withoutSizeLimit", entityOfSize(801))
|
||||||
|
responseWithDirective ← Http().singleRequest(requestToDirective)
|
||||||
|
} yield responseWithDirective
|
||||||
|
|
||||||
|
val response = Await.result(future, 5 seconds)
|
||||||
|
response.status shouldEqual StatusCodes.OK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override def afterAll() = {
|
||||||
|
system.terminate
|
||||||
|
}
|
||||||
|
|
||||||
|
private def entityOfSize(size: Int) = HttpEntity(ContentTypes.`text/plain(UTF-8)`, "0" * size)
|
||||||
|
}
|
||||||
|
|
@ -65,6 +65,89 @@ class MiscDirectivesSpec extends RoutingSpec {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"the withSizeLimit directive" should {
|
||||||
|
"not apply if entity is not consumed" in {
|
||||||
|
val route = withSizeLimit(500) { completeOk }
|
||||||
|
|
||||||
|
Post("/abc", entityOfSize(500)) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
}
|
||||||
|
|
||||||
|
Post("/abc", entityOfSize(501)) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"apply if entity is consumed" in {
|
||||||
|
val route = withSizeLimit(500) {
|
||||||
|
entity(as[String]) { _ ⇒
|
||||||
|
completeOk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Post("/abc", entityOfSize(500)) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
}
|
||||||
|
|
||||||
|
Post("/abc", entityOfSize(501)) ~> Route.seal(route) ~> check {
|
||||||
|
status shouldEqual StatusCodes.BadRequest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"properly handle nested directives by applying innermost `withSizeLimit` directive" in {
|
||||||
|
val route =
|
||||||
|
withSizeLimit(500) {
|
||||||
|
withSizeLimit(800) {
|
||||||
|
entity(as[String]) { _ ⇒
|
||||||
|
completeOk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Post("/abc", entityOfSize(800)) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
}
|
||||||
|
|
||||||
|
Post("/abc", entityOfSize(801)) ~> Route.seal(route) ~> check {
|
||||||
|
status shouldEqual StatusCodes.BadRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
val route2 =
|
||||||
|
withSizeLimit(500) {
|
||||||
|
withSizeLimit(400) {
|
||||||
|
entity(as[String]) { _ ⇒
|
||||||
|
completeOk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Post("/abc", entityOfSize(400)) ~> route2 ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
}
|
||||||
|
|
||||||
|
Post("/abc", entityOfSize(401)) ~> Route.seal(route2) ~> check {
|
||||||
|
status shouldEqual StatusCodes.BadRequest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"the withoutSizeLimit directive" should {
|
||||||
|
"skip request entity size verification" in {
|
||||||
|
val route =
|
||||||
|
withSizeLimit(500) {
|
||||||
|
withoutSizeLimit {
|
||||||
|
entity(as[String]) { _ ⇒
|
||||||
|
completeOk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Post("/abc", entityOfSize(501)) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
implicit class AddStringToIn(acceptLanguageHeaderString: String) {
|
implicit class AddStringToIn(acceptLanguageHeaderString: String) {
|
||||||
def test(body: ((String*) ⇒ String) ⇒ Unit): Unit =
|
def test(body: ((String*) ⇒ String) ⇒ Unit): Unit =
|
||||||
s"properly handle `$acceptLanguageHeaderString`" in {
|
s"properly handle `$acceptLanguageHeaderString`" in {
|
||||||
|
|
@ -88,4 +171,6 @@ class MiscDirectivesSpec extends RoutingSpec {
|
||||||
}
|
}
|
||||||
|
|
||||||
def remoteAddress(ip: String) = RemoteAddress(InetAddress.getByName(ip))
|
def remoteAddress(ip: String) = RemoteAddress(InetAddress.getByName(ip))
|
||||||
|
|
||||||
|
private def entityOfSize(size: Int) = HttpEntity(ContentTypes.`text/plain(UTF-8)`, "0" * size)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,25 @@ abstract class MiscDirectives extends MethodDirectives {
|
||||||
D.rejectEmptyResponse { inner.get.delegate }
|
D.rejectEmptyResponse { inner.get.delegate }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fails the stream with [[akka.http.scaladsl.model.EntityStreamSizeException]] if its request entity size exceeds
|
||||||
|
* given limit. Limit given as parameter overrides limit configured with ``akka.http.parsing.max-content-length``.
|
||||||
|
*
|
||||||
|
* Beware that request entity size check is executed when entity is consumed.
|
||||||
|
*/
|
||||||
|
def withSizeLimit(maxBytes: Long, inner: Supplier[Route]): Route = RouteAdapter {
|
||||||
|
D.withSizeLimit(maxBytes) { inner.get.delegate }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables the size limit (configured by `akka.http.parsing.max-content-length` by default) checking on the incoming
|
||||||
|
* [[akka.http.javadsl.model.HttpRequest]] entity.
|
||||||
|
* Can be useful when handling arbitrarily large data uploads in specific parts of your routes.
|
||||||
|
*/
|
||||||
|
def withoutSizeLimit(inner: Supplier[Route]): Route = RouteAdapter {
|
||||||
|
D.withoutSizeLimit { inner.get.delegate }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inspects the request's `Accept-Language` header and determines,
|
* Inspects the request's `Accept-Language` header and determines,
|
||||||
* which of the given language alternatives is preferred by the client.
|
* which of the given language alternatives is preferred by the client.
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ package akka.http.scaladsl.server
|
||||||
package directives
|
package directives
|
||||||
|
|
||||||
import akka.http.scaladsl.model._
|
import akka.http.scaladsl.model._
|
||||||
|
import akka.http.scaladsl.server.directives.BasicDirectives._
|
||||||
import headers._
|
import headers._
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -71,6 +72,27 @@ trait MiscDirectives {
|
||||||
BasicDirectives.extractRequest.map { request ⇒
|
BasicDirectives.extractRequest.map { request ⇒
|
||||||
LanguageNegotiator(request.headers).pickLanguage(first :: List(more: _*)) getOrElse first
|
LanguageNegotiator(request.headers).pickLanguage(first :: List(more: _*)) getOrElse first
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fails the stream with [[akka.http.scaladsl.model.EntityStreamSizeException]] if its request entity size exceeds
|
||||||
|
* given limit. Limit given as parameter overrides limit configured with `akka.http.parsing.max-content-length`.
|
||||||
|
*
|
||||||
|
* Beware that request entity size check is executed when entity is consumed.
|
||||||
|
*
|
||||||
|
* @group misc
|
||||||
|
*/
|
||||||
|
def withSizeLimit(maxBytes: Long): Directive0 =
|
||||||
|
mapRequestContext(_.mapRequest(_.mapEntity(_.withSizeLimit(maxBytes))))
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Disables the size limit (configured by `akka.http.parsing.max-content-length` by default) checking on the incoming
|
||||||
|
* [[HttpRequest]] entity.
|
||||||
|
* Can be useful when handling arbitrarily large data uploads in specific parts of your routes.
|
||||||
|
*
|
||||||
|
* @group misc
|
||||||
|
*/
|
||||||
|
def withoutSizeLimit: Directive0 = MiscDirectives._withoutSizeLimit
|
||||||
}
|
}
|
||||||
|
|
||||||
object MiscDirectives extends MiscDirectives {
|
object MiscDirectives extends MiscDirectives {
|
||||||
|
|
@ -95,4 +117,7 @@ object MiscDirectives extends MiscDirectives {
|
||||||
case Complete(response) if response.entity.isKnownEmpty ⇒ Rejected(Nil)
|
case Complete(response) if response.entity.isKnownEmpty ⇒ Rejected(Nil)
|
||||||
case x ⇒ x
|
case x ⇒ x
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val _withoutSizeLimit: Directive0 =
|
||||||
|
mapRequestContext(_.mapRequest(_.mapEntity(_.withoutSizeLimit)))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue