diff --git a/src/aphront/response/AphrontFileResponse.php b/src/aphront/response/AphrontFileResponse.php --- a/src/aphront/response/AphrontFileResponse.php +++ b/src/aphront/response/AphrontFileResponse.php @@ -97,7 +97,12 @@ if ($this->rangeMin || $this->rangeMax) { $len = $this->getContentLength(); $min = $this->rangeMin; + $max = $this->rangeMax; + if ($max === null) { + $max = ($len - 1); + } + $headers[] = array('Content-Range', "bytes {$min}-{$max}/{$len}"); $content_len = ($max - $min) + 1; } else { diff --git a/src/applications/files/controller/PhabricatorFileDataController.php b/src/applications/files/controller/PhabricatorFileDataController.php --- a/src/applications/files/controller/PhabricatorFileDataController.php +++ b/src/applications/files/controller/PhabricatorFileDataController.php @@ -64,14 +64,21 @@ $range = $request->getHTTPHeader('range'); if ($range) { $matches = null; - if (preg_match('/^bytes=(\d+)-(\d+)$/', $range, $matches)) { + if (preg_match('/^bytes=(\d+)-(\d*)$/', $range, $matches)) { // Note that the "Range" header specifies bytes differently than // we do internally: the range 0-1 has 2 bytes (byte 0 and byte 1). $begin = (int)$matches[1]; - $end = (int)$matches[2] + 1; + + // The "Range" may be "200-299" or "200-", meaning "until end of file". + if (strlen($matches[2])) { + $range_end = (int)$matches[2]; + $end = $range_end + 1; + } else { + $range_end = null; + } $response->setHTTPResponseCode(206); - $response->setRange($begin, ($end - 1)); + $response->setRange($begin, $range_end); } } diff --git a/src/applications/files/format/__tests__/PhabricatorFileStorageFormatTestCase.php b/src/applications/files/format/__tests__/PhabricatorFileStorageFormatTestCase.php --- a/src/applications/files/format/__tests__/PhabricatorFileStorageFormatTestCase.php +++ b/src/applications/files/format/__tests__/PhabricatorFileStorageFormatTestCase.php @@ -91,6 +91,14 @@ $raw_data .= $data_chunk; } $this->assertEqual('cow jumped', $raw_data); + + $iterator = $file->getFileDataIterator(4, null); + $raw_data = ''; + foreach ($iterator as $data_chunk) { + $raw_data .= $data_chunk; + } + $this->assertEqual('cow jumped over the full moon.', $raw_data); + } public function testStorageTampering() {