We're writing a Spring service that makes an HTTP endpoint available through which a video (or audio) file from an Amazon S3 store can be streamed. The basic idea is that you can type in an url in the Google Chrome address bar, and the service will fetch the file from S3 and stream it, in such a way that the user can start watching immediately without having to wait for a download to complete, and that the user can click on a random spot in the video's progress bar and immediately start watching the video from that spot.
The way I understand this should work in theory, is that Chrome starts downloading the file. The service responds with HTTP 200 and includes an Accept-Ranges: bytes
and a Content-Length: filesize
header. The filesize
is known, because we can query that as metadata from S3 without fetching the entire file. Including these headers causes the browser to cancel the download, and request the file again with a Range: bytes=0-whatever
header (where whatever
is some chunk size that Chrome decides). The service then responds with HTTP 206 (Partial content) and the requested byte range, which we can determine easily because S3 supports the same range protocol. Chrome then requests successive chunks from the service, until the stream ends.
On the Spring side, we're sending the data out in a ResponseEntity<InputStreamResource>
(as per this SO answer).
However, we observe in practice that while Chrome's cancels its first request after a few hundred bytes. However, it sends a second request with a Range: bytes=0-
header, effectively asking for the entire file. The server responds with an HTTP 206. As a result, is has only downloaded a few hundred bytes of video, and the video obviously doesn't start playing.
Interestingly, in Firefox it all works properly. Unfortunately, our app needs to support Chrome. Are we missing some part of the protocol?