withRangeSupport
Signature
def withRangeSupport(): Directive0
def withRangeSupport(rangeCountLimit: Int, rangeCoalescingThreshold:Long): Directive0
The signature shown is simplified, the real signature uses magnets. [1]
[1] | See The Magnet Pattern for an explanation of magnet-based overloading. |
Description
Transforms the response from its inner route into a 206 Partial Content response if the client requested only part of the resource with a Range header.
Augments responses to GET requests with an Accept-Ranges: bytes header and converts them into partial responses if the request contains a valid Range request header. The requested byte-ranges are coalesced (merged) if they lie closer together than the specified rangeCoalescingThreshold argument.
In order to prevent the server from becoming overloaded with trying to prepare multipart/byteranges responses for high numbers of potentially very small ranges the directive rejects requests requesting more than rangeCountLimit ranges with a TooManyRangesRejection. Requests with unsatisfiable ranges are rejected with an UnsatisfiableRangeRejection.
The withRangeSupport() form (without parameters) uses the range-coalescing-threshold and range-count-limit settings from the akka.http.routing configuration.
This directive is transparent to non-GET requests.
See also: https://tools.ietf.org/html/rfc7233
Example
val route =
withRangeSupport {
complete("ABCDEFGH")
}
Get() ~> addHeader(Range(ByteRange(3, 4))) ~> route ~> check {
headers should contain(`Content-Range`(ContentRange(3, 4, 8)))
status shouldEqual StatusCodes.PartialContent
responseAs[String] shouldEqual "DE"
}
// we set "akka.http.routing.range-coalescing-threshold = 2"
// above to make sure we get two BodyParts
Get() ~> addHeader(Range(ByteRange(0, 1), ByteRange(1, 2), ByteRange(6, 7))) ~> route ~> check {
headers.collectFirst { case `Content-Range`(_, _) => true } shouldBe None
val responseF = responseAs[Multipart.ByteRanges].parts
.runFold[List[Multipart.ByteRanges.BodyPart]](Nil)((acc, curr) => curr :: acc)
val response = Await.result(responseF, 3.seconds).reverse
response should have length 2
val part1 = response(0)
part1.contentRange === ContentRange(0, 2, 8)
part1.entity should matchPattern {
case HttpEntity.Strict(_, bytes) if bytes.utf8String == "ABC" =>
}
val part2 = response(1)
part2.contentRange === ContentRange(6, 7, 8)
part2.entity should matchPattern {
case HttpEntity.Strict(_, bytes) if bytes.utf8String == "GH" =>
}
}
Contents