Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/ProxyHttp.php32
-rw-r--r--libs/upgradephp/upgrade.php15
-rw-r--r--tests/PHPUnit/Core/ServeStaticFileTest.php102
-rw-r--r--tests/resources/staticFileServer.php15
4 files changed, 154 insertions, 10 deletions
diff --git a/core/ProxyHttp.php b/core/ProxyHttp.php
index 5952089eda..c500b40fdf 100644
--- a/core/ProxyHttp.php
+++ b/core/ProxyHttp.php
@@ -56,8 +56,13 @@ class ProxyHttp
* @param string $contentType The content type of the static file.
* @param bool $expireFarFuture Day in the far future to set the Expires header to.
* Should be set to false for files that should not be cached.
+ * @param int|false $byteStart The starting byte in the file to serve. If false, the data from the beginning
+ * of the file will be served.
+ * @param int|false $byteEnd The ending byte in the file to serve. If false, the data from $byteStart to the
+ * end of the file will be served.
*/
- public static function serverStaticFile($file, $contentType, $expireFarFutureDays = 100)
+ public static function serverStaticFile($file, $contentType, $expireFarFutureDays = 100, $byteStart = false,
+ $byteEnd = false)
{
// if the file cannot be found return HTTP status code '404'
if (!file_exists($file)) {
@@ -65,7 +70,6 @@ class ProxyHttp
return;
}
- // conditional GET
$modifiedSince = Http::getModifiedSinceHeader();
$fileModifiedTime = @filemtime($file);
@@ -88,6 +92,14 @@ class ProxyHttp
}
// if we have to serve the file, serve it now, either in the clear or compressed
+ if ($byteStart === false) {
+ $byteStart = 0;
+ }
+
+ if ($byteEnd === false) {
+ $byteEnd = filesize($file);
+ }
+
$compressed = false;
$encoding = '';
$compressedFileLocation = AssetManager::getInstance()->getAssetDirectory() . '/' . basename($file);
@@ -102,11 +114,14 @@ class ProxyHttp
// compress the file if it doesn't exist or is newer than the existing cached file, and cache
// the compressed result
if (self::shouldCompressFile($file, $filegz)) {
- self::compressFile($file, $filegz, $encoding);
+ self::compressFile($file, $filegz, $encoding, $byteStart, $byteEnd);
}
$compressed = true;
$file = $filegz;
+
+ $byteStart = 0;
+ $byteEnd = filesize($file);
}
} else {
// if a compressed file exists, the file was manually compressed so we just serve that
@@ -115,6 +130,9 @@ class ProxyHttp
) {
$compressed = true;
$file = $filegz;
+
+ $byteStart = 0;
+ $byteEnd = filesize($file);
}
}
}
@@ -122,7 +140,7 @@ class ProxyHttp
@header('Last-Modified: ' . $lastModified);
if (!$phpOutputCompressionEnabled) {
- @header('Content-Length: ' . filesize($file));
+ @header('Content-Length: ' . ($byteEnd - $byteStart));
}
if (!empty($contentType)) {
@@ -133,7 +151,7 @@ class ProxyHttp
@header('Content-Encoding: ' . $encoding);
}
- if (!_readfile($file)) {
+ if (!_readfile($file, $byteStart, $byteEnd)) {
self::setHttpStatus('505 Internal server error');
}
}
@@ -248,9 +266,11 @@ class ProxyHttp
return !file_exists($compressedFilePath) || ($toCompressLastModified > $compressedLastModified);
}
- private static function compressFile($fileToCompress, $compressedFilePath, $compressionEncoding)
+ private static function compressFile($fileToCompress, $compressedFilePath, $compressionEncoding, $byteStart,
+ $byteEnd)
{
$data = file_get_contents($fileToCompress);
+ $data = substr($data, $byteStart, $byteEnd - $byteStart);
if ($compressionEncoding == 'deflate') {
$data = gzdeflate($data, 9);
diff --git a/libs/upgradephp/upgrade.php b/libs/upgradephp/upgrade.php
index 5ab68078a8..7d23172470 100644
--- a/libs/upgradephp/upgrade.php
+++ b/libs/upgradephp/upgrade.php
@@ -612,20 +612,27 @@ function safe_unserialize( $str )
* @param resource $context
* @return int the number of bytes read from the file, or false if an error occurs
*/
-function _readfile($filename, $useIncludePath = false, $context = null)
+function _readfile($filename, $byteStart, $byteEnd, $useIncludePath = false, $context = null)
{
$count = @filesize($filename);
// built-in function has a 2 MB limit when using mmap
- if (function_exists('readfile') && $count <= (2 * 1024 * 1024)) {
+ if (function_exists('readfile')
+ && $count <= (2 * 1024 * 1024)
+ && $byteStart == 0
+ && $byteEnd == $count
+ ) {
return @readfile($filename, $useIncludePath, $context);
}
// when in doubt (or when readfile() function is disabled)
$handle = @fopen($filename, SettingsServer::isWindows() ? "rb" : "r");
if ($handle) {
- while(!feof($handle)) {
- echo fread($handle, 8192);
+ fseek($handle, $byteStart);
+
+ for ($pos = $byteStart; $pos < $byteEnd && !feof($handle); $pos = ftell($handle)) {
+ echo fread($handle, min(8192, $byteEnd - $pos));
+
ob_flush();
flush();
}
diff --git a/tests/PHPUnit/Core/ServeStaticFileTest.php b/tests/PHPUnit/Core/ServeStaticFileTest.php
index b3536b7aa3..42aab636d4 100644
--- a/tests/PHPUnit/Core/ServeStaticFileTest.php
+++ b/tests/PHPUnit/Core/ServeStaticFileTest.php
@@ -36,8 +36,16 @@ define("UNIT_TEST_MODE", "unitTestMode");
define("NULL_FILE_SRV_MODE", "nullFile");
define("GHOST_FILE_SRV_MODE", "ghostFile");
define("TEST_FILE_SRV_MODE", "testFile");
+define("PARTIAL_TEST_FILE_SRV_MODE", "partialTestFile");
+define("WHOLE_TEST_FILE_WITH_RANGE_SRV_MODE", "wholeTestFileWithRange");
+
+define("PARTIAL_BYTE_START", 1204);
+define("PARTIAL_BYTE_END", 14724);
// If the static file server has not been requested, the standard unit test case class is defined
+/**
+ * @group ServeStaticFileTest
+ */
class Test_Piwik_ServeStaticFile extends PHPUnit_Framework_TestCase
{
public function tearDown()
@@ -389,6 +397,90 @@ class Test_Piwik_ServeStaticFile extends PHPUnit_Framework_TestCase
}
/**
+ * @group Core
+ */
+ public function test_partialFileServeNoCompression()
+ {
+ $this->removeCompressedFiles();
+
+ $curlHandle = curl_init();
+ curl_setopt($curlHandle, CURLOPT_URL, $this->getPartialTestFileSrvModeUrl());
+ curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, true);
+ $partialResponse = curl_exec($curlHandle);
+ $responseInfo = curl_getinfo($curlHandle);
+ curl_close($curlHandle);
+
+ clearstatcache();
+
+ // check no compressed files created
+ $this->assertFalse(file_exists($this->getCompressedFileLocation() . ".deflate"));
+ $this->assertFalse(file_exists($this->getCompressedFileLocation() . ".gz"));
+
+ // check $partialResponse
+ $this->assertEquals(PARTIAL_BYTE_END - PARTIAL_BYTE_START, $responseInfo["size_download"]);
+
+ $expectedPartialContents = substr(file_get_contents(TEST_FILE_LOCATION), PARTIAL_BYTE_START,
+ PARTIAL_BYTE_END - PARTIAL_BYTE_START);
+ $this->assertEquals($expectedPartialContents, $partialResponse);
+ }
+
+ /**
+ * @group Core
+ */
+ public function test_partialFileServeWithCompression()
+ {
+ $this->removeCompressedFiles();
+
+ $curlHandle = curl_init();
+ curl_setopt($curlHandle, CURLOPT_URL, $this->getPartialTestFileSrvModeUrl());
+ curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($curlHandle, CURLOPT_ENCODING, "deflate");
+ $partialResponse = curl_exec($curlHandle);
+ $responseInfo = curl_getinfo($curlHandle);
+ curl_close($curlHandle);
+
+ clearstatcache();
+
+ // check the correct compressed file is created
+ $this->assertTrue(file_exists($this->getCompressedFileLocation() . ".deflate"));
+ $this->assertFalse(file_exists($this->getCompressedFileLocation() . ".gz"));
+
+ // check $partialResponse
+ $expectedPartialContents = substr(file_get_contents(TEST_FILE_LOCATION), PARTIAL_BYTE_START,
+ PARTIAL_BYTE_END - PARTIAL_BYTE_START);
+ $this->assertEquals($expectedPartialContents, $partialResponse);
+
+ $this->removeCompressedFiles();
+ }
+
+ /**
+ * @group Core
+ */
+ public function test_wholeFileServeWithByteRange()
+ {
+ $this->removeCompressedFiles();
+
+ $curlHandle = curl_init();
+ curl_setopt($curlHandle, CURLOPT_URL, $this->getWholeTestFileWithRangeSrvModeUrl());
+ curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($curlHandle, CURLOPT_ENCODING, "deflate");
+ $fullResponse = curl_exec($curlHandle);
+ $responseInfo = curl_getinfo($curlHandle);
+ curl_close($curlHandle);
+
+ clearstatcache();
+
+ // check the correct compressed file is created
+ $this->assertTrue(file_exists($this->getCompressedFileLocation() . ".deflate"));
+ $this->assertFalse(file_exists($this->getCompressedFileLocation() . ".gz"));
+
+ // check $fullResponse
+ $this->assertEquals(file_get_contents(TEST_FILE_LOCATION), $fullResponse);
+
+ $this->removeCompressedFiles();
+ }
+
+ /**
* Helper methods
*/
private function getStaticSrvUrl()
@@ -415,6 +507,16 @@ class Test_Piwik_ServeStaticFile extends PHPUnit_Framework_TestCase
return $this->getStaticSrvUrl() . TEST_FILE_SRV_MODE;
}
+ private function getPartialTestFileSrvModeUrl()
+ {
+ return $this->getStaticSrvUrl() . PARTIAL_TEST_FILE_SRV_MODE;
+ }
+
+ private function getWholeTestFileWithRangeSrvModeUrl()
+ {
+ return $this->getStaticSrvUrl() . WHOLE_TEST_FILE_WITH_RANGE_SRV_MODE;
+ }
+
private function setZlibOutputRequest($url)
{
return $url . "&" . ZLIB_OUTPUT_REQUEST_VAR . "=1";
diff --git a/tests/resources/staticFileServer.php b/tests/resources/staticFileServer.php
index cdaa54830f..69ed020696 100644
--- a/tests/resources/staticFileServer.php
+++ b/tests/resources/staticFileServer.php
@@ -54,6 +54,11 @@ define("ZLIB_OUTPUT_REQUEST_VAR", "zlibOutput");
define("NULL_FILE_SRV_MODE", "nullFile");
define("GHOST_FILE_SRV_MODE", "ghostFile");
define("TEST_FILE_SRV_MODE", "testFile");
+define("PARTIAL_TEST_FILE_SRV_MODE", "partialTestFile");
+define("WHOLE_TEST_FILE_WITH_RANGE_SRV_MODE", "wholeTestFileWithRange");
+
+define("PARTIAL_BYTE_START", 1204);
+define("PARTIAL_BYTE_END", 14724);
/**
@@ -89,4 +94,14 @@ switch ($staticFileServerMode) {
ProxyHttp::serverStaticFile(TEST_FILE_LOCATION, TEST_FILE_CONTENT_TYPE);
break;
+
+ case PARTIAL_TEST_FILE_SRV_MODE:
+
+ ProxyHttp::serverStaticFile(TEST_FILE_LOCATION, TEST_FILE_CONTENT_TYPE, $expireFarFutureDays = 100, PARTIAL_BYTE_START, PARTIAL_BYTE_END);
+ break;
+
+ case WHOLE_TEST_FILE_WITH_RANGE_SRV_MODE:
+
+ ProxyHttp::serverStaticFile(TEST_FILE_LOCATION, TEST_FILE_CONTENT_TYPE, $expireFarFutureDays = 100, 0, filesize(TEST_FILE_LOCATION));
+ break;
} \ No newline at end of file