From dc5012f118dced0b5acd2e9aa48db2f4df3ef838 Mon Sep 17 00:00:00 2001 From: Mateusz Uzdowski Date: Mon, 22 Jul 2013 16:27:52 +1200 Subject: [PATCH] Cache the versioned feed to prevent DDOS. --- _config.php | 3 +++ code/VersionFeed.php | 5 +++- code/VersionFeed_Controller.php | 48 ++++++++++++++++++++++++++------- 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/_config.php b/_config.php index f18ff31..36ccb11 100644 --- a/_config.php +++ b/_config.php @@ -2,3 +2,6 @@ SiteTree::add_extension('VersionFeed'); ContentController::add_extension('VersionFeed_Controller'); + +// Set the cache lifetime to 5 mins. +SS_Cache::set_cache_lifetime('VersionFeed_Controller', 5*60); diff --git a/code/VersionFeed.php b/code/VersionFeed.php index 693fcd5..1dafb95 100644 --- a/code/VersionFeed.php +++ b/code/VersionFeed.php @@ -51,6 +51,9 @@ class VersionFeed extends SiteTreeExtension { $version->DiffContent->setValue('
'.$diff->obj('Content')->forTemplate().'
'); $changed = true; } + + // Copy the link so it can be cached by SS_Cache. + $version->GeneratedLink = $version->AbsoluteLink(); } // Omit the versions that haven't been visibly changed (only takes the above fields into consideration). @@ -93,4 +96,4 @@ class VersionFeed extends SiteTreeExtension { public function getDefaultRSSLink() { if ($this->owner->PublicHistory) return $this->owner->Link('changes'); } -} \ No newline at end of file +} diff --git a/code/VersionFeed_Controller.php b/code/VersionFeed_Controller.php index 2fb1c07..08d4a71 100644 --- a/code/VersionFeed_Controller.php +++ b/code/VersionFeed_Controller.php @@ -29,9 +29,19 @@ class VersionFeed_Controller extends Extension { function changes() { if(!$this->owner->PublicHistory) throw new SS_HTTPResponse_Exception('Page history not viewable', 404);; + // Cache the diffs, otherwise it will take 5secs to generate 100 diffs which could lead to DOS. + $cache = SS_Cache::factory('VersionFeed_Controller'); + $cache->setOption('automatic_serialization', true); + $key = 'changes' . $this->owner->Version; + $entries = $cache->load($key); + if(!$entries) { + $entries = $this->owner->getDiffedChanges(); + $cache->save($entries, $key); + } + // Generate the output. $title = sprintf(_t('RSSHistory.SINGLEPAGEFEEDTITLE', 'Updates to %s page'), $this->owner->Title); - $rss = new RSSFeed($this->owner->getDiffedChanges(), $this->owner->request->getURL(), $title, '', 'Title', '', null); + $rss = new RSSFeed($entries, $this->owner->request->getURL(), $title, '', 'Title', '', null); $rss->setTemplate('Page_changes_rss'); return $rss->outputToBrowser(); } @@ -40,15 +50,33 @@ class VersionFeed_Controller extends Extension { * Get all changes from the site in a RSS feed. */ function allchanges() { - // Fetch the latest changes on the entire site. - $latestChanges = DB::query('SELECT * FROM "SiteTree_versions" WHERE "WasPublished"=\'1\' AND "CanViewType" IN (\'Anyone\', \'Inherit\') AND "ShowInSearch"=1 AND ("PublicHistory" IS NULL OR "PublicHistory" = \'1\') ORDER BY "LastEdited" DESC LIMIT 20'); - $changeList = new ArrayList(); - foreach ($latestChanges as $record) { - // Get the diff to the previous version. - $version = new Versioned_Version($record); - $changes = $version->getDiffedChanges($version->Version, false); - if ($changes && $changes->Count()) $changeList->push($changes->First()); + $latestChanges = DB::query('SELECT * FROM "SiteTree_versions" WHERE "WasPublished"=\'1\' AND "CanViewType" IN (\'Anyone\', \'Inherit\') AND "ShowInSearch"=1 AND ("PublicHistory" IS NULL OR "PublicHistory" = \'1\') ORDER BY "LastEdited" DESC LIMIT 20'); + $lastChange = $latestChanges->record(); + $latestChanges->rewind(); + + if ($lastChange) { + + // Cache the diffs, otherwise it will take 5secs to generate 100 diffs which could lead to DOS. + $cache = SS_Cache::factory('VersionFeed_Controller'); + $cache->setOption('automatic_serialization', true); + $key = 'allchanges' . preg_replace('#[^a-zA-Z0-9_]#', '', $lastChange['LastEdited']); + + $changeList = $cache->load($key); + if(!$changeList) { + + $changeList = new ArrayList(); + + foreach ($latestChanges as $record) { + // Get the diff to the previous version. + $version = new Versioned_Version($record); + $changes = $version->getDiffedChanges($version->Version, false); + if ($changes && $changes->Count()) $changeList->push($changes->First()); + } + + $cache->save($changeList, $key); + } + } // Produce output @@ -65,4 +93,4 @@ class VersionFeed_Controller extends Extension { function linkToAllSitesRSSFeedTitle() { return sprintf(_t('RSSHistory.SITEFEEDTITLE', 'Updates to %s'), SiteConfig::current_site_config()->Title); } -} \ No newline at end of file +}