[ticket/14774] Support partial downloads of attachments

phpBB already had limited support for partial downloads, but only for
resuming downloads, disregarding any range ending before EOF.

WebKit on iOS and OS X uses partial downloads when fetching media
files. Previously, only MP3 attachments could play directly in the
browser, reported as a live stream, but with this change, all
supported media formats should render as expected.

Tested using cURL by verifying that partial downloads give exactly the
same results compared to Apache.

PHPBB3-14774
This commit is contained in:
Dan Villiom Podlaski Christiansen 2016-09-07 03:58:33 +02:00
parent 65921a7891
commit d2750b650f
2 changed files with 119 additions and 59 deletions

View file

@ -254,12 +254,22 @@ function send_file_to_browser($attachment, $upload_dir, $category)
send_status_line(206, 'Partial Content');
header('Content-Range: bytes ' . $range['byte_pos_start'] . '-' . $range['byte_pos_end'] . '/' . $range['bytes_total']);
header('Content-Length: ' . $range['bytes_requested']);
}
// First read chunks
while (!feof($fp) && ftell($fp) < $range['byte_pos_end'] - 8192)
{
echo fread($fp, 8192);
}
// Then, read the remainder
echo fread($fp, $range['bytes_requested'] % 8192);
}
else
{
while (!feof($fp))
{
echo fread($fp, 8192);
}
}
fclose($fp);
}
else
@ -529,6 +539,9 @@ function phpbb_find_range_request()
*/
function phpbb_parse_range_request($request_array, $filesize)
{
$first_byte_pos = -1;
$last_byte_pos = -1;
// Go through all ranges
foreach ($request_array as $range_string)
{
@ -540,36 +553,37 @@ function phpbb_parse_range_request($request_array, $filesize)
continue;
}
// Substitute defaults
if ($range[0] === '')
{
// Return last $range[1] bytes.
if (!$range[1])
{
continue;
$range[0] = 0;
}
if ($range[1] >= $filesize)
if ($range[1] === '')
{
$range[1] = $filesize - 1;
}
if ($last_byte_pos >= 0 && $last_byte_pos + 1 != $range[0])
{
// We only support contiguous ranges, no multipart stuff :(
return false;
}
$first_byte_pos = $filesize - (int) $range[1];
$last_byte_pos = $filesize - 1;
}
else
{
// Return bytes from $range[0] to $range[1]
$first_byte_pos = (int) $range[0];
$last_byte_pos = (int) $range[1];
if ($last_byte_pos && $last_byte_pos < $first_byte_pos)
if ($range[1] && $range[1] < $range[0])
{
// The requested range contains 0 bytes.
continue;
}
// Return bytes from $range[0] to $range[1]
if ($first_byte_pos < 0)
{
$first_byte_pos = (int) $range[0];
}
$last_byte_pos = (int) $range[1];
if ($first_byte_pos >= $filesize)
{
// Requested range not satisfiable
@ -583,10 +597,9 @@ function phpbb_parse_range_request($request_array, $filesize)
}
}
// We currently do not support range requests that end before the end of the file
if ($last_byte_pos != $filesize - 1)
if ($first_byte_pos < 0 || $last_byte_pos < 0)
{
continue;
return false;
}
return array(
@ -595,7 +608,6 @@ function phpbb_parse_range_request($request_array, $filesize)
'bytes_requested' => $last_byte_pos - $first_byte_pos + 1,
'bytes_total' => $filesize,
);
}
}
/**

View file

@ -45,23 +45,71 @@ class phpbb_download_http_byte_range_test extends phpbb_test_case
public function parse_range_request_data()
{
return array(
// Does not read until the end of file.
// Valid request
array(
array('3-4'),
10,
false,
array(
'byte_pos_start' => 3,
'byte_pos_end' => 4,
'bytes_requested' => 2,
'bytes_total' => 10,
),
),
// Valid request, handle second range.
// Get the beginning
array(
array('-5'),
10,
array(
'byte_pos_start' => 0,
'byte_pos_end' => 5,
'bytes_requested' => 6,
'bytes_total' => 10,
),
),
// Get the end
array(
array('5-'),
10,
array(
'byte_pos_start' => 5,
'byte_pos_end' => 9,
'bytes_requested' => 5,
'bytes_total' => 10,
),
),
// Overlong request
array(
array('3-20'),
10,
array(
'byte_pos_start' => 3,
'byte_pos_end' => 9,
'bytes_requested' => 7,
'bytes_total' => 10,
),
),
// Multiple, contiguous range
array(
array('10-20', '21-30'),
125,
array(
'byte_pos_start' => 10,
'byte_pos_end' => 30,
'bytes_requested' => 21,
'bytes_total' => 125,
)
),
// We don't do multiple, non-contiguous range
array(
array('0-0', '120-125'),
125,
array(
'byte_pos_start' => 120,
'byte_pos_end' => 124,
'bytes_requested' => 5,
'bytes_total' => 125,
)
false,
),
);
}