diff --git a/RssCloud/Subscriber.php b/RssCloud/Subscriber.php index 3475237..9159cc4 100644 --- a/RssCloud/Subscriber.php +++ b/RssCloud/Subscriber.php @@ -27,7 +27,8 @@ public function __construct( * or when the lease is old enough that the cloud server has probably expired it. * * rssCloud has no lease negotiation — the server does not tell us how long the subscription - * lasts — so this is purely a local timer. The conventional expiry is around 24 hours. + * lasts — so this is purely a local timer. Subscriptions expire after 25 hours and are meant to + * be renewed every 24 (http://walkthrough.rsscloud.co/). * * @param RssCloudState $state */ diff --git a/configure.phtml b/configure.phtml index da4ada7..204278a 100644 --- a/configure.phtml +++ b/configure.phtml @@ -48,7 +48,7 @@
-

diff --git a/extension.php b/extension.php index 612307f..9fced78 100644 --- a/extension.php +++ b/extension.php @@ -25,12 +25,22 @@ final class RssCloudExtension extends Minz_Extension { public const NAME = 'rssCloud'; /** - * rssCloud has no lease negotiation, and the protocol documentation does not state an expiry. - * The convention among implementations is around 24 hours, so renew slightly early — the same - * policy core applies to WebSub leases in `FreshRSS_Feed::pubSubHubbubPrepare()`. + * rssCloud has no lease negotiation: the `pleaseNotify` response carries no duration. The rule + * is documented instead — subscriptions expire after 25 hours and must be renewed every 24 + * (http://walkthrough.rsscloud.co/). Renewing at 23 stays an hour inside that requirement and + * leaves two hours before expiry, which is the margin {@see self::MAX_SUBSCRIPTIONS_PER_RUN} + * eats into when many subscriptions fall due at once. Core applies the same renew-early policy + * to WebSub leases in `FreshRSS_Feed::pubSubHubbubPrepare()`. */ public const DEFAULT_RENEW_HOURS = 23; + /** + * Renewing later than the documented 24-hour rule cannot work: the subscription is already gone + * by 25 hours, so every larger value simply lets it lapse. Clamped on the read path rather than + * only in the form, so a hand-edited configuration cannot get past it either. + */ + public const MAX_RENEW_HOURS = 24; + /** How stale a cloud-covered resource may get before we poll it anyway, as a safety net. */ public const DEFAULT_MAX_STALENESS_HOURS = 24; @@ -135,7 +145,8 @@ public function isPollingSkipped(): bool { } public function renewSeconds(): int { - return max(1, $this->getSystemConfigurationInt('renew_hours') ?? self::DEFAULT_RENEW_HOURS) * 3600; + $hours = $this->getSystemConfigurationInt('renew_hours') ?? self::DEFAULT_RENEW_HOURS; + return min(self::MAX_RENEW_HOURS, max(1, $hours)) * 3600; } public function maxStalenessSeconds(): int { diff --git a/i18n/en/ext.php b/i18n/en/ext.php index cfcdd72..01ca433 100644 --- a/i18n/en/ext.php +++ b/i18n/en/ext.php @@ -17,7 +17,7 @@ 'regenerate_token' => 'Regenerate callback token', 'regenerate_token.help' => 'Changes the secret path segment of the callback. Existing subscriptions stop being delivered until they are renewed.', 'renew_hours' => 'Renew subscriptions after (hours)', - 'renew_hours.help' => 'rssCloud does not negotiate a lease duration. Most servers expire subscriptions after about 24 hours, so renew slightly sooner than that.', + 'renew_hours.help' => 'rssCloud does not negotiate a lease duration. Subscriptions expire after 25 hours and are meant to be renewed every 24, so the default of 23 leaves a margin. Anything above 24 would simply lapse, and is capped.', 'skip_polling' => 'Skip polling covered resources', 'skip_polling.help' => 'Stop polling a resource on a timer while its cloud subscription is healthy. It is still polled if it goes stale, or when refreshed individually.', ),