Skip to content
Merged
82 changes: 43 additions & 39 deletions public_html/static.php
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ function validateStaticFile(string $path): ?string
*/
function serveStaticFile($path): void
{
// Clean any output buffers to prevent Content-Length mismatch with PHP built-in server
while (ob_get_level()) {
ob_end_clean();
}

$lastModifiedTime = filemtime($path);

header('Last-Modified: ' . gmdate('D, d M Y H:i:s \G\M\T', $lastModifiedTime));
Expand All @@ -133,74 +138,73 @@ function serveStaticFile($path): void
}

$size = filesize($path);
$fp = fopen($path, 'r');
$range = [0, $size - 1];
$ext = pathinfo($path, \PATHINFO_EXTENSION);

$headers = [
'Accept-Ranges' => 'bytes',
'Content-Type' => SUPPORTED_TYPES[strtolower($ext)],
'Cache-Control' => 'public, max-age=604800',
'Expires' => gmdate('D, d M Y H:i:s \G\M\T', time() + 30 * 86400),
];

$start = 0;
$end = $size - 1;

// Handle range requests
if (isset($_SERVER['HTTP_RANGE'])) {
// $valid = preg_match('^bytes=\d*-\d*(,\d*-\d*)*$', $_SERVER['HTTP_RANGE']);
if (!str_starts_with($_SERVER['HTTP_RANGE'], 'bytes=')) {
http_response_code(416); // "Range Not Satisfiable"
header('Content-Range: bytes */' . $size); // Required in 416.
header('Content-Range: bytes */' . $size);
return;
}

$ranges = explode(',', substr($_SERVER['HTTP_RANGE'], 6));
$range = explode('-', $ranges[0]); // TODO: only support the first range now.
$range = explode('-', $ranges[0]);

// Handle suffix-range (e.g., "bytes=-500" means last 500 bytes)
if ($range[0] === '') {
$range[0] = 0;
}
if ($range[1] === '') {
$range[1] = $size - 1;
$start = max(0, $size - (int) $range[1]);
} else {
$start = (int) $range[0];
$end = $range[1] === '' ? $size - 1 : (int) $range[1];
}

if ($range[0] >= 0 && ($range[1] <= $size - 1) && $range[0] <= $range[1]) {
http_response_code(206); // "Partial Content"
header('Content-Range: bytes ' . sprintf('%u-%u/%u', $range[0], $range[1], $size));
} else {
if ($start < 0 || $end > $size - 1 || $start > $end) {
http_response_code(416); // "Range Not Satisfiable"
header('Content-Range: bytes */' . $size);
return;
}

http_response_code(206); // "Partial Content"
header('Content-Range: bytes ' . sprintf('%u-%u/%u', $start, $end, $size));
}

$contentLength = $range[1] - $range[0] + 1;
$ext = pathinfo($path, \PATHINFO_EXTENSION);
// Open file before sending headers so we can return 500 on failure
$fp = fopen($path, 'r');
if ($fp === false) {
http_response_code(500);
return;
}

$headers = [
'Accept-Ranges' => 'bytes',
'Content-Length' => $contentLength,
'Content-Type' => SUPPORTED_TYPES[strtolower($ext)],
// 'Content-Disposition: attachment; filename="xxxxx"',
'Cache-Control' => 'public, max-age=604800',
'Expires' => gmdate('D, d M Y H:i:s \G\M\T', time() + 30 * 86400),
];
$headers['Content-Length'] = $end - $start + 1;

foreach ($headers as $k => $v) {
header("{$k}: {$v}", true);
}

if ($range[0] > 0) {
fseek($fp, $range[0]);
if ($start > 0) {
fseek($fp, $start);
}

$sentSize = 0;

while (!feof($fp) && (connection_status() === \CONNECTION_NORMAL)) {
$readingSize = $contentLength - $sentSize;
$readingSize = min($readingSize, 512 * 1024);
$remaining = $end - $start + 1;

if ($readingSize <= 0) {
while ($remaining > 0 && !feof($fp) && connection_status() === \CONNECTION_NORMAL) {
$chunk = fread($fp, min($remaining, 8192));
if ($chunk === false) {
break;
}

$data = fread($fp, $readingSize);
if ($data === false) {
break;
}

$sentSize += strlen($data);
echo $data;
echo $chunk;
$remaining -= strlen($chunk);
flush();
}

Expand Down
Loading
Loading