Skip to content

Commit

Permalink
=htp #961 Make extractStrictEntity provide strict entity for inner ro…
Browse files Browse the repository at this point in the history
…utes

Use toStrictEntity so the request is updated to provide the strict
entity for inner routes ensuring that the request data is only read
once.
  • Loading branch information
jonas committed Mar 29, 2017
1 parent edc940b commit 938f626
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ package directives
import akka.http.scaladsl.model._
import akka.stream.scaladsl.Source
import akka.util.ByteString
import scala.concurrent.duration._
import java.util.concurrent.ThreadLocalRandom

class BasicDirectivesSpec extends RoutingSpec {

Expand Down Expand Up @@ -107,4 +109,37 @@ class BasicDirectivesSpec extends RoutingSpec {
} ~> check { responseAs[String] shouldEqual "" }
}
}
}

"The `extractStrictEntity` directive" should {
"change request to contain strict entity for inner routes" in {
val chunks = () List("Akka", "HTTP").map(HttpEntity.Chunk(_)).iterator
val entity = HttpEntity.Chunked(ContentTypes.`text/plain(UTF-8)`, Source.fromIterator(chunks))

Post("/abc", entity) ~> {
extractRequestEntity { before
extractStrictEntity(200.millis) { _
extractRequestEntity { after
complete(Seq(before, after).map(_.isInstanceOf[HttpEntity.Strict]).mkString(" => "))
}
}
}
} ~> check { responseAs[String] shouldEqual "false => true" }
}

"only consume data once when nested" in {
val randomStream = Iterator.continually(ThreadLocalRandom.current.nextInt(0, 2).toString).take(100)
val chunks = Source.fromIterator(() randomStream.map(HttpEntity.Chunk(_)))
val entity = HttpEntity.Chunked(ContentTypes.`text/plain(UTF-8)`, chunks)

Post("/abc", entity) ~> {
extractStrictEntity(200.millis) { outer
extractStrictEntity(200.millis) { inner
/* Check that the string representations of the outer and inner
* random number sequences are identical. */
complete(Seq(outer, inner).map(_.data.utf8String).distinct.size.toString)
}
}
} ~> check { responseAs[String] shouldEqual "1" }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -336,19 +336,7 @@ trait BasicDirectives {
* @group basic
*/
def extractStrictEntity(timeout: FiniteDuration): Directive1[HttpEntity.Strict] =
extract { ctx
import ctx.materializer

ctx.request.entity.toStrict(timeout)

}.flatMap { entity
import FutureDirectives._

onComplete(entity).flatMap {
case Success(x) provide(x)
case Failure(t) StandardRoute(_.fail(t))
}
}
toStrictEntity(timeout) & extract(_.request.entity.asInstanceOf[HttpEntity.Strict])

/**
* WARNING: This will read the entire request entity into memory regardless of size and effectively disable streaming.
Expand Down

0 comments on commit 938f626

Please sign in to comment.