diff --git a/phpBB/includes/functions_download.php b/phpBB/includes/functions_download.php index 8f6526dd71..d61ce3d098 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; - } - - 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, + ); } /** 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,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, ), ); }