From d373b7b452c6d64b47180940ed88c99c21bd9bc3 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Mon, 19 Dec 2022 21:36:50 +0300 Subject: [PATCH] * bring back cache-busting for feed icons based on timestamp * DiskCache: use singleton pattern to create less cache object instances * DiskCache: implement ETag --- classes/article.php | 4 ++-- classes/counters.php | 6 +++--- classes/diskcache.php | 19 +++++++++++++++---- classes/feeds.php | 4 ++-- classes/handler/public.php | 4 ++-- classes/pref/feeds.php | 9 +++++---- classes/rssutils.php | 16 ++++++++-------- js/Feeds.js | 6 +++--- .../filters/af_comics_gocomics_farside.php | 2 +- plugins/cache_starred_images/init.php | 4 ++-- 10 files changed, 43 insertions(+), 31 deletions(-) diff --git a/classes/article.php b/classes/article.php index 1d441dee2..609ddeebe 100755 --- a/classes/article.php +++ b/classes/article.php @@ -447,7 +447,7 @@ class Article extends Handler_Protected { $rv = []; - $cache = new DiskCache("images"); + $cache = DiskCache::instance("images"); foreach ($encs as $enc) { $cache_key = sha1($enc->content_url); @@ -640,7 +640,7 @@ class Article extends Handler_Protected { $article_stream = UrlHelper::rewrite_relative($site_url, $article_stream); } - $cache = new DiskCache("images"); + $cache = DiskCache::instance("images"); if ($article_image && $cache->exists(sha1($article_image))) $article_image = $cache->get_url(sha1($article_image)); diff --git a/classes/counters.php b/classes/counters.php index 9699cb97c..48b7264dd 100644 --- a/classes/counters.php +++ b/classes/counters.php @@ -187,9 +187,9 @@ class Counters { $last_updated = TimeHelper::make_local_datetime($line['last_updated'], false); if (Feeds::_has_icon($id)) { - $has_img = filemtime(Feeds::_get_icon_file($id)); + $ts = filemtime(Feeds::_get_icon_file($id)); } else { - $has_img = false; + $ts = 0; } // hide default un-updated timestamp i.e. 1970-01-01 (?) -fox @@ -201,7 +201,7 @@ class Counters { "updated" => $last_updated, "counter" => (int) $line["count"], "markedcounter" => (int) $line["count_marked"], - "has_img" => (int) $has_img + "ts" => (int) $ts ]; $cv["error"] = $line["last_error"]; diff --git a/classes/diskcache.php b/classes/diskcache.php index bf7031587..96c826728 100644 --- a/classes/diskcache.php +++ b/classes/diskcache.php @@ -3,6 +3,8 @@ class DiskCache implements Cache_Adapter { /** @var Cache_Adapter $adapter */ private $adapter; + private static $instances = []; + /** * https://stackoverflow.com/a/53662733 * @@ -195,6 +197,13 @@ class DiskCache implements Cache_Adapter { 'text/x-scriptzsh' => 'zsh' ]; + public static function instance(string $dir) : DiskCache { + if ((self::$instances[$dir] ?? null) == null) + self::$instances[$dir] = new self($dir); + + return self::$instances[$dir]; + } + public function __construct(string $dir) { foreach (PluginHost::getInstance()->get_plugins() as $n => $p) { if (implements_interface($p, "Cache_Adapter")) { @@ -302,9 +311,10 @@ class DiskCache implements Cache_Adapter { return false; } - $gmt_modified = gmdate("D, d M Y H:i:s", (int)$this->get_mtime($filename)) . " GMT"; + $file_mtime = $this->get_mtime($filename); + $gmt_modified = gmdate("D, d M Y H:i:s", (int)$file_mtime) . " GMT"; - if (($_SERVER['HTTP_IF_MODIFIED_SINCE'] ?? '') == $gmt_modified) { + if (($_SERVER['HTTP_IF_MODIFIED_SINCE'] ?? '') == $gmt_modified || ($_SERVER['HTTP_IF_NONE_MATCH'] ?? '') == $file_mtime) { header('HTTP/1.1 304 Not Modified'); return false; } @@ -339,7 +349,8 @@ class DiskCache implements Cache_Adapter { header("Expires: $stamp_expires", true); header("Last-Modified: $gmt_modified", true); - header("Cache-Control: public"); + header("Cache-Control: no-cache"); + header("ETag: $file_mtime"); header_remove("Pragma"); @@ -378,7 +389,7 @@ class DiskCache implements Cache_Adapter { $doc = new DOMDocument(); if (@$doc->loadHTML('' . $res)) { $xpath = new DOMXPath($doc); - $cache = new DiskCache("images"); + $cache = DiskCache::instance("images"); $entries = $xpath->query('(//img[@src]|//source[@src|@srcset]|//video[@poster|@src])'); diff --git a/classes/feeds.php b/classes/feeds.php index d34a23e4b..382d8dbf8 100755 --- a/classes/feeds.php +++ b/classes/feeds.php @@ -1163,7 +1163,7 @@ class Feeds extends Handler_Protected { } static function _get_icon_file(int $feed_id): string { - $favicon_cache = new DiskCache('feed-icons'); + $favicon_cache = DiskCache::instance('feed-icons'); return $favicon_cache->get_full_path((string)$feed_id); } @@ -1182,7 +1182,7 @@ class Feeds extends Handler_Protected { } static function _has_icon(int $feed_id): bool { - $favicon_cache = new DiskCache('feed-icons'); + $favicon_cache = DiskCache::instance('feed-icons'); return $favicon_cache->exists((string)$feed_id); } diff --git a/classes/handler/public.php b/classes/handler/public.php index b848b15fe..190c806be 100755 --- a/classes/handler/public.php +++ b/classes/handler/public.php @@ -759,7 +759,7 @@ class Handler_Public extends Handler { // we do not allow files with extensions at the moment $filename = str_replace(".", "", $filename); - $cache = new DiskCache($cache_dir); + $cache = DiskCache::instance($cache_dir); if ($cache->exists($filename)) { $cache->send($filename); @@ -771,7 +771,7 @@ class Handler_Public extends Handler { function feed_icon() : void { $id = (int)$_REQUEST['id']; - $cache = new DiskCache('feed-icons'); + $cache = DiskCache::instance('feed-icons'); if ($cache->exists((string)$id)) { $cache->send((string)$id); diff --git a/classes/pref/feeds.php b/classes/pref/feeds.php index 067b8225e..a9ae91979 100755 --- a/classes/pref/feeds.php +++ b/classes/pref/feeds.php @@ -455,7 +455,7 @@ class Pref_Feeds extends Handler_Protected { function removeIcon(): void { $feed_id = (int) $_REQUEST["feed_id"]; - $cache = new DiskCache('feed-icons'); + $cache = DiskCache::instance('feed-icons'); $feed = ORM::for_table('ttrss_feeds') ->where('owner_uid', $_SESSION['uid']) @@ -487,7 +487,7 @@ class Pref_Feeds extends Handler_Protected { if ($feed && $tmp_file && move_uploaded_file($_FILES['icon_file']['tmp_name'], $tmp_file)) { if (filesize($tmp_file) < Config::get(Config::MAX_FAVICON_FILE_SIZE)) { - $cache = new DiskCache('feed-icons'); + $cache = DiskCache::instance('feed-icons'); if ($cache->put((string)$feed_id, file_get_contents($tmp_file))) { @@ -514,7 +514,8 @@ class Pref_Feeds extends Handler_Protected { if (file_exists($tmp_file)) unlink($tmp_file); - print json_encode(['rc' => $rc, 'icon_url' => Feeds::_get_icon($feed_id)]); + print json_encode(['rc' => $rc, 'icon_url' => + Feeds::_get_icon($feed_id) . "?ts=" . time() ]); } function editfeed(): void { @@ -1188,7 +1189,7 @@ class Pref_Feeds extends Handler_Protected { $pdo->commit(); - $favicon_cache = new DiskCache('feed-icons'); + $favicon_cache = DiskCache::instance('feed-icons'); if ($favicon_cache->exists((string)$id)) $favicon_cache->remove((string)$id); diff --git a/classes/rssutils.php b/classes/rssutils.php index 9d9b335c6..561171d09 100755 --- a/classes/rssutils.php +++ b/classes/rssutils.php @@ -37,7 +37,7 @@ class RSSUtils { $pdo = Db::pdo(); $sth = $pdo->prepare("SELECT id FROM ttrss_feeds WHERE id = ?"); - $cache = new DiskCache('feed-icons'); + $cache = DiskCache::instance('feed-icons'); if ($cache->is_writable()) { $dh = opendir($cache->get_full_path("")); @@ -348,7 +348,7 @@ class RSSUtils { $pdo = Db::pdo(); /** @var DiskCache $cache */ - $cache = new DiskCache('feeds'); + $cache = DiskCache::instance('feeds'); if (Config::get(Config::DB_TYPE) == "pgsql") { $favicon_interval_qpart = "favicon_last_checked < NOW() - INTERVAL '12 hour'"; @@ -606,7 +606,7 @@ class RSSUtils { $feed_obj->favicon_last_checked = Db::NOW(); $feed_obj->save(); - $favicon_cache = new DiskCache('feed-icons'); + $favicon_cache = DiskCache::instance('feed-icons'); $favicon_modified = $favicon_cache->exists($feed) ? $favicon_cache->get_mtime($feed) : -1; @@ -1320,7 +1320,7 @@ class RSSUtils { * @see FeedEnclosure */ static function cache_enclosures(array $enclosures, string $site_url): void { - $cache = new DiskCache("images"); + $cache = DiskCache::instance("images"); if ($cache->is_writable()) { foreach ($enclosures as $enc) { @@ -1372,7 +1372,7 @@ class RSSUtils { /* TODO: move to DiskCache? */ static function cache_media(string $html, string $site_url): void { - $cache = new DiskCache("images"); + $cache = DiskCache::instance("images"); if ($html && $cache->is_writable()) { $doc = new DOMDocument(); @@ -1695,7 +1695,7 @@ class RSSUtils { $dh = opendir($old_dir); - $cache = new DiskCache('feed-icons'); + $cache = DiskCache::instance('feed-icons'); if ($dh) { while (($old_filename = readdir($dh)) !== false) { @@ -1714,7 +1714,7 @@ class RSSUtils { } static function housekeeping_common(): void { - $cache = new DiskCache(""); + $cache = DiskCache::instance(""); $cache->expire_all(); self::migrate_feed_icons(); @@ -1789,7 +1789,7 @@ class RSSUtils { break; } - $favicon_cache = new DiskCache('feed-icons'); + $favicon_cache = DiskCache::instance('feed-icons'); if ($favicon_cache->is_writable()) { Debug::log("favicon: $favicon_url looks valid, saving to cache", Debug::LOG_VERBOSE); diff --git a/js/Feeds.js b/js/Feeds.js index b3913b1f0..42641dc74 100644 --- a/js/Feeds.js +++ b/js/Feeds.js @@ -71,7 +71,7 @@ const Feeds = { const kind = elems[l].kind; const ctr = parseInt(elems[l].counter); const error = elems[l].error; - const has_img = elems[l].has_img; + const ts = elems[l].ts; const updated = elems[l].updated; if (id == "global-unread") { @@ -98,9 +98,9 @@ const Feeds = { this.setValue(id, false, 'updated', updated); if (id > 0) { - if (has_img) { + if (ts) { this.setIcon(id, false, - App.getInitParam("icons_url") + '?' + dojo.objectToQuery({op: 'feed_icon', id: id})); + App.getInitParam("icons_url") + '?' + dojo.objectToQuery({op: 'feed_icon', id: id, ts: ts})); } else { this.setIcon(id, false, 'images/blank_icon.gif'); } diff --git a/plugins/af_comics/filters/af_comics_gocomics_farside.php b/plugins/af_comics/filters/af_comics_gocomics_farside.php index e4e230516..e2951eb36 100644 --- a/plugins/af_comics/filters/af_comics_gocomics_farside.php +++ b/plugins/af_comics/filters/af_comics_gocomics_farside.php @@ -50,7 +50,7 @@ class Af_Comics_Gocomics_FarSide extends Af_ComicFilter { if ($content_node) { $imgs = $xpath->query('//img[@data-src]', $content_node); - $cache = new DiskCache("images"); + $cache = DiskCache::instance("images"); foreach ($imgs as $img) { $image_url = $img->getAttribute('data-src'); diff --git a/plugins/cache_starred_images/init.php b/plugins/cache_starred_images/init.php index daaab2dca..208eafde9 100755 --- a/plugins/cache_starred_images/init.php +++ b/plugins/cache_starred_images/init.php @@ -21,8 +21,8 @@ class Cache_Starred_Images extends Plugin { function init($host) { $this->host = $host; - $this->cache = new DiskCache("starred-images"); - $this->cache_status = new DiskCache("starred-images.status-files"); + $this->cache = DiskCache::instance("starred-images"); + $this->cache_status = DiskCache::instance("starred-images.status-files"); if ($this->cache->make_dir()) chmod($this->cache->get_dir(), 0777);