Handling Partial Content Requests (HTTP 206) for audio streaming

Web content streaming requires sending partial files between a server and a client, most often a web browser. This is a strict requirement for controlling audio playback using an HTML audio tag. While a simple HTTP 200 response containing the whole file will work for most use cases, it will not allow proper playback control, as is to be expected of a respectable music streaming app. 

Request

First, the browser requests a partial file by including the Range header, which encodes the position in bytes. The first word refers to the allowed range type, bytes in our case.

bytes=<start_offset>-<end_offset>

Response

Next, the server should reply that :

  1. it accepts file ranges, using the Accept-Ranges: bytes header;
  2. it is sending an incomplete file, using the 206 HTTP status code, containing a specific number of bytes with the Content-Length header;
  3. and that it is sending the requested byte range, in the Content-Range header.

The Content-Range header should contain the first byte index, the last, and the total file size.

Content-Range: bytes <start>-<end>/<total>

Note that the last fragment’s “end” offset should be exactly one less than the total file size.

Example

Below is a quick and dirty example for sending a partial file in python.

if request.headers['HTTP_RANGE']:
  total_size = os.path.getsize(filename)
  http_range = request.headers['HTTP_RANGE']
  start = int(http_range.split("=")[1].split("-")[0])
  end = start + 2000000 if http_range.split("-")[1] == '' else int(http_range.split("-")[1])
  
  if end > total_size:
      end = total_size - 1
  
  with open(filename, 'rb') as f:
    f.seek(start)
    body = f.read(end - start)
  return Response(206, {"Content-Type": "application/octet-stream",
                        "Accept-Ranges": "bytes",
                        "Content-Range": "bytes {}-{}/{}".format(start, end, total_size)},
                  body)

 

Leave a Reply

Your email address will not be published. Required fields are marked *