WebSub: ignore http/https scheme difference in Self URL comparison (#9005)

* WebSub: ignore http/https scheme difference in Self URL comparison

The PubSubHubbub endpoint logged 'Self URL does not match registered
canonical URL' whenever a feed's self URL differed from the registered
canonical URL only by http vs https (common with http->https redirects),
spamming the log. Compare the URLs ignoring the http/https scheme while
still requiring host/path/query to match exactly.

Closes #3087

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Stylistic preferences

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
This commit is contained in:
Jam Balaya
2026-07-09 14:50:42 +09:00
committed by GitHub
parent 6908d998d4
commit 7744f1fed9
3 changed files with 51 additions and 2 deletions

View File

@@ -295,6 +295,14 @@ final class FreshRSS_http_Util {
($url1['port'] ?? '') === ($url2['port'] ?? '');
}
/**
* Return 0 if values on either side are equal ignoring the HTTP vs HTTPS differences, or 1/-1 if they differ.
*/
public static function compareUrlIgnoringHttps(string $url1, string $url2): int {
$normalizeScheme = static fn(string $url): string => preg_replace('#^https?://#i', '//', trim($url)) ?? $url;
return $normalizeScheme($url1) <=> $normalizeScheme($url2);
}
/**
* Returns a value for CURLOPT_RESOLVE as an array, null if no allowed IPs were found, false if the domain failed to resolve.
*

View File

@@ -121,14 +121,14 @@ if ($httpLink !== '' && preg_match_all('/<([^>]+)>;\\s*rel="([^"]+)"/', $httpLin
// }
if (!empty($links['self'])) {
$httpSelf = FreshRSS_http_Util::checkUrl($links['self']) ?: '';
if ($self !== '' && $self !== $httpSelf) {
if ($self !== '' && FreshRSS_http_Util::compareUrlIgnoringHttps($self, $httpSelf) !== 0) {
Minz_Log::warning('Warning: Self URL mismatch between XML [' . $self . '] and HTTP!: ' . $httpSelf, PSHB_LOG);
}
$self = $httpSelf;
}
}
if ($self !== $canonical) {
if (FreshRSS_http_Util::compareUrlIgnoringHttps($self, $canonical) !== 0) {
//header('HTTP/1.1 422 Unprocessable Entity');
Minz_Log::warning('Warning: Self URL [' . $self . '] does not match registered canonical URL!: ' . $canonical, PSHB_LOG);
//die('Self URL does not match registered canonical URL!');

View File

@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
use PHPUnit\Framework\Attributes\DataProvider;
/**
* Tests for FreshRSS_http_Util
*/
class httpUtilTest extends \PHPUnit\Framework\TestCase {
#[DataProvider('provideUrlsIgnoringHttps')]
public function test_compareUrlIgnoringHttps(string $url1, string $url2, bool $expected): void {
self::assertEquals($expected, FreshRSS_http_Util::compareUrlIgnoringHttps($url1, $url2) === 0);
}
/** @return list<array{string,string,bool}> */
public static function provideUrlsIgnoringHttps(): array {
return [
// Only the scheme differs → equal
['http://www.blogger.com/feeds/1/posts', 'https://www.blogger.com/feeds/1/posts', true],
['https://example.net/feed.xml?a=1&b=2', 'http://example.net/feed.xml?a=1&b=2', true],
['HTTP://Example.net/Feed', 'https://Example.net/Feed', true],
['HTTPS://Example.net/Feed', 'http://Example.net/Feed', true],
// Fully identical → equal
['https://example.net/feed', 'https://example.net/feed', true],
['', '', true],
// Path differs → not equal (scheme-only tolerance must not hide real mismatches)
['http://example.net/a', 'https://example.net/b', false],
// Trailing slash is a path difference → not equal
['http://example.net/', 'https://example.net', false],
// Host differs → not equal
['http://a.example.net/feed', 'https://b.example.net/feed', false],
// Query differs → not equal
['https://example.net/feed?a=1', 'http://example.net/feed?a=2', false],
// Non-http(s) schemes are compared as-is
['ftp://example.net/feed', 'https://example.net/feed', false],
];
}
}