diff --git a/core/Tracker.php b/core/Tracker.php index ed911e55f68dcf580d57c641f9cdbfd514b68463..8252c494c3be31cc63955ef4bef07fd3ec8caaf9 100644 --- a/core/Tracker.php +++ b/core/Tracker.php @@ -454,7 +454,7 @@ class Tracker Common::sendHeader('Content-Type: text/html; charset=utf-8'); echo $this->getMessageFromException($e); } else { - $this->outputTransparentGif(); + $this->sendResponse(); } die(1); exit; @@ -501,7 +501,7 @@ class Tracker } switch ($this->getState()) { case self::STATE_LOGGING_DISABLE: - $this->outputTransparentGif (); + $this->sendResponse(); Common::printDebug("Logging disabled, display transparent logo"); break; @@ -513,7 +513,7 @@ class Tracker case self::STATE_NOSCRIPT_REQUEST: case self::STATE_NOTHING_TO_NOTICE: default: - $this->outputTransparentGif (); + $this->sendResponse(); Common::printDebug("Nothing to notice => default behaviour"); break; } @@ -648,7 +648,7 @@ class Tracker return $visit; } - protected function outputTransparentGif () + private function sendResponse() { if (isset($GLOBALS['PIWIK_TRACKER_DEBUG']) && $GLOBALS['PIWIK_TRACKER_DEBUG'] @@ -660,11 +660,24 @@ class Tracker // If there was an error during tracker, return so errors can be flushed return; } - $transGifBase64 = "R0lGODlhAQABAIAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="; - Common::sendHeader('Content-Type: image/gif'); $this->outputAccessControlHeaders(); + $request = $_GET + $_POST; + + if (array_key_exists('send_image', $request) && $request['send_image'] === '0') { + Common::sendHeader("HTTP/1.0 204 No Response"); + return; + } + + $this->outputTransparentGif(); + } + + protected function outputTransparentGif () + { + $transGifBase64 = "R0lGODlhAQABAIAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="; + Common::sendHeader('Content-Type: image/gif'); + print(base64_decode($transGifBase64)); } diff --git a/js/piwik.js b/js/piwik.js index e374649c645000d0d7020db9f195e28de1dab145..98f22d7f8a0e58463f0790ca590feeee60a98e26 100644 --- a/js/piwik.js +++ b/js/piwik.js @@ -2925,7 +2925,8 @@ if (typeof Piwik !== 'object') { '&_viewts=' + lastVisitTs + (String(lastEcommerceOrderTs).length ? '&_ects=' + lastEcommerceOrderTs : '') + (String(referralUrl).length ? '&_ref=' + encodeWrapper(purify(referralUrl.slice(0, referralUrlMaxLength))) : '') + - (charSet ? '&cs=' + encodeWrapper(charSet) : ''); + (charSet ? '&cs=' + encodeWrapper(charSet) : '') + + '&send_image=0'; // browser features for (i in browserFeatures) { diff --git a/libs/PiwikTracker/PiwikTracker.php b/libs/PiwikTracker/PiwikTracker.php index 39eb82b6bd855bd53f987328ad6332ef2ed9408a..c998e1913dee40dd1031c880f561a9788c833a68 100644 --- a/libs/PiwikTracker/PiwikTracker.php +++ b/libs/PiwikTracker/PiwikTracker.php @@ -219,6 +219,8 @@ class PiwikTracker $this->doBulkRequests = false; $this->storedTrackingActions = array(); + $this->sendImageResponse = true; + $this->visitorCustomVar = $this->getCustomVariablesFromCookie(); } @@ -500,6 +502,14 @@ class PiwikTracker $this->configCookiePath = $path; } + /** + * If image response is disabled Piwik will respond with a HTTP 204 header instead of responding with a gif. + */ + public function disableSendImageResponse() + { + $this->sendImageResponse = false; + } + /** * Fix-up domain */ @@ -1524,6 +1534,7 @@ class PiwikTracker (!empty($this->city) ? '&city=' . urlencode($this->city) : '') . (!empty($this->lat) ? '&lat=' . urlencode($this->lat) : '') . (!empty($this->long) ? '&long=' . urlencode($this->long) : '') . + (!$this->sendImageResponse ? '&send_image=0' : '') . // DEBUG $this->DEBUG_APPEND_URL; diff --git a/tests/PHPUnit/Framework/Constraint/ResponseCode.php b/tests/PHPUnit/Framework/Constraint/ResponseCode.php new file mode 100644 index 0000000000000000000000000000000000000000..b934ea008f63521ecde0b77de3163a25cd99003d --- /dev/null +++ b/tests/PHPUnit/Framework/Constraint/ResponseCode.php @@ -0,0 +1,56 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ +namespace Piwik\Tests\Framework\Constraint; + +class ResponseCode extends \PHPUnit_Framework_Constraint +{ + + /** + * @param integer $value Expected response code + */ + public function __construct($value) + { + parent::__construct(); + $this->value = $value; + } + + /** + * Evaluates the constraint for parameter $other. Returns TRUE if the + * constraint is met, FALSE otherwise. + * + * @param mixed $other Value or object to evaluate. + * @return bool + */ + public function matches($other) + { + $options = array( + CURLOPT_URL => $other, + CURLOPT_HEADER => true, + CURLOPT_TIMEOUT => 1, + CURLOPT_RETURNTRANSFER => true + ); + + $ch = curl_init(); + curl_setopt_array($ch, $options); + @curl_exec($ch); + $responseCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + + return $this->value === (int) $responseCode; + } + + /** + * Returns a string representation of the constraint. + * + * @return string + */ + public function toString() + { + return 'does not return response code ' . $this->exporter->export($this->value); + } +}?> \ No newline at end of file diff --git a/tests/PHPUnit/Framework/TestCase/SystemTestCase.php b/tests/PHPUnit/Framework/TestCase/SystemTestCase.php index e153dc5f2fa037d62886085c3428de5b1439046b..96b60cab0a1c7c7b866fd46b1ed43bbd012aa5b7 100755 --- a/tests/PHPUnit/Framework/TestCase/SystemTestCase.php +++ b/tests/PHPUnit/Framework/TestCase/SystemTestCase.php @@ -16,6 +16,7 @@ use Piwik\DataAccess\ArchiveTableCreator; use Piwik\Db; use Piwik\DbHelper; use Piwik\ReportRenderer; +use Piwik\Tests\Framework\Constraint\ResponseCode; use Piwik\Tests\Framework\TestRequest\ApiTestConfig; use Piwik\Tests\Framework\TestRequest\Collection; use Piwik\Tests\Framework\TestRequest\Response; @@ -578,4 +579,9 @@ abstract class SystemTestCase extends PHPUnit_Framework_TestCase $this->markTestSkipped('Sometimes fail on php 5.3'); } } + + public function assertResponseCode($expectedResponseCode, $url, $message = '') + { + self::assertThat($url, new ResponseCode($expectedResponseCode), $message); + } } diff --git a/tests/PHPUnit/System/TrackerTest.php b/tests/PHPUnit/System/TrackerTest.php new file mode 100755 index 0000000000000000000000000000000000000000..c3b7125eed0abb75ed71e61692bf346859d296a6 --- /dev/null +++ b/tests/PHPUnit/System/TrackerTest.php @@ -0,0 +1,72 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ +namespace Piwik\Tests\System; + +use Piwik\Tests\Framework\Fixture; +use Piwik\Tests\Framework\TestCase\SystemTestCase; + +/** + * @group TrackerTest + * @group Plugins + */ +class TrackerTest extends SystemTestCase +{ + public static $fixture = null; + + /** + * @var \PiwikTracker + */ + private $tracker; + + public function setUp() + { + parent::setUp(); + + $idSite = 1; + $dateTime = '2014-01-01 00:00:01'; + + if (!Fixture::siteCreated($idSite)) { + Fixture::createWebsite($dateTime); + } + + $this->tracker = Fixture::getTracker($idSite, $dateTime, $defaultInit = true); + } + + public function test_response_ShouldContainAnImage() + { + $response = $this->tracker->doTrackPageView('Test'); + + Fixture::checkResponse($response); + $this->assertNotEmpty($response); + } + + public function test_response_ShouldBeEmpty_IfImageIsDisabled() + { + $this->tracker->disableSendImageResponse(); + + $response = $this->tracker->doTrackPageView('Test'); + + $this->assertSame('', $response); + } + + public function test_response_ShouldSend200ResponseCode_IfImageIsEnabled() + { + $url = $this->tracker->getUrlTrackPageView('Test'); + + $this->assertResponseCode(200, $url); + } + + public function test_response_ShouldSend204ResponseCode_IfImageIsDisabled() + { + $url = $this->tracker->getUrlTrackPageView('Test'); + $url .= '&send_image=0'; + + $this->assertResponseCode(204, $url); + } + +} diff --git a/tests/javascript/index.php b/tests/javascript/index.php index 802cf492d95ff7a7b8525223b56a6fd16455ba99..b4e737792d5621f6a841298bfb38f4639885cc64 100644 --- a/tests/javascript/index.php +++ b/tests/javascript/index.php @@ -2368,12 +2368,14 @@ function PiwikTest() { }); test("getRequest()", function() { - expect(1); + expect(2); var tracker = Piwik.getTracker(); tracker.setCustomData("key is X", "value is Y"); equal( tracker.getRequest('hello=world').indexOf('hello=world&idsite=&rec=1&r='), 0); + + ok( -1 !== tracker.getRequest('hello=world').indexOf('send_image=0'), 'should disable sending image response'); }); // support for setCustomRequestProcessing( customRequestContentProcessingLogic )