mirror of
https://github.com/phpbb/phpbb.git
synced 2025-02-24 20:13:22 +01:00
Merge branch 'ticket/bantu/9627' into develop
* ticket/bantu/9627: [ticket/9627] Prefix function names with 'phpbb_'. [ticket/9627] Adding unit tests for http_byte_range(). [ticket/9627] Adding download unit tests. [ticket/9627] Split http_range_request() into several functions. [ticket/9627] Make sure range request reads till the end of the file. [ticket/9627] Make use of 'static' since the function is called more than once [ticket/9627] Make sure the database record for the filesize is correct. [ticket/9627] Do not increase download counter if file is requested partially. [ticket/9627] Support for HTTP range requests in download/file.php
This commit is contained in:
commit
1d81671b7e
@ -246,7 +246,7 @@ if (!download_allowed())
|
||||
$download_mode = (int) $extensions[$attachment['extension']]['download_mode'];
|
||||
|
||||
// Fetching filename here to prevent sniffing of filename
|
||||
$sql = 'SELECT attach_id, is_orphan, in_message, post_msg_id, extension, physical_filename, real_filename, mimetype, filetime
|
||||
$sql = 'SELECT attach_id, is_orphan, in_message, post_msg_id, extension, physical_filename, real_filename, mimetype, filesize, filetime
|
||||
FROM ' . ATTACHMENTS_TABLE . "
|
||||
WHERE attach_id = $download_id";
|
||||
$result = $db->sql_query_limit($sql, 1);
|
||||
@ -275,7 +275,7 @@ if ($thumbnail)
|
||||
{
|
||||
$attachment['physical_filename'] = 'thumb_' . $attachment['physical_filename'];
|
||||
}
|
||||
else if (($display_cat == ATTACHMENT_CATEGORY_NONE/* || $display_cat == ATTACHMENT_CATEGORY_IMAGE*/) && !$attachment['is_orphan'])
|
||||
else if (($display_cat == ATTACHMENT_CATEGORY_NONE/* || $display_cat == ATTACHMENT_CATEGORY_IMAGE*/) && !$attachment['is_orphan'] && !phpbb_http_byte_range($attachment['filesize']))
|
||||
{
|
||||
// Update download count
|
||||
$sql = 'UPDATE ' . ATTACHMENTS_TABLE . '
|
||||
|
@ -157,6 +157,16 @@ function send_file_to_browser($attachment, $upload_dir, $category)
|
||||
trigger_error('UNABLE_TO_DELIVER_FILE');
|
||||
}
|
||||
|
||||
// Make sure the database record for the filesize is correct
|
||||
if ($size > 0 && $size != $attachment['filesize'])
|
||||
{
|
||||
// Update database record
|
||||
$sql = 'UPDATE ' . ATTACHMENTS_TABLE . '
|
||||
SET filesize = ' . (int) $size . '
|
||||
WHERE attach_id = ' . (int) $attachment['attach_id'];
|
||||
$db->sql_query($sql);
|
||||
}
|
||||
|
||||
// Now the tricky part... let's dance
|
||||
header('Pragma: public');
|
||||
|
||||
@ -226,6 +236,16 @@ function send_file_to_browser($attachment, $upload_dir, $category)
|
||||
|
||||
if ($fp !== false)
|
||||
{
|
||||
// Deliver file partially if requested
|
||||
if ($range = phpbb_http_byte_range($size))
|
||||
{
|
||||
fseek($fp, $range['byte_pos_start']);
|
||||
|
||||
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))
|
||||
{
|
||||
echo fread($fp, 8192);
|
||||
@ -407,3 +427,151 @@ function file_gc()
|
||||
$db->sql_close();
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP range support (RFC 2616 Section 14.35)
|
||||
*
|
||||
* Allows browsers to request partial file content
|
||||
* in case a download has been interrupted.
|
||||
*
|
||||
* @param int $filesize the size of the file in bytes we are about to deliver
|
||||
*
|
||||
* @return mixed false if the whole file has to be delivered
|
||||
* associative array on success
|
||||
*/
|
||||
function phpbb_http_byte_range($filesize)
|
||||
{
|
||||
// Only call find_range_request() once.
|
||||
static $request_array;
|
||||
|
||||
if (!$filesize)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isset($request_array))
|
||||
{
|
||||
$request_array = phpbb_find_range_request();
|
||||
}
|
||||
|
||||
return (empty($request_array)) ? false : phpbb_parse_range_request($request_array, $filesize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for HTTP range request in super globals.
|
||||
*
|
||||
* @return mixed false if no request found
|
||||
* array of strings containing the requested ranges otherwise
|
||||
* e.g. array(0 => '0-0', 1 => '123-125')
|
||||
*/
|
||||
function phpbb_find_range_request()
|
||||
{
|
||||
$globals = array(
|
||||
array('_SERVER', 'HTTP_RANGE'),
|
||||
array('_ENV', 'HTTP_RANGE'),
|
||||
);
|
||||
|
||||
foreach ($globals as $array)
|
||||
{
|
||||
$global = $array[0];
|
||||
$key = $array[1];
|
||||
|
||||
// Make sure range request starts with "bytes="
|
||||
if (isset($GLOBALS[$global][$key]) && strpos($GLOBALS[$global][$key], 'bytes=') === 0)
|
||||
{
|
||||
// Strip leading 'bytes='
|
||||
// Multiple ranges can be separated by a comma
|
||||
return explode(',', substr($GLOBALS[$global][$key], 6));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyses a range request array.
|
||||
*
|
||||
* A range request can contain multiple ranges,
|
||||
* we however only handle the first request and
|
||||
* only support requests from a given byte to the end of the file.
|
||||
*
|
||||
* @param array $request_array array of strings containing the requested ranges
|
||||
* @param int $filesize the full size of the file in bytes that has been requested
|
||||
*
|
||||
* @return mixed false if the whole file has to be delivered
|
||||
* associative array on success
|
||||
* byte_pos_start the first byte position, can be passed to fseek()
|
||||
* byte_pos_end the last byte position
|
||||
* bytes_requested the number of bytes requested
|
||||
* bytes_total the full size of the file
|
||||
*/
|
||||
function phpbb_parse_range_request($request_array, $filesize)
|
||||
{
|
||||
// Go through all ranges
|
||||
foreach ($request_array as $range_string)
|
||||
{
|
||||
$range = explode('-', trim($range_string));
|
||||
|
||||
// "-" is invalid, "0-0" however is valid and means the very first byte.
|
||||
if (sizeof($range) != 2 || $range[0] === '' && $range[1] === '')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// We currently do not support range requests that end before the end of the file
|
||||
if ($last_byte_pos != $filesize - 1)
|
||||
{
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ require_once 'dbal/all_tests.php';
|
||||
require_once 'regex/all_tests.php';
|
||||
require_once 'network/all_tests.php';
|
||||
require_once 'random/all_tests.php';
|
||||
require_once 'download/all_tests.php';
|
||||
|
||||
// exclude the test directory from code coverage reports
|
||||
if (version_compare(PHPUnit_Runner_Version::id(), '3.5.0') >= 0)
|
||||
@ -59,6 +60,7 @@ class phpbb_all_tests
|
||||
$suite->addTest(phpbb_regex_all_tests::suite());
|
||||
$suite->addTest(phpbb_network_all_tests::suite());
|
||||
$suite->addTest(phpbb_random_all_tests::suite());
|
||||
$suite->addTest(phpbb_download_all_tests::suite());
|
||||
|
||||
return $suite;
|
||||
}
|
||||
|
40
tests/download/all_tests.php
Normal file
40
tests/download/all_tests.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* @package testing
|
||||
* @copyright (c) 2010 phpBB Group
|
||||
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
|
||||
*
|
||||
*/
|
||||
|
||||
if (!defined('PHPUnit_MAIN_METHOD'))
|
||||
{
|
||||
define('PHPUnit_MAIN_METHOD', 'phpbb_download_all_tests::main');
|
||||
}
|
||||
|
||||
require_once 'test_framework/framework.php';
|
||||
require_once 'PHPUnit/TextUI/TestRunner.php';
|
||||
|
||||
require_once 'download/http_byte_range.php';
|
||||
|
||||
class phpbb_download_all_tests
|
||||
{
|
||||
public static function main()
|
||||
{
|
||||
PHPUnit_TextUI_TestRunner::run(self::suite());
|
||||
}
|
||||
|
||||
public static function suite()
|
||||
{
|
||||
$suite = new PHPUnit_Framework_TestSuite('phpBB Download Tests');
|
||||
|
||||
$suite->addTestSuite('phpbb_download_http_byte_range_test');
|
||||
|
||||
return $suite;
|
||||
}
|
||||
}
|
||||
|
||||
if (PHPUnit_MAIN_METHOD == 'phpbb_download_all_tests::main')
|
||||
{
|
||||
phpbb_regex_all_tests::main();
|
||||
}
|
62
tests/download/http_byte_range.php
Normal file
62
tests/download/http_byte_range.php
Normal file
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* @package testing
|
||||
* @copyright (c) 2010 phpBB Group
|
||||
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
|
||||
*
|
||||
*/
|
||||
|
||||
require_once 'test_framework/framework.php';
|
||||
require_once '../phpBB/includes/functions_download.php';
|
||||
|
||||
class phpbb_download_http_byte_range_test extends phpbb_test_case
|
||||
{
|
||||
public function test_find_range_request()
|
||||
{
|
||||
// Missing 'bytes=' prefix
|
||||
$_SERVER['HTTP_RANGE'] = 'bztes=';
|
||||
$this->assertEquals(false, phpbb_find_range_request());
|
||||
unset($_SERVER['HTTP_RANGE']);
|
||||
|
||||
$_ENV['HTTP_RANGE'] = 'bztes=';
|
||||
$this->assertEquals(false, phpbb_find_range_request());
|
||||
unset($_ENV['HTTP_RANGE']);
|
||||
|
||||
$_SERVER['HTTP_RANGE'] = 'bytes=0-0,123-125';
|
||||
$this->assertEquals(array('0-0', '123-125'), phpbb_find_range_request());
|
||||
unset($_SERVER['HTTP_RANGE']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider parse_range_request_data()
|
||||
*/
|
||||
public function test_parse_range_request($request_array, $filesize, $expected)
|
||||
{
|
||||
$this->assertEquals($expected, phpbb_parse_range_request($request_array, $filesize));
|
||||
}
|
||||
|
||||
public function parse_range_request_data()
|
||||
{
|
||||
return array(
|
||||
// Does not read until the end of file.
|
||||
array(
|
||||
array('3-4'),
|
||||
10,
|
||||
false,
|
||||
),
|
||||
|
||||
// Valid request, handle second range.
|
||||
array(
|
||||
array('0-0', '120-125'),
|
||||
125,
|
||||
array(
|
||||
'byte_pos_start' => 120,
|
||||
'byte_pos_end' => 124,
|
||||
'bytes_requested' => 5,
|
||||
'bytes_total' => 125,
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user