httpUtilTest.php 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. <?php
  2. declare(strict_types=1);
  3. use PHPUnit\Framework\Attributes\DataProvider;
  4. /**
  5. * Tests for FreshRSS_http_Util
  6. */
  7. final class httpUtilTest extends \PHPUnit\Framework\TestCase {
  8. #[DataProvider('provideUrlsIgnoringHttps')]
  9. public function test_compareUrlIgnoringHttps(string $url1, string $url2, bool $expected): void {
  10. self::assertEquals($expected, FreshRSS_http_Util::compareUrlIgnoringHttps($url1, $url2) === 0);
  11. }
  12. #[\Override]
  13. protected function tearDown(): void {
  14. $resolveOk = new ReflectionProperty(FreshRSS_http_Util::class, 'resolve_ok');
  15. $resolveOk->setValue(null, []); // Restore the default empty cache
  16. }
  17. #[DataProvider('provideUrlsForRetryAfter')]
  18. public function test_getRetryAfterFile(string $url1, string $url2, bool $sameFile): void {
  19. $getRetryAfterFile = new ReflectionMethod(FreshRSS_http_Util::class, 'getRetryAfterFile');
  20. self::assertSame($sameFile, $getRetryAfterFile->invoke(null, $url1, '') === $getRetryAfterFile->invoke(null, $url2, ''));
  21. }
  22. /** @return array<string,array{string,string,bool}> */
  23. public static function provideUrlsForRetryAfter(): array {
  24. return [
  25. // A public server waits as a whole, per port
  26. 'public server' => ['https://198.51.100.7/feed1', 'https://198.51.100.7/feed2?a=1&b=2', true],
  27. 'public server, other port' => ['https://198.51.100.7/feed', 'https://198.51.100.7:8443/feed', false],
  28. // A server on a local network waits URL by URL
  29. 'local IP address' => ['http://192.168.1.2/feed1', 'http://192.168.1.2/feed2', false],
  30. 'local domain' => ['http://rss-bridge.lan/?bridge=A', 'http://rss-bridge.lan/?bridge=B', false],
  31. 'local, same URL' => ['http://192.168.1.2/feed', 'http://192.168.1.2/feed', true],
  32. ];
  33. }
  34. #[DataProvider('provideCidrRanges')]
  35. public function test_checkCIDR(string $ip, string $range, bool $expected): void {
  36. $checkCIDR = new ReflectionMethod(FreshRSS_http_Util::class, 'checkCIDR');
  37. self::assertEquals($expected, $checkCIDR->invoke(null, $ip, $range));
  38. }
  39. /** @return list<array{string,string,bool}> */
  40. public static function provideCidrRanges(): array {
  41. return [
  42. // A range without a subnet is a single address
  43. ['192.168.1.1', '192.168.1.1', true],
  44. ['192.168.1.2', '192.168.1.1', false],
  45. ['2001:db8::1', '2001:db8::1', true],
  46. ['2001:db8::2', '2001:db8::1', false],
  47. // An explicit subnet keeps working
  48. ['192.168.1.1', '192.168.1.1/32', true],
  49. ['192.168.1.42', '192.168.1.0/24', true],
  50. ['192.168.2.42', '192.168.1.0/24', false],
  51. ['192.168.1.1', '0.0.0.0/0', true],
  52. ['2001:db8::1', '2001:db8::/32', true],
  53. // Invalid input is still rejected
  54. ['192.168.1.1', '', false],
  55. ['192.168.1.1', '192.168.1.1/', false],
  56. ['192.168.1.1', '192.168.1.1/33', false],
  57. ['192.168.1.1', '192.168.1.1/abc', false],
  58. ['192.168.1.1', 'gateway.localdomain', false],
  59. ['192.168.1.1', '2001:db8::1', false],
  60. ['not-an-ip', '192.168.1.1', false],
  61. ];
  62. }
  63. /** @return list<array{string,string,bool}> */
  64. public static function provideUrlsIgnoringHttps(): array {
  65. return [
  66. // Only the scheme differs → equal
  67. ['http://www.blogger.com/feeds/1/posts', 'https://www.blogger.com/feeds/1/posts', true],
  68. ['https://example.net/feed.xml?a=1&b=2', 'http://example.net/feed.xml?a=1&b=2', true],
  69. ['HTTP://Example.net/Feed', 'https://Example.net/Feed', true],
  70. ['HTTPS://Example.net/Feed', 'http://Example.net/Feed', true],
  71. // Fully identical → equal
  72. ['https://example.net/feed', 'https://example.net/feed', true],
  73. ['', '', true],
  74. // Path differs → not equal (scheme-only tolerance must not hide real mismatches)
  75. ['http://example.net/a', 'https://example.net/b', false],
  76. // Trailing slash is a path difference → not equal
  77. ['http://example.net/', 'https://example.net', false],
  78. // Host differs → not equal
  79. ['http://a.example.net/feed', 'https://b.example.net/feed', false],
  80. // Query differs → not equal
  81. ['https://example.net/feed?a=1', 'http://example.net/feed?a=2', false],
  82. // Non-http(s) schemes are compared as-is
  83. ['ftp://example.net/feed', 'https://example.net/feed', false],
  84. ];
  85. }
  86. public function test_getCurlResolveInfoAcceptsPublicNat64Address(): void {
  87. FreshRSS_Context::initSystem();
  88. $resolveOk = new ReflectionProperty(FreshRSS_http_Util::class, 'resolve_ok');
  89. $resolveOk->setValue(null, [
  90. 'example.test' => [
  91. '192.0.66.96',
  92. '64:ff9b::c000:4260',
  93. ],
  94. ]);
  95. self::assertSame(
  96. ['example.test:443:192.0.66.96,[64:ff9b::c000:4260]'],
  97. FreshRSS_http_Util::getCurlResolveInfo('https://example.test/feed')
  98. );
  99. }
  100. public function test_getCurlResolveInfoRejectsPrivateNat64Address(): void {
  101. FreshRSS_Context::initSystem();
  102. $resolveOk = new ReflectionProperty(FreshRSS_http_Util::class, 'resolve_ok');
  103. $resolveOk->setValue(null, [
  104. 'example.test' => [
  105. '64:ff9b::a9fe:a9fe',
  106. ],
  107. ]);
  108. self::assertNull(
  109. FreshRSS_http_Util::getCurlResolveInfo('https://example.test/feed')
  110. );
  111. }
  112. public function test_getCurlResolveInfoRejectsLocalUseNat64Address(): void {
  113. FreshRSS_Context::initSystem();
  114. $resolveOk = new ReflectionProperty(FreshRSS_http_Util::class, 'resolve_ok');
  115. $resolveOk->setValue(null, [
  116. 'example.test' => [
  117. '64:ff9b:1::c000:4260',
  118. '64:ff9b:1::a9fe:a9fe',
  119. ],
  120. ]);
  121. self::assertNull(
  122. FreshRSS_http_Util::getCurlResolveInfo('https://example.test/feed')
  123. );
  124. }
  125. }