From 54f73f9329f273af6806255e31c72594328be696 Mon Sep 17 00:00:00 2001 From: Giovanni Caporaletti Date: Sat, 16 Apr 2016 18:08:31 +0200 Subject: [PATCH] support HttpEntity.with/withoutSizeLimit in scaladsl HttpEntity, #20342 also add a missing test for withSizeLimit --- .../akka/http/scaladsl/model/HttpEntity.scala | 39 +++++++++++++++++++ .../http/scaladsl/model/HttpEntitySpec.scala | 30 ++++++++++++++ project/MiMa.scala | 7 +++- 3 files changed, 75 insertions(+), 1 deletion(-) diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/HttpEntity.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/HttpEntity.scala index 5ff1f1b9a4..dce014a8ae 100644 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/HttpEntity.scala +++ b/akka-http-core/src/main/scala/akka/http/scaladsl/model/HttpEntity.scala @@ -86,6 +86,45 @@ sealed trait HttpEntity extends jm.HttpEntity { */ def withContentType(contentType: ContentType): HttpEntity + /** + * Apply the given size limit to this entity by returning a new entity instance which automatically verifies that the + * data stream encapsulated by this instance produces at most `maxBytes` data bytes. In case this verification fails + * the respective stream will be terminated with an `EntityStreamException` either directly at materialization + * time (if the Content-Length is known) or whenever more data bytes than allowed have been read. + * + * When called on `Strict` entities the method will return the entity itself if the length is within the bound, + * otherwise a `Default` entity with a single element data stream. This allows for potential refinement of the + * entity size limit at a later point (before materialization of the data stream). + * + * By default all message entities produced by the HTTP layer automatically carry the limit that is defined in the + * application's `max-content-length` config setting. If the entity is transformed in a way that changes the + * Content-Length and then another limit is applied then this new limit will be evaluated against the new + * Content-Length. If the entity is transformed in a way that changes the Content-Length and no new limit is applied + * then the previous limit will be applied against the previous Content-Length. + * + * Note that the size limit applied via this method will only have any effect if the `Source` instance contained + * in this entity has been appropriately modified via the `HttpEntity.limitable` method. For all entities created + * by the HTTP layer itself this is always the case, but if you create entities yourself and would like them to + * properly respect limits defined via this method you need to make sure to apply `HttpEntity.limitable` yourself. + */ + override def withSizeLimit(maxBytes: Long): HttpEntity + + /** + * Lift the size limit from this entity by returning a new entity instance which skips the size verification. + * + * By default all message entities produced by the HTTP layer automatically carry the limit that is defined in the + * application's `max-content-length` config setting. It is recommended to always keep an upper limit on accepted + * entities to avoid potential attackers flooding you with too large requests/responses, so use this method with caution. + * + * Note that the size limit applied via this method will only have any effect if the `Source` instance contained + * in this entity has been appropriately modified via the `HttpEntity.limitable` method. For all entities created + * by the HTTP layer itself this is always the case, but if you create entities yourself and would like them to + * properly respect limits defined via this method you need to make sure to apply `HttpEntity.limitable` yourself. + * + * See [[withSizeLimit]] for more details. + */ + override def withoutSizeLimit: HttpEntity + /** Java API */ override def getContentType: jm.ContentType = contentType diff --git a/akka-http-core/src/test/scala/akka/http/scaladsl/model/HttpEntitySpec.scala b/akka-http-core/src/test/scala/akka/http/scaladsl/model/HttpEntitySpec.scala index 9290f71655..7a533830a7 100755 --- a/akka-http-core/src/test/scala/akka/http/scaladsl/model/HttpEntitySpec.scala +++ b/akka-http-core/src/test/scala/akka/http/scaladsl/model/HttpEntitySpec.scala @@ -157,20 +157,50 @@ class HttpEntitySpec extends FreeSpec with MustMatchers with BeforeAndAfterAll { withReturnType[UniversalEntity](Strict(tpe, abc).withoutSizeLimit) withReturnType[RequestEntity](Strict(tpe, abc).asInstanceOf[RequestEntity].withoutSizeLimit) withReturnType[ResponseEntity](Strict(tpe, abc).asInstanceOf[ResponseEntity].withoutSizeLimit) + withReturnType[HttpEntity](Strict(tpe, abc).asInstanceOf[HttpEntity].withoutSizeLimit) } "Default" in { withReturnType[Default](Default(tpe, 11, source(abc, de, fgh, ijk)).withoutSizeLimit) withReturnType[RequestEntity](Default(tpe, 11, source(abc, de, fgh, ijk)).asInstanceOf[RequestEntity].withoutSizeLimit) withReturnType[ResponseEntity](Default(tpe, 11, source(abc, de, fgh, ijk)).asInstanceOf[ResponseEntity].withoutSizeLimit) + withReturnType[HttpEntity](Default(tpe, 11, source(abc, de, fgh, ijk)).asInstanceOf[HttpEntity].withoutSizeLimit) } "CloseDelimited" in { withReturnType[CloseDelimited](CloseDelimited(tpe, source(abc, de, fgh, ijk)).withoutSizeLimit) withReturnType[ResponseEntity](CloseDelimited(tpe, source(abc, de, fgh, ijk)).asInstanceOf[ResponseEntity].withoutSizeLimit) + withReturnType[HttpEntity](CloseDelimited(tpe, source(abc, de, fgh, ijk)).asInstanceOf[HttpEntity].withoutSizeLimit) } "Chunked" in { withReturnType[Chunked](Chunked(tpe, source(Chunk(abc), Chunk(fgh), Chunk(ijk), LastChunk)).withoutSizeLimit) withReturnType[RequestEntity](Chunked(tpe, source(Chunk(abc), Chunk(fgh), Chunk(ijk), LastChunk)).asInstanceOf[RequestEntity].withoutSizeLimit) withReturnType[ResponseEntity](Chunked(tpe, source(Chunk(abc), Chunk(fgh), Chunk(ijk), LastChunk)).asInstanceOf[ResponseEntity].withoutSizeLimit) + withReturnType[HttpEntity](Chunked(tpe, source(Chunk(abc), Chunk(fgh), Chunk(ijk), LastChunk)).asInstanceOf[HttpEntity].withoutSizeLimit) + } + } + "support withSizeLimit" - { + "Strict" in { + HttpEntity.Empty.withSizeLimit(123L) + withReturnType[UniversalEntity](Strict(tpe, abc).withSizeLimit(123L)) + withReturnType[RequestEntity](Strict(tpe, abc).asInstanceOf[RequestEntity].withSizeLimit(123L)) + withReturnType[ResponseEntity](Strict(tpe, abc).asInstanceOf[ResponseEntity].withSizeLimit(123L)) + withReturnType[HttpEntity](Strict(tpe, abc).asInstanceOf[HttpEntity].withSizeLimit(123L)) + } + "Default" in { + withReturnType[Default](Default(tpe, 11, source(abc, de, fgh, ijk)).withoutSizeLimit) + withReturnType[RequestEntity](Default(tpe, 11, source(abc, de, fgh, ijk)).asInstanceOf[RequestEntity].withSizeLimit(123L)) + withReturnType[ResponseEntity](Default(tpe, 11, source(abc, de, fgh, ijk)).asInstanceOf[ResponseEntity].withSizeLimit(123L)) + withReturnType[HttpEntity](Default(tpe, 11, source(abc, de, fgh, ijk)).asInstanceOf[HttpEntity].withSizeLimit(123L)) + } + "CloseDelimited" in { + withReturnType[CloseDelimited](CloseDelimited(tpe, source(abc, de, fgh, ijk)).withSizeLimit(123L)) + withReturnType[ResponseEntity](CloseDelimited(tpe, source(abc, de, fgh, ijk)).asInstanceOf[ResponseEntity].withSizeLimit(123L)) + withReturnType[HttpEntity](CloseDelimited(tpe, source(abc, de, fgh, ijk)).asInstanceOf[HttpEntity].withSizeLimit(123L)) + } + "Chunked" in { + withReturnType[Chunked](Chunked(tpe, source(Chunk(abc), Chunk(fgh), Chunk(ijk), LastChunk)).withSizeLimit(123L)) + withReturnType[RequestEntity](Chunked(tpe, source(Chunk(abc), Chunk(fgh), Chunk(ijk), LastChunk)).asInstanceOf[RequestEntity].withSizeLimit(123L)) + withReturnType[ResponseEntity](Chunked(tpe, source(Chunk(abc), Chunk(fgh), Chunk(ijk), LastChunk)).asInstanceOf[ResponseEntity].withSizeLimit(123L)) + withReturnType[HttpEntity](Chunked(tpe, source(Chunk(abc), Chunk(fgh), Chunk(ijk), LastChunk)).asInstanceOf[HttpEntity].withSizeLimit(123L)) } } } diff --git a/project/MiMa.scala b/project/MiMa.scala index 6487630f79..f948d50454 100644 --- a/project/MiMa.scala +++ b/project/MiMa.scala @@ -742,7 +742,12 @@ object MiMa extends AutoPlugin { // #20123 ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.stream.scaladsl.FlowOps.recoverWithRetries") - ) + ), + "2.4.4" -> Seq( + // #20342 HttpEntity scaladsl overrides + ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.http.scaladsl.model.HttpEntity.withoutSizeLimit"), + ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.http.scaladsl.model.HttpEntity.withSizeLimit") + ) ) } }