Warm tip: This article is reproduced from serverfault.com, please click

How best to respond to an open HTTP range request

发布于 2020-08-27 10:26:29

I am currently going through the process of attempting to respond to HTTP Range Requests so that video can be streamed from our server and also satisfy Safari actually playing the video.

I do have the added complication that the video file is encrypted on disk preventing me from being able to Seek in the stream but that is not really within the scope of this question.

I have noticed that Chrome and at least the new Edge request open ranges (0 - ). What is the correct thing to respond with here? Currently I respond with the full video which I understand completely defeats the purpose of streaming.

var range = context.Request.Headers["Range"].Split('=', '-');
var startByteIndex = int.Parse(range[1]);
// Quite a few browers send open ended requests for the range (e.g. 0- ).
long endByteIndex;
if (!long.TryParse(range[2], out endByteIndex))
{
    endByteIndex = streamLength - 1;
}

Below is my full attempt so far at responding.

if (!string.IsNullOrEmpty(context.Request.Headers["Range"]))
{
    var range = context.Request.Headers["Range"].Split('=', '-');
    var startByteIndex = int.Parse(range[1]);
    // Quite a few browers send open ended requests for the range (e.g. 0- ).
    long endByteIndex;
    if (!long.TryParse(range[2], out endByteIndex))
    {
        endByteIndex = streamLength - 1;
    }

    Debug.WriteLine("Range request for " + context.Request.Headers["Range"]);

    // Make sure the request is within the bounds of the video.
    if (endByteIndex >= streamLength)
    {
        context.Response.StatusCode = (int)HttpStatusCode.RequestedRangeNotSatisfiable;
        return false;
    }

    var currentIndex = 0;

    // SEEKING IS NOT WHOLE AND COMPLETE.
    // Get to the requested start. We are not allowed to seek with CBC + AES.
    while (currentIndex < startByteIndex) // TODO: we could probably work out a more suitable buffer size here to get to the start index.
    {
        var dummy = new byte[bufferLength];

        var a = videoReadStream.Read(dummy, 0, bufferLength);
        currentIndex += bufferLength;
    }

    // Fast but unreliable given AES + CBC.
    //fileStream.Seek(startByteIndex, SeekOrigin.Begin);

    dataToRead = endByteIndex - startByteIndex + 1;

    // Supply the relevant partial content headers.
    context.Response.StatusCode = (int)HttpStatusCode.PartialContent;
    context.Response.AddHeader("Content-Range", $"bytes {startByteIndex}-{endByteIndex}/{streamLength}");
    context.Response.AddHeader("Content-Length", dataToRead.ToString());
}
else
{
    context.Response.AddHeader("Cache-Control", "private, max-age=1200");
    context.Response.Cache.SetExpires(DateTime.Now.AddMinutes(20));
    context.Response.AddHeader("content-disposition", "inline;filename=" + fileID);

    context.Response.AddHeader("Accept-Ranges", "bytes");
}

var buffer = new byte[bufferLength];
while (dataToRead > 0 && context.Response.IsClientConnected)
{
    videoReadStream.Read(buffer, 0, bufferLength);

    // Write the data to the current output stream.
    context.Response.OutputStream.Write(buffer, 0, bufferLength);

    // Flush the data to the HTML output.
    context.Response.Flush();

    buffer = new byte[bufferLength];
    dataToRead -= bufferLength;
}

Most frustratingly I notice that Edge (I haven't tested others yet) always seem to send an open request

Range request for bytes=0-
Range request for bytes=1867776-
Range request for bytes=3571712-
Range request for bytes=5341184-
Range request for bytes=7176192-
Range request for bytes=9273344-
Range request for bytes=10977280-
Range request for bytes=12943360-
Range request for bytes=14614528-
Range request for bytes=16384000-
Range request for bytes=18087936-
Range request for bytes=19955712-
Range request for bytes=21823488-
Range request for bytes=23625728-
Range request for bytes=25690112-
Range request for bytes=27525120-
Range request for bytes=39256064-
Range request for bytes=41222144-
Range request for bytes=42270720-

Should I just decide on a chunk size to respond with and stick with that? I notice that if I did respond with chunks of just 3 in size then Edge does request a lot more ranges in increments of 3.

Questioner
Bijington
Viewed
1
Joe 2020-11-30 19:45:13

This is a similar, but not identical, question to What byte range 0- means.

What is the correct thing to respond with here?

The correct thing to respond with here is the entire resource. However, as this client included a Range header, you may also respond with a 206 Partial content and a subset of the file.

Should I just decide on a chunk size to respond with and stick with that?

Pretty much, based on what's efficient for your server. Note that, as in Firefox won't request further data after receiving 206 with specified content range, you may encounter browsers that don't do the right thing.