mirror of
https://github.com/Vateron-Media/XC_VM.git
synced 2026-10-03 12:02:29 +02:00
- Timeshift TS seeking returned the wrong bytes: the start file was estimated from the average file size, files before it were never skipped (an empty `if` where a `continue` belonged) and the in-file offset came out negative, so every catch-up seek streamed from the archive's first byte; the range end was ignored too. The served range is now mapped exactly onto the minute files (first file from its .offset). - The timeshift throttle never reset its chunk counter (vod.php's copy did), so past vod_limit_perc every chunk paused a whole second. - A shared HttpRange parser (RFC 7233 single ranges) replaces the inline copies: suffix ranges (bytes=-N) died on PHP 8 arithmetic, an unsatisfiable range answered with the resource's range instead of `bytes */size`, and Accept-Ranges said "0-<len>" instead of "bytes". VOD never reads past a bounded range's end. - Direct-proxy VOD reads the source's headers with cURL: get_headers() goes through the https stream wrapper, which does not work under PHP-FPM here, and returned Content-Length as an array after a redirect. The upstream is asked for exactly the requested range, and curl's verbose output no longer goes to the FPM log on every request. - The limiter spares the requesting connection by uuid (VOD/timeshift). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
44 lines
1.9 KiB
PHP
44 lines
1.9 KiB
PHP
<?php
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
use XcVm\Streaming\Delivery\HttpRange;
|
|
|
|
/**
|
|
* @covers \XcVm\Streaming\Delivery\HttpRange
|
|
*
|
|
* The single-range parser the VOD and timeshift endpoints share. The inline
|
|
* copies it replaced broke on suffix ranges (`bytes=-N`) and answered an
|
|
* unsatisfiable range with the full resource's Content-Range.
|
|
*/
|
|
final class HttpRangeTest extends TestCase {
|
|
|
|
public function testNoRangeServesTheWholeResource(): void {
|
|
$this->assertNull(HttpRange::parse(null, 1000));
|
|
$this->assertNull(HttpRange::parse('', 1000));
|
|
$this->assertNull(HttpRange::parse('items=0-9', 1000), 'another unit is ignored');
|
|
}
|
|
|
|
public function testBoundedAndOpenRanges(): void {
|
|
$this->assertSame([0, 99], HttpRange::parse('bytes=0-99', 1000));
|
|
$this->assertSame([0, 1], HttpRange::parse('bytes=0-1', 1000), 'a player probing the first bytes');
|
|
$this->assertSame([500, 999], HttpRange::parse('bytes=500-', 1000));
|
|
$this->assertSame([10, 999], HttpRange::parse('bytes=10-5000', 1000), 'end clamped to the size');
|
|
$this->assertSame([10, 20], HttpRange::parse(' Bytes = 10 - 20 ', 1000));
|
|
}
|
|
|
|
public function testSuffixRangeIsTheLastBytes(): void {
|
|
$this->assertSame([900, 999], HttpRange::parse('bytes=-100', 1000));
|
|
$this->assertSame([0, 999], HttpRange::parse('bytes=-5000', 1000), 'longer than the resource: all of it');
|
|
}
|
|
|
|
public function testUnsatisfiableRanges(): void {
|
|
$this->assertFalse(HttpRange::parse('bytes=1000-', 1000), 'starts past the end');
|
|
$this->assertFalse(HttpRange::parse('bytes=5-2', 1000), 'end before start');
|
|
$this->assertFalse(HttpRange::parse('bytes=-0', 1000), 'empty suffix');
|
|
$this->assertFalse(HttpRange::parse('bytes=0-1,5-6', 1000), 'multipart is not served');
|
|
$this->assertFalse(HttpRange::parse('bytes=abc', 1000));
|
|
$this->assertFalse(HttpRange::parse('bytes=-', 1000));
|
|
$this->assertFalse(HttpRange::parse('bytes=0-9', 0), 'nothing to serve');
|
|
}
|
|
}
|