5

To give some context, I have a <video> tag that has a src attribute that points to a method on my node.js server. That method gets an mp4 file from another server, or rather part of an mp4 file, depending on the Range HTTP header specified by the browser, for example - Range:bytes=0-.

Expected Behaviour (Chrome behaviour)

To prevent my node.js server from downloading the entire file from the third party server, I have implemented a max buffer of around 5MB to download at one time. So if the the user sends a request with to fetch the video with the headers

GET /play-test/videoId HTTP/1.1
Host: 127.0.0.1:8000
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Accept-Encoding: identity;q=1, *;q=0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36
Accept: */*
Referer: http://127.0.0.1:8000/movie/99861
Accept-Language: en-US,en;q=0.8,es;q=0.6
Range: bytes=0-

..then my server will respond with

HTTP/1.1 206 Partial Content
X-Powered-By: Express
Content-Range: bytes 0-5000000/415473786
Connection: keep-alive
Accept-Ranges: bytes
Content-Length: 5000001
Content-Type: video/mp4
Date: Tue, 20 Oct 2015 12:50:42 GMT

This, I believe, is a common enough pattern - although the client (in this case, the browser) has requested has requested bytes=0- (start to end), I have instead responded with the first 5MB and most importantly told the client that response contains only those 5MB out of a total of 415MB (Content-Range: bytes 0-5000000/415473786). The response is also has a status of 206 indicating that the response is a partial one.

In Chrome, this works as expected - just before the video has finished playing the first 5MB of video it makes another request to the same endpoint but with the headers

GET /play-test/videoId HTTP/1.1
Host: 127.0.0.1:8000
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Accept-Encoding: identity;q=1, *;q=0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36
Accept: */*
Referer: http://127.0.0.1:8000/movie/99861
Accept-Language: en-US,en;q=0.8,es;q=0.6
Range: bytes=5000001-

Again I respond with the maximum 5MB...

HTTP/1.1 206 Partial Content
X-Powered-By: Express
Content-Range: bytes 5000001-10000001/415473786
Connection: keep-alive
Accept-Ranges: bytes
Content-Length: 5000001
Content-Type: video/mp4
Date: Tue, 20 Oct 2015 12:51:08 GMT

This pattern continues until the video ends, the user pauses or skips using the seek bar, in which case the browser request a specific byte range for the time required. As I said, all works well in Chrome.

Firefox behaviour

Describing Firefox's behaviour is much more straight-forward that describing the correct behaviour!

Firefox request

Host: 127.0.0.1:8000
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:41.0) Gecko/20100101 Firefox/41.0
Accept: video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5
Accept-Language: en-US,en;q=0.5
Range: bytes=0-
Referer: http://127.0.0.1:8000/movie/272
Connection: keep-alive

Response

206 Partial Content
Accept-Ranges: bytes
Connection: keep-alive
Content-Length: 5000001
Content-Range: bytes 0-5000000/415473786
Content-Type: video/mp4
Date: Tue, 20 Oct 2015 13:15:49 GMT
X-Powered-By: Express

Then that's it - Firefox plays the first 5MB of video and the no further requests are made to the server. The seek bar displays the correct duration of the video but the seek bar is non-functional. When the user attempts to seek the video skips back to the beginning and plays the first 5MB again.

Any help would be appreciated.

Joe
  • 29,416
  • 12
  • 68
  • 88
garethdn
  • 12,022
  • 11
  • 49
  • 83
  • Does this answer your question? [How to make browser request smaller range with 206 Partial Content](https://stackoverflow.com/questions/36114598/how-to-make-browser-request-smaller-range-with-206-partial-content) – Joe Nov 30 '20 at 11:36
  • See https://bugzilla.mozilla.org/show_bug.cgi?id=570755 for this bug filed against Firefox, and https://bugzilla.mozilla.org/show_bug.cgi?id=1418430 (fixed in Firefox 59) which appears to address it in the specific case of video streaming. – Joe Nov 30 '20 at 11:42

1 Answers1

-1

the client (in this case, the browser) has requested has requested bytes=0- (start to end), I have instead responded with the first 5MB

I think that's your problem right there. The spec says that you should return the entire available byte range when a client ask for a range of 0-. You're not following the spec, hence why it doesn't work.

idbehold
  • 16,833
  • 5
  • 47
  • 74
  • Why then does FireFox perform a Range Request if it unknowingly of the content requests the full file anyways. This behaviour of FireFox deviates from other browsers including Chrome, IE, Edge and Safari. We support hosting large video file's of several GB. You suggest on FF i should send the entire file, then for FireFox to conclude it has fetched 2GB for the user only to watch the first 10MB? – Bas Goossen May 06 '17 at 12:52
  • @BasGoossen I'm not sure what type server-side language you're using but most have a way to know when the connection to the client has been closed. I'm assuming that what all of these browser do is make the initial GET with the `Range: 0-` because they need to handle the case where a web server doesn't handle Range requests. If the server **does** support Range requests then it will respond with the correct Content-Range header, but now that the browser knows this they will keep accepting the bytes being sent until they've filled some buffer. At which point the browser will abort the request. – idbehold May 06 '17 at 15:02
  • @BasGoossen when the user is approaching the end of the buffer the browser will send a new Range request since they know the server supports it. I think the problem you're having is that maybe Firefox's buffer is larger. What Firefox is expecting is for your server to keep sending bytes until Firefox's buffer has filled. Yes the logic in Firefox could be improved so that it handled a an incorrect Content-Range response header like you're sending, but according to the spec it really shouldn't have to. According to the spec you should be sending a `Content-Range: bytes 0-415473785/415473786`. – idbehold May 06 '17 at 15:08
  • @BasGoossen and then be listening for the connection to close. It does make things more complicated on your end, but that's the spec. – idbehold May 06 '17 at 15:09
  • Wel actually i am listening for the connection to close, but it does not. Even if i close the video element, and remove it from the DOM after the video is requested it keeps listening for a long time before closing the connection. This behaviour is also viewable in the developers console, the network tab. Eventually it does close, but only after more than 100MB or so. – Bas Goossen May 06 '17 at 17:05
  • @BasGoossen wait, so you continue to send bytes after the 5mb? – idbehold May 06 '17 at 17:57
  • Only if i fully follow the spec and send the first Partial response as 0-415473785/415473786 (It's not hard for me to alter the server response as i've written the server side code myself.) – Bas Goossen May 06 '17 at 20:13
  • Mozilla seem to acknowledge (https://bugzilla.mozilla.org/show_bug.cgi?id=570755) that the spec permits responding with a sub-range ("So its totally ok for the server to return 206 with a subset of the bytes requested (as long as the request was a range request).") – Joe Nov 30 '20 at 11:41