aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Villiom Podlaski Christiansen <danchr@gmail.com>2016-09-07 03:58:33 +0200
committerDan Villiom Podlaski Christiansen <danchr@gmail.com>2016-09-07 03:58:33 +0200
commitd2750b650fb3600ef2cf7b7139998e1a71c7d5ef (patch)
tree7c7a018e29f0fcd293609bee8ae32a6b42c5c557
parent65921a78915e0c9bf494e8c780681522644abf7d (diff)
downloadforums-d2750b650fb3600ef2cf7b7139998e1a71c7d5ef.tar
forums-d2750b650fb3600ef2cf7b7139998e1a71c7d5ef.tar.gz
forums-d2750b650fb3600ef2cf7b7139998e1a71c7d5ef.tar.bz2
forums-d2750b650fb3600ef2cf7b7139998e1a71c7d5ef.tar.xz
forums-d2750b650fb3600ef2cf7b7139998e1a71c7d5ef.zip
[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
-rw-r--r--phpBB/includes/functions_download.php98
-rw-r--r--tests/download/http_byte_range_test.php62
2 files changed, 110 insertions, 50 deletions
diff --git a/phpBB/includes/functions_download.php b/phpBB/includes/functions_download.php
index 2c6f62227c..78ccebf276 100644
--- a/phpBB/includes/functions_download.php
+++ b/phpBB/includes/functions_download.php
@@ -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;
- }
+ $range[0] = 0;
+ }
- if ($range[1] >= $filesize)
- {
- return false;
- }
+ if ($range[1] === '')
+ {
+ $range[1] = $filesize - 1;
+ }
- $first_byte_pos = $filesize - (int) $range[1];
- $last_byte_pos = $filesize - 1;
+ if ($last_byte_pos >= 0 && $last_byte_pos + 1 != $range[0])
+ {
+ // We only support contiguous ranges, no multipart stuff :(
+ return false;
}
- else
+
+ if ($range[1] && $range[1] < $range[0])
{
- // Return bytes from $range[0] to $range[1]
+ // 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 ($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;
- }
+ $last_byte_pos = (int) $range[1];
- // 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 >= $filesize)
+ {
+ // Requested range not satisfiable
+ return false;
}
- // We currently do not support range requests that end before the end of the file
- if ($last_byte_pos != $filesize - 1)
+ // Adjust last-byte-pos if it is absent or greater than the content.
+ if ($range[1] === '' || $last_byte_pos >= $filesize)
{
- continue;
+ $last_byte_pos = $filesize - 1;
}
+ }
- 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,
- );
+ 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,
+ );
}
/**
diff --git a/tests/download/http_byte_range_test.php b/tests/download/http_byte_range_test.php
index f920299048..8975ec1799 100644
--- a/tests/download/http_byte_range_test.php
+++ b/tests/download/http_byte_range_test.php
@@ -45,24 +45,72 @@ 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('0-0', '120-125'),
- 125,
+ array('-5'),
+ 10,
array(
- 'byte_pos_start' => 120,
- 'byte_pos_end' => 124,
+ '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,
+ false,
+ ),
);
}
}