diff --git a/core/AssetManager.php b/core/AssetManager.php index 3013f1d41d0a638bae7e60a4c71c4288853d40ae..e65639b12f917dbbed52206608c229d6f879c1a0 100644 --- a/core/AssetManager.php +++ b/core/AssetManager.php @@ -12,13 +12,8 @@ namespace Piwik; use Exception; use JSMin; -use Piwik\Config; -use Piwik\Piwik; -use Piwik\Common; -use Piwik\Version; -use Piwik\PluginsManager; -use Piwik\Translate; use lessc; +use Piwik\Translate; /** * @see libs/jsmin/jsmin.php @@ -492,7 +487,7 @@ class AssetManager * Also tries to remove compressed version of the merged file. * * @param string $filename filename of the merged asset - * @see Piwik::serveStaticFile() + * @see ProxyStaticFile::serveStaticFile(serveFile * @throws Exception if the file couldn't be deleted */ private static function removeMergedAsset($filename) @@ -505,7 +500,7 @@ class AssetManager } // Tries to remove compressed version of the merged file. - // See Piwik::serveStaticFile() for more info on static file compression + // See Piwik::serveFile() for more info on static file compression $compressedFileLocation = PIWIK_USER_PATH . Piwik::COMPRESSED_FILE_LOCATION . $filename; @unlink($compressedFileLocation . ".deflate"); diff --git a/core/Piwik.php b/core/Piwik.php index a96e0122b8e1041fd16352f2639ad891eaa45652..6d178f45eb72c20643da125c1e23c85132ccf072 100644 --- a/core/Piwik.php +++ b/core/Piwik.php @@ -188,22 +188,6 @@ class Piwik return Url::getCurrentScheme() === 'https'; } - /** - * Set response header, e.g., HTTP/1.0 200 Ok - * - * @param string $status Status - * @return bool - */ - static public function setHttpStatus($status) - { - if (substr_compare(PHP_SAPI, '-fcgi', -5)) { - @header($_SERVER['SERVER_PROTOCOL'] . ' ' . $status); - } else { - // FastCGI - @header('Status: ' . $status); - } - } - /** * Workaround IE bug when downloading certain document types over SSL and * cache control headers are present, e.g., @@ -665,163 +649,6 @@ class Piwik return $messages; } - /** - * Test if php output is compressed - * - * @return bool True if php output is (or suspected/likely) to be compressed - */ - static public function isPhpOutputCompressed() - { - // Off = ''; On = '1'; otherwise, it's a buffer size - $zlibOutputCompression = ini_get('zlib.output_compression'); - - // could be ob_gzhandler, ob_deflatehandler, etc - $outputHandler = ini_get('output_handler'); - - // output handlers can be stacked - $obHandlers = array_filter(ob_list_handlers(), function($var) { - return $var !== "default output handler"; - }); - - // user defined handler via wrapper - $autoPrependFile = ini_get('auto_prepend_file'); - $autoAppendFile = ini_get('auto_append_file'); - - return !empty($zlibOutputCompression) || - !empty($outputHandler) || - !empty($obHandlers) || - !empty($autoPrependFile) || - !empty($autoAppendFile); - } - - /** - * Serve static files through php proxy. - * - * It performs the following actions: - * - Checks the file is readable or returns "HTTP/1.0 404 Not Found" - * - Returns "HTTP/1.1 304 Not Modified" after comparing the HTTP_IF_MODIFIED_SINCE - * with the modification date of the static file - * - Will try to compress the static file according to HTTP_ACCEPT_ENCODING. Compressed files are store in - * the /tmp directory. If compressing extensions are not available, a manually gzip compressed file - * can be provided in the /tmp directory. It has to bear the same name with an added .gz extension. - * Using manually compressed static files requires you to manually update the compressed file when - * the static file is updated. - * - Overrides server cache control config to allow caching - * - Sends Very Accept-Encoding to tell proxies to store different version of the static file according - * to users encoding capacities. - * - * Warning: - * Compressed filed are stored in the /tmp directory. - * If this method is used with two files bearing the same name but located in different locations, - * there is a risk of conflict. One file could be served with the content of the other. - * A future upgrade of this method would be to recreate the directory structure of the static file - * within a /tmp/compressed-static-files directory. - * - * @param string $file The location of the static file to serve - * @param string $contentType The content type of the static file. - * @param bool $expireFarFuture If set to true, will set Expires: header in far future. - * Should be set to false for files that don't have a cache buster (eg. piwik.js) - */ - static public function serveStaticFile($file, $contentType, $expireFarFuture = true) - { - if (file_exists($file)) { - // conditional GET - $modifiedSince = ''; - if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { - $modifiedSince = $_SERVER['HTTP_IF_MODIFIED_SINCE']; - - // strip any trailing data appended to header - if (false !== ($semicolon = strpos($modifiedSince, ';'))) { - $modifiedSince = substr($modifiedSince, 0, $semicolon); - } - } - - $fileModifiedTime = @filemtime($file); - $lastModified = gmdate('D, d M Y H:i:s', $fileModifiedTime) . ' GMT'; - - // set HTTP response headers - self::overrideCacheControlHeaders('public'); - @header('Vary: Accept-Encoding'); - @header('Content-Disposition: inline; filename=' . basename($file)); - - if ($expireFarFuture) { - // Required by proxy caches potentially in between the browser and server to cache the request indeed - @header("Expires: " . gmdate('D, d M Y H:i:s', time() + 86400 * 100) . ' GMT'); - } - - // Returns 304 if not modified since - if ($modifiedSince === $lastModified) { - self::setHttpStatus('304 Not Modified'); - } else { - // optional compression - $compressed = false; - $encoding = ''; - $compressedFileLocation = PIWIK_USER_PATH . self::COMPRESSED_FILE_LOCATION . basename($file); - - $phpOutputCompressionEnabled = self::isPhpOutputCompressed(); - if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && !$phpOutputCompressionEnabled) { - $acceptEncoding = $_SERVER['HTTP_ACCEPT_ENCODING']; - - if (extension_loaded('zlib') && function_exists('file_get_contents') && function_exists('file_put_contents')) { - if (preg_match('/(?:^|, ?)(deflate)(?:,|$)/', $acceptEncoding, $matches)) { - $encoding = 'deflate'; - $filegz = $compressedFileLocation . '.deflate'; - } else if (preg_match('/(?:^|, ?)((x-)?gzip)(?:,|$)/', $acceptEncoding, $matches)) { - $encoding = $matches[1]; - $filegz = $compressedFileLocation . '.gz'; - } - - if (!empty($encoding)) { - // compress-on-demand and use cache - if (!file_exists($filegz) || ($fileModifiedTime > @filemtime($filegz))) { - $data = file_get_contents($file); - - if ($encoding == 'deflate') { - $data = gzdeflate($data, 9); - } else if ($encoding == 'gzip' || $encoding == 'x-gzip') { - $data = gzencode($data, 9); - } - - file_put_contents($filegz, $data); - } - - $compressed = true; - $file = $filegz; - } - } else { - // manually compressed - $filegz = $compressedFileLocation . '.gz'; - if (preg_match('/(?:^|, ?)((x-)?gzip)(?:,|$)/', $acceptEncoding, $matches) && file_exists($filegz) && ($fileModifiedTime < @filemtime($filegz))) { - $encoding = $matches[1]; - $compressed = true; - $file = $filegz; - } - } - } - - @header('Last-Modified: ' . $lastModified); - - if (!$phpOutputCompressionEnabled) { - @header('Content-Length: ' . filesize($file)); - } - - if (!empty($contentType)) { - @header('Content-Type: ' . $contentType); - } - - if ($compressed) { - @header('Content-Encoding: ' . $encoding); - } - - if (!_readfile($file)) { - self::setHttpStatus('505 Internal server error'); - } - } - } else { - self::setHttpStatus('404 Not Found'); - } - } - /** * Create CSV (or other delimited) files * @@ -1734,7 +1561,7 @@ class Piwik static public function disconnectDatabase() { \Zend_Registry::get('db')->closeConnection(); -} + } /** * Checks the database server version against the required minimum diff --git a/js/index.php b/js/index.php index 6c67ffdc81c3b0a462d9401c7b197925651d4a12..4ee43b7e644c5812b00db3fed41c604ee9a61e45 100644 --- a/js/index.php +++ b/js/index.php @@ -5,7 +5,7 @@ * @link http://piwik.org * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later */ -use Piwik\Piwik; +use Piwik\ProxyStaticFile; /** * Tracker proxy @@ -31,6 +31,6 @@ $file = '../piwik.js'; // There is no cache buster parameter so we don't set Expires: header $expireFarFuture = false; -Piwik::serveStaticFile($file, "application/javascript; charset=UTF-8", $expireFarFuture); +ProxyStaticFile::serveFile($file, "application/javascript; charset=UTF-8", $expireFarFuture); exit; diff --git a/plugins/Proxy/Controller.php b/plugins/Proxy/Controller.php index af251fe32ae8cae8ab1c8e3814503629cea65118..9378a6f3e1be2f35283bd4607805ef6cca36df04 100644 --- a/plugins/Proxy/Controller.php +++ b/plugins/Proxy/Controller.php @@ -10,9 +10,10 @@ */ namespace Piwik\Plugins\Proxy; -use Piwik\Piwik; -use Piwik\Common; use Piwik\AssetManager; +use Piwik\Common; +use Piwik\Piwik; +use Piwik\ProxyStaticFile; use Piwik\Url; /** @@ -34,7 +35,7 @@ class Controller extends \Piwik\Controller public function getCss() { $cssMergedFile = AssetManager::getMergedCssFileLocation(); - Piwik::serveStaticFile($cssMergedFile, "text/css"); + ProxyStaticFile::serveFile($cssMergedFile, "text/css"); } /** @@ -46,7 +47,7 @@ class Controller extends \Piwik\Controller public function getJs() { $jsMergedFile = AssetManager::getMergedJsFileLocation(); - Piwik::serveStaticFile($jsMergedFile, self::JS_MIME_TYPE); + ProxyStaticFile::serveFile($jsMergedFile, self::JS_MIME_TYPE); } /** @@ -64,7 +65,7 @@ class Controller extends \Piwik\Controller AssetManager::removeTranslationsJsFile(); $translationsJsFile = AssetManager::getTranslationsJsFileLocation(); - Piwik::serveStaticFile($translationsJsFile, self::JS_MIME_TYPE); + ProxyStaticFile::serveFile($translationsJsFile, self::JS_MIME_TYPE); } /** diff --git a/tests/PHPUnit/Core/ServeStaticFileTest.php b/tests/PHPUnit/Core/ServeStaticFileTest.php index 116edab40a0a0ee5b12de3c0418f791d61342151..a5cbaa1d393e7f2fdd97fd13056fcc27e8e93fdb 100644 --- a/tests/PHPUnit/Core/ServeStaticFileTest.php +++ b/tests/PHPUnit/Core/ServeStaticFileTest.php @@ -1,19 +1,20 @@ <?php /** - * This php file is used to unit test Piwik::serveStaticFile() + * This php file is used to unit test Piwik::serveFile() * Unit tests for this method should normally be located in /tests/core/Piwik.test.php - * To make a comprehensive test suit for Piwik::serveStaticFile() (ie. being able to test combinations of request + * To make a comprehensive test suit for Piwik::serveFile() (ie. being able to test combinations of request * headers, being able to test response headers and so on) we need to simulate static file requests in a controlled * environment - * The php code which simulates requests using Piwik::serveStaticFile() is provided in the same file (ie. this one) - * as the unit testing code for Piwik::serveStaticFile() + * The php code which simulates requests using Piwik::serveFile() is provided in the same file (ie. this one) + * as the unit testing code for Piwik::serveFile() * This decision has a structural impact on the usual unit test file structure - * serveStaticFile.test.php has been created to avoid making too many modifications to /tests/core/Piwik.test.php + * serveFile.test.php has been created to avoid making too many modifications to /tests/core/Piwik.test.php */ // This is Piwik logo, the static file used in this test suit -use Piwik\Piwik; use Piwik\Common; +use Piwik\Piwik; +use Piwik\ProxyStaticFile; define("TEST_FILE_LOCATION", realpath(dirname(__FILE__) . "/../../resources/lipsum.txt")); define("TEST_FILE_CONTENT_TYPE", "text/plain"); @@ -25,7 +26,7 @@ define("ZLIB_OUTPUT_REQUEST_VAR", "zlibOutput"); /** * These constants define the mode in which this php file is used : - * - for unit testing Piwik::serveStaticFile() or + * - for unit testing Piwik::serveFile() or * - as a static file server */ define("STATIC_SERVER_MODE", "staticServerMode"); @@ -52,11 +53,11 @@ class Test_Piwik_ServeStaticFile extends PHPUnit_Framework_TestCase */ public function test_phpOutputCompression() { - $this->assertFalse(Piwik::isPhpOutputCompressed()); + $this->assertFalse(ProxyStaticFile::isPhpOutputCompressed()); } /** - * Checks that "HTTP/1.0 404 Not Found" is returned when Piwik::serveStaticFile is called with a null file + * Checks that "HTTP/1.0 404 Not Found" is returned when Piwik::serveFile is called with a null file * * @group ServeStaticFile */ @@ -73,7 +74,7 @@ class Test_Piwik_ServeStaticFile extends PHPUnit_Framework_TestCase } /** - * Checks that "HTTP/1.0 404 Not Found" is returned when Piwik::serveStaticFile is called with a non existing file + * Checks that "HTTP/1.0 404 Not Found" is returned when Piwik::serveFile is called with a non existing file * * @group ServeStaticFile */ @@ -90,7 +91,7 @@ class Test_Piwik_ServeStaticFile extends PHPUnit_Framework_TestCase } /** - * Checks that "HTTP/1.0 505 Internal server error" is returned when Piwik::serveStaticFile is called with a + * Checks that "HTTP/1.0 505 Internal server error" is returned when Piwik::serveFile is called with a * non-readable file * * @group ServeStaticFile diff --git a/tests/resources/staticFileServer.php b/tests/resources/staticFileServer.php index 3860cf19102dc2038136061d95c7c1d9cee641e3..0646b6b3cc08eb459ce629c0df253e1089668f8c 100644 --- a/tests/resources/staticFileServer.php +++ b/tests/resources/staticFileServer.php @@ -1,16 +1,16 @@ <?php /** - * This php file is used to unit test Piwik::serveStaticFile() - * To make a comprehensive test suit for Piwik::serveStaticFile() (ie. being able to test combinations of request + * This php file is used to unit test Piwik::serveFile() + * To make a comprehensive test suit for Piwik::serveFile() (ie. being able to test combinations of request * headers, being able to test response headers and so on) we need to simulate static file requests in a controlled * environment - * The php code which simulates requests using Piwik::serveStaticFile() is provided in the same file (ie. this one) - * as the unit testing code for Piwik::serveStaticFile() + * The php code which simulates requests using Piwik::serveFile() is provided in the same file (ie. this one) + * as the unit testing code for Piwik::serveFile() * This decision has a structural impact on the usual unit test file structure - * serveStaticFile.test.php has been created to avoid making too many modifications to /tests/core/Piwik.test.php + * serveFile.test.php has been created to avoid making too many modifications to /tests/core/Piwik.test.php */ -use Piwik\Piwik; use Piwik\Common; +use Piwik\ProxyStaticFile; define('PIWIK_DOCUMENT_ROOT', dirname(__FILE__).'/../../'); if(file_exists(PIWIK_DOCUMENT_ROOT . '/bootstrap.php')) @@ -58,7 +58,7 @@ define("TEST_FILE_SRV_MODE", "testFile"); /** * If the static file server has been requested, the response sent back to the browser will be the content produced by - * the execution of Piwik:serveStaticFile(). In this case, unit tests won't be executed + * the execution of Piwik:serveFile(). In this case, unit tests won't be executed */ // Getting the server mode $staticFileServerMode = Common::getRequestVar(SRV_MODE_REQUEST_VAR, ""); @@ -72,21 +72,21 @@ if ($staticFileServerMode === "") { } switch ($staticFileServerMode) { - // The static file server calls Piwik::serveStaticFile with a null file + // The static file server calls Piwik::serveFile with a null file case NULL_FILE_SRV_MODE: - Piwik::serveStaticFile(null, TEST_FILE_CONTENT_TYPE); + ProxyStaticFile::serveFile(null, TEST_FILE_CONTENT_TYPE); break; - // The static file server calls Piwik::serveStaticFile with a non-existing file + // The static file server calls Piwik::serveFile with a non-existing file case GHOST_FILE_SRV_MODE: - Piwik::serveStaticFile(TEST_FILE_LOCATION . ".ghost", TEST_FILE_CONTENT_TYPE); + ProxyStaticFile::serveFile(TEST_FILE_LOCATION . ".ghost", TEST_FILE_CONTENT_TYPE); break; - // The static file server calls Piwik::serveStaticFile with the test file + // The static file server calls Piwik::serveFile with the test file case TEST_FILE_SRV_MODE: - Piwik::serveStaticFile(TEST_FILE_LOCATION, TEST_FILE_CONTENT_TYPE); + ProxyStaticFile::serveFile(TEST_FILE_LOCATION, TEST_FILE_CONTENT_TYPE); break; } \ No newline at end of file