1
0
mirror of https://github.com/phpbb/phpbb.git synced 2025-01-17 14:18:24 +01:00

[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,11 +254,21 @@ 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']);
}
while (!feof($fp))
// 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
{
echo fread($fp, 8192);
while (!feof($fp))
{
echo fread($fp, 8192);
}
}
fclose($fp);
}
@ -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,62 +553,61 @@ function phpbb_parse_range_request($request_array, $filesize)
continue;
}
// Substitute defaults
if ($range[0] === '')
{
// Return last $range[1] bytes.
if (!$range[1])
{
continue;
}
if ($range[1] >= $filesize)
{
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)
{
// The requested range contains 0 bytes.
continue;
}
if ($first_byte_pos >= $filesize)
{
// Requested range not satisfiable
return false;
}
// Adjust last-byte-pos if it is absent or greater than the content.
if ($range[1] === '' || $last_byte_pos >= $filesize)
{
$last_byte_pos = $filesize - 1;
}
$range[0] = 0;
}
// We currently do not support range requests that end before the end of the file
if ($last_byte_pos != $filesize - 1)
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;
}
if ($range[1] && $range[1] < $range[0])
{
// The requested range contains 0 bytes.
continue;
}
return array(
'byte_pos_start' => $first_byte_pos,
'byte_pos_end' => $last_byte_pos,
'bytes_requested' => $last_byte_pos - $first_byte_pos + 1,
'bytes_total' => $filesize,
);
// 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
return false;
}
// Adjust last-byte-pos if it is absent or greater than the content.
if ($range[1] === '' || $last_byte_pos >= $filesize)
{
$last_byte_pos = $filesize - 1;
}
}
if ($first_byte_pos < 0 || $last_byte_pos < 0)
{
return false;
}
return array(
'byte_pos_start' => $first_byte_pos,
'byte_pos_end' => $last_byte_pos,
'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,
),
);
}