* bring back cache-busting for feed icons based on timestamp

* DiskCache: use singleton pattern to create less cache object instances
 * DiskCache: implement ETag
This commit is contained in:
Andrew Dolgov 2022-12-19 21:36:50 +03:00
parent 20d6aaa9ab
commit d373b7b452
No known key found for this signature in database
GPG Key ID: 1A56B4FA25D4AF2A
10 changed files with 43 additions and 31 deletions

View File

@ -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));

View File

@ -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"];

View File

@ -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('<?xml encoding="UTF-8">' . $res)) {
$xpath = new DOMXPath($doc);
$cache = new DiskCache("images");
$cache = DiskCache::instance("images");
$entries = $xpath->query('(//img[@src]|//source[@src|@srcset]|//video[@poster|@src])');

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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');
}

View File

@ -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');

View File

@ -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);