I am trying to stream a mp3 file to SoundManager with the HTTP/1.1 Partial Content header to allow some protection to my media files and also allow users to seek to different locations of the track. The code I have works in IE7-10, Firefox, Safari and Opera but refuses to work in Google Chrome. If I was to remove the Partial Content header it would play the file but wouldn't allow the user to seek.
When examining the network tab in Chrome's dev tools there are 2 requests, one is stuck with the status of pending and the other has the status of canceled. both of these requests have a size of 13B. The file I am trying to play is 9.11MB.
Below is the code I am using to set the headers and read the file.
$name = $_GET['name'];
$file_size = filesize($file);
$file = "C:\xampp\htdocs\..\protected\music\testsong.mp3"; // song, user and all other checks are performed before this to get the file path.
$etag = md5(uniqid(rand(), true));
$open_file = fopen($file, 'rb');
if( !$open_file ) throw new Exception('Could not open file');
$start = 0;
$end = $file_size - 1;
if( isset($_SERVER['HTTP_RANGE']) && !empty($_SERVER['HTTP_RANGE']) ) {
$range = explode('-', substr($_SERVER['HTTP_RANGE'], strlen('bytes=')));
$start = $range[0];
if( $range[1] > 0 ) $end = $range[1];
header('HTTP/1.1 206 Partial Content');
header('Status: 206');
header('Accept-Ranges: bytes');
header('Content-Range: ' . $_SERVER['HTTP_RANGE'] . '/' . $file_size);
header('Content-Length: ' . ($end - $start + 1));
} else header('Content-Length: ' . $file_size);
header('Content-Type: ' . $content_type);
if( $download ) header('Content-Disposition: attachment; filename="' . $name . '"');
header('Last-Modified: ' . date('D, d M Y H:i:s \G\M\T', filemtime($file)));
header('ETag: "' . $etag . '"');
header('Expires: 0');
header('Pragma: no-cache');
header('Cache-Control: must-revalidate');
if( $start > 0 ) fseek($open_file, $start);
$bytes_position = $start;
while( !feof($open_file) && $bytes_position <= $end ) {
if( connection_aborted() || connection_status() != 0 ) throw New Exception('Connection Aborted');
$chunk = 1048000;
if( $bytes_position + $chunk > $end + 1 ) $chunk = $end - $bytes_position + 1;
$chunk = fread($open_file, $chunk);
if( !$chunk ) throw New Exception('Could not read file');
print($chunk);
flush();
$bytes_position += $chunk;
}
fclose($open_file);
The Chrome WebRequests API mentions that specific request headers are not available to the onBeforeSendHeaders event, meaning that extensions cannot read and/or modify these headers. Here is an excerpt from the documentation:
Internally, one URL request can be split into several HTTP requests (for example to fetch individual byte ranges from a large file) or can be handled by the network stack without communicating with the network. For this reason, the API does not provide the final HTTP headers that are sent to the network.
HTTP requests contain headers such as User-Agent or Content-Type. Apart from headers attached by browsers, Android apps may add extra headers, like Cookie or Referrer through the EXTRA_HEADERS Intent extra. For security reasons, Chrome filters some of the extra headers depending on how and where an intent is launched.
Starting from Chrome 79, request header modifications affect Cross-Origin Resource Sharing (CORS) checks. If modified headers for cross-origin requests do not meet the criteria, it will result in sending a CORS preflight to ask the server if such headers can be accepted.
I'm pretty sure the problem you're having is with the content-range header
try this instead
header('Content-Range: bytes ' . $start . '-' . $end . '/' . $file_size);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With