Skip to main content

Range Directives

withRangeSupport​

Adds support for RFC 7233 byte ranges to the inner route: every GET response carries Accept-Ranges: bytes, and a GET request with a Range: bytes=... header receives a 206 Partial Content slice of a 200 response, with the Content-Range header set. Several ranges are answered as multipart/byteranges; ranges that are at most 80 bytes apart (Pekko's range-coalescing-threshold) are merged into one part, and the parts are sent in ascending order in one pass over the body. Responses other than 200, and non-GET requests, pass through untouched.

A request whose ranges all lie outside the entity is rejected with an UnsatisfiableRangeRejection (416 Range Not Satisfiable with Content-Range: bytes */<length> when sealed). A 200 response without Content-Length cannot be sliced and is rejected the same way, as in Pekko; its sealed 416 carries no Content-Range. More than 16 ranges reject with a TooManyRangesRejection (416). Requests whose Range header is invalid (a last position before the first; RFC 7233 requires the header to be ignored) or uses a unit other than bytes are served in full. Combine with conditional by putting conditional on the outside.

Like Pekko, the parts of a multipart/byteranges response are sent in ascending order (both read the body once). Two differences: for a response of unknown length Pekko's sealed 416 carries Content-Range: bytes */-1, which is not valid RFC 7233 syntax, so stir omits the header; and bytes=-0, which http4s parses as bytes=0-, serves the whole entity as a 206 where Pekko answers 416.

val route: Route = path("video.mp4") {
withRangeSupport {
getFromFile("/var/media/video.mp4")
}
}

withRangeSupportLimits(rangeCountLimit, rangeCoalescingThreshold) sets the limit on the number of ranges and the distance under which neighbouring ranges are merged.