Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion server/lib/OPodSync/Feed.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public function sync(): void
$db->exec('END');
}

public function fetch(): bool
public function fetch(?int $soft_timeout = null): bool
{
if (function_exists('curl_exec')) {
$ch = curl_init($this->feed_url);
Expand Down Expand Up @@ -101,7 +101,18 @@ public function fetch(): bool
return false;
}

// Soft time budget for parsing this feed, started after the feed downloads
// so we measure parse time, not network wait. This is useful in cases
// where this is called in an iteration fetching all feeds - especially
// if that iteration has a process fail limit say using set_time_limit()
$deadline = $soft_timeout !== null ? microtime(true) + $soft_timeout : null;

while (preg_match('!<item[^>]*>(.*?)</item>!s', $body, $match)) {
if ($deadline !== null && microtime(true) > $deadline) {
error_log(sprintf('oPodSync: parsing feed %s exceeded the %ds budget, skipping', $this->feed_url, $soft_timeout));
return false;
}

$body = str_replace($match[0], '', $body);
$item = $match[1];
$pubdate = $this->getTagValue($item, 'pubDate');
Expand Down
18 changes: 14 additions & 4 deletions server/lib/OPodSync/GPodder.php
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ public function listEpisodes(int $subscription): array
ORDER BY e.pubdate DESC;', $subscription, $this->user->id);
}

public function updateFeedForSubscription(int $subscription): ?Feed
public function updateFeedForSubscription(int $subscription, ?int $soft_timeout = null): ?Feed
{
$db = DB::getInstance();
$url = $db->firstColumn('SELECT url FROM subscriptions WHERE id = ?;', $subscription);
Expand All @@ -272,7 +272,7 @@ public function updateFeedForSubscription(int $subscription): ?Feed

$feed = new Feed($url);

if (!$feed->fetch()) {
if (!$feed->fetch($soft_timeout)) {
return null;
}

Expand Down Expand Up @@ -361,8 +361,18 @@ public function updateAllFeeds(bool $cli = false): void

$db = DB::getInstance();

// Two time limits per feed. A soft one allowing 30s per feed and failing
// gracefully allowing the iteration to continue and a hard one to allow
// the process (and the iteration) to completely fail if a feed takes even
// longer to respond (for example for an unbounded feed item). This is
// necessary because it is not possible to catch the failure from
// set_time_limit() thus some feeds after the failure will never get updated.
$soft_timeout = 30;
$hard_time_limit = 120;

foreach ($db->iterate($sql) as $row) {
@set_time_limit(30); // Extend running time;
// set_time_limit() restarts the timer, so reset the hard limit per feed
@set_time_limit($hard_time_limit);

if ($cli) {
printf("Updating %s\n", $row->url);
Expand All @@ -373,7 +383,7 @@ public function updateAllFeeds(bool $cli = false): void
flush();
}

$this->updateFeedForSubscription($row->subscription);
$this->updateFeedForSubscription($row->subscription, $soft_timeout);
$i++;
}

Expand Down