1
0
mirror of https://github.com/phpbb/phpbb.git synced 2025-08-08 17:56:52 +02: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:
Igor Wiedler
2010-10-25 22:12:24 +02:00
5 changed files with 274 additions and 2 deletions

View File

@@ -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,
);
}
}