diff --git a/tests/PHPCurlClass/ContentRangeServer.php b/tests/PHPCurlClass/ContentRangeServer.php index 796d2c1..8e6e6d5 100644 --- a/tests/PHPCurlClass/ContentRangeServer.php +++ b/tests/PHPCurlClass/ContentRangeServer.php @@ -8,8 +8,6 @@ class ContentRangeServer { public function serve($path) { - $range = new RangeHeader($_SERVER['HTTP_RANGE']); - $filesize = filesize($path); $fp = fopen($path, 'r'); @@ -19,16 +17,25 @@ class ContentRangeServer header('Accept-Ranges: bytes'); fpassthru($fp); } else { - header('HTTP/1.1 206 Partial Content'); - header('Content-Length: ' . $range->getLength($filesize)); - header('Content-Range: ' . $range->getContentRangeHeader($filesize)); + $range = new RangeHeader($_SERVER['HTTP_RANGE'], $path); - $start = $range->getFirstBytePosition($filesize); + if (!$range->isValid()) { + header('HTTP/1.1 416 Requested Range Not Satisfiable'); + header('Content-Range: ' . $range->getContentRangeHeader()); + exit; + } + + $length = $range->getLength(); + + header('HTTP/1.1 206 Partial Content'); + header('Content-Length: ' . $length); + header('Content-Range: ' . $range->getContentRangeHeader()); + + $start = $range->getFirstBytePosition(); if ($start > 0) { fseek($fp, $start, SEEK_SET); } - $length = $range->getLength($filesize); $chunk_size = 4096; while ($length) { $read = $length > $chunk_size ? $chunk_size : $length; diff --git a/tests/PHPCurlClass/RangeHeader.php b/tests/PHPCurlClass/RangeHeader.php index ff0c1cf..3835e15 100644 --- a/tests/PHPCurlClass/RangeHeader.php +++ b/tests/PHPCurlClass/RangeHeader.php @@ -6,48 +6,65 @@ class RangeHeader { private $first_byte; private $last_byte; + private $filesize; + private $is_valid = true; - public function __construct($http_range_header) + public function __construct($http_range_header, $file_path) { // Simulate basic support for the Content-Range header. preg_match('/bytes=(\d+)?-(\d+)?/', $http_range_header, $matches); $this->first_byte = isset($matches['1']) ? (int)$matches['1'] : null; $this->last_byte = isset($matches['2']) ? (int)$matches['2'] : null; + + $this->filesize = filesize($file_path); + + // Start position begins after end of file. + if ($this->first_byte >= $this->filesize) { + $this->is_valid = false; + } + + // "If the last-byte-pos value is present, it MUST be greater than or equal to the first-byte-pos in that + // byte-range-spec, or the byte- range-spec is syntactically invalid." + if (!($this->last_byte === null) && !($this->last_byte >= $this->first_byte)) { + $this->is_valid = false; + } } - public function getFirstBytePosition($file_size) + public function getFirstBytePosition() { - $size = (int)$file_size; - if ($this->first_byte === null) { - return $size - 1 - $this->last_byte; + return $this->filesize - 1 - $this->last_byte; } return $this->first_byte; } - public function getLastBytePosition($file_size) + public function getLastBytePosition() { - $size = (int)$file_size; - if ($this->last_byte === null) { - return $size - 1; + return $this->filesize - 1; } return $this->last_byte; } - public function getLength($file_size) + public function getLength() { - $size = (int)$file_size; - - return $this->getLastBytePosition($size) - $this->getFirstBytePosition($size) + 1; + return $this->getLastBytePosition() - $this->getFirstBytePosition() + 1; } - public function getContentRangeHeader($file_size) + public function getByteRangeSpec() { - return - 'bytes ' . $this->getFirstBytePosition($file_size) . '-' . $this->getLastBytePosition($file_size) . '/' . - $file_size; + return $this->is_valid ? $this->getFirstBytePosition() . '-' . $this->getLastBytePosition() : '*'; + } + + public function getContentRangeHeader() + { + return 'bytes ' . $this->getByteRangeSpec() . '/' . $this->filesize; + } + + public function isValid() + { + return $this->is_valid; } }