mirror of
https://github.com/linuxserver/Heimdall.git
synced 2025-12-10 08:53:57 +09:00
Updates to vendors etc
This commit is contained in:
@@ -25,16 +25,14 @@ use Symfony\Component\HttpFoundation\File\File;
|
||||
*/
|
||||
class BinaryFileResponse extends Response
|
||||
{
|
||||
protected static $trustXSendfileTypeHeader = false;
|
||||
protected static bool $trustXSendfileTypeHeader = false;
|
||||
|
||||
/**
|
||||
* @var File
|
||||
*/
|
||||
protected $file;
|
||||
protected $offset = 0;
|
||||
protected $maxlen = -1;
|
||||
protected $deleteFileAfterSend = false;
|
||||
protected $chunkSize = 16 * 1024;
|
||||
protected File $file;
|
||||
protected ?\SplTempFileObject $tempFileObject = null;
|
||||
protected int $offset = 0;
|
||||
protected int $maxlen = -1;
|
||||
protected bool $deleteFileAfterSend = false;
|
||||
protected int $chunkSize = 16 * 1024;
|
||||
|
||||
/**
|
||||
* @param \SplFileInfo|string $file The file to stream
|
||||
@@ -65,15 +63,18 @@ class BinaryFileResponse extends Response
|
||||
*/
|
||||
public function setFile(\SplFileInfo|string $file, ?string $contentDisposition = null, bool $autoEtag = false, bool $autoLastModified = true): static
|
||||
{
|
||||
$isTemporaryFile = $file instanceof \SplTempFileObject;
|
||||
$this->tempFileObject = $isTemporaryFile ? $file : null;
|
||||
|
||||
if (!$file instanceof File) {
|
||||
if ($file instanceof \SplFileInfo) {
|
||||
$file = new File($file->getPathname());
|
||||
$file = new File($file->getPathname(), !$isTemporaryFile);
|
||||
} else {
|
||||
$file = new File((string) $file);
|
||||
$file = new File($file);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$file->isReadable()) {
|
||||
if (!$file->isReadable() && !$isTemporaryFile) {
|
||||
throw new FileException('File must be readable.');
|
||||
}
|
||||
|
||||
@@ -83,7 +84,7 @@ class BinaryFileResponse extends Response
|
||||
$this->setAutoEtag();
|
||||
}
|
||||
|
||||
if ($autoLastModified) {
|
||||
if ($autoLastModified && !$isTemporaryFile) {
|
||||
$this->setAutoLastModified();
|
||||
}
|
||||
|
||||
@@ -109,8 +110,8 @@ class BinaryFileResponse extends Response
|
||||
*/
|
||||
public function setChunkSize(int $chunkSize): static
|
||||
{
|
||||
if ($chunkSize < 1 || $chunkSize > \PHP_INT_MAX) {
|
||||
throw new \LogicException('The chunk size of a BinaryFileResponse cannot be less than 1 or greater than PHP_INT_MAX.');
|
||||
if ($chunkSize < 1) {
|
||||
throw new \InvalidArgumentException('The chunk size of a BinaryFileResponse cannot be less than 1.');
|
||||
}
|
||||
|
||||
$this->chunkSize = $chunkSize;
|
||||
@@ -125,7 +126,7 @@ class BinaryFileResponse extends Response
|
||||
*/
|
||||
public function setAutoLastModified(): static
|
||||
{
|
||||
$this->setLastModified(\DateTimeImmutable::createFromFormat('U', $this->file->getMTime()));
|
||||
$this->setLastModified(\DateTimeImmutable::createFromFormat('U', $this->tempFileObject ? time() : $this->file->getMTime()));
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -137,7 +138,7 @@ class BinaryFileResponse extends Response
|
||||
*/
|
||||
public function setAutoEtag(): static
|
||||
{
|
||||
$this->setEtag(base64_encode(hash_file('sha256', $this->file->getPathname(), true)));
|
||||
$this->setEtag(base64_encode(hash_file('xxh128', $this->file->getPathname(), true)));
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -188,7 +189,12 @@ class BinaryFileResponse extends Response
|
||||
}
|
||||
|
||||
if (!$this->headers->has('Content-Type')) {
|
||||
$this->headers->set('Content-Type', $this->file->getMimeType() ?: 'application/octet-stream');
|
||||
$mimeType = null;
|
||||
if (!$this->tempFileObject) {
|
||||
$mimeType = $this->file->getMimeType();
|
||||
}
|
||||
|
||||
$this->headers->set('Content-Type', $mimeType ?: 'application/octet-stream');
|
||||
}
|
||||
|
||||
parent::prepare($request);
|
||||
@@ -196,7 +202,9 @@ class BinaryFileResponse extends Response
|
||||
$this->offset = 0;
|
||||
$this->maxlen = -1;
|
||||
|
||||
if (false === $fileSize = $this->file->getSize()) {
|
||||
if ($this->tempFileObject) {
|
||||
$fileSize = $this->tempFileObject->fstat()['size'];
|
||||
} elseif (false === $fileSize = $this->file->getSize()) {
|
||||
return $this;
|
||||
}
|
||||
$this->headers->remove('Transfer-Encoding');
|
||||
@@ -217,15 +225,19 @@ class BinaryFileResponse extends Response
|
||||
}
|
||||
if ('x-accel-redirect' === strtolower($type)) {
|
||||
// Do X-Accel-Mapping substitutions.
|
||||
// @link https://www.nginx.com/resources/wiki/start/topics/examples/x-accel/#x-accel-redirect
|
||||
$parts = HeaderUtils::split($request->headers->get('X-Accel-Mapping', ''), ',=');
|
||||
// @link https://github.com/rack/rack/blob/main/lib/rack/sendfile.rb
|
||||
// @link https://mattbrictson.com/blog/accelerated-rails-downloads
|
||||
if (!$request->headers->has('X-Accel-Mapping')) {
|
||||
throw new \LogicException('The "X-Accel-Mapping" header must be set when "X-Sendfile-Type" is set to "X-Accel-Redirect".');
|
||||
}
|
||||
$parts = HeaderUtils::split($request->headers->get('X-Accel-Mapping'), ',=');
|
||||
foreach ($parts as $part) {
|
||||
[$pathPrefix, $location] = $part;
|
||||
if (str_starts_with($path, $pathPrefix)) {
|
||||
$path = $location.substr($path, \strlen($pathPrefix));
|
||||
// Only set X-Accel-Redirect header if a valid URI can be produced
|
||||
// as nginx does not serve arbitrary file paths.
|
||||
$this->headers->set($type, $path);
|
||||
$this->headers->set($type, rawurlencode($path));
|
||||
$this->maxlen = 0;
|
||||
break;
|
||||
}
|
||||
@@ -255,13 +267,13 @@ class BinaryFileResponse extends Response
|
||||
$end = min($end, $fileSize - 1);
|
||||
if ($start < 0 || $start > $end) {
|
||||
$this->setStatusCode(416);
|
||||
$this->headers->set('Content-Range', sprintf('bytes */%s', $fileSize));
|
||||
$this->headers->set('Content-Range', \sprintf('bytes */%s', $fileSize));
|
||||
} elseif ($end - $start < $fileSize - 1) {
|
||||
$this->maxlen = $end < $fileSize ? $end - $start + 1 : -1;
|
||||
$this->offset = $start;
|
||||
|
||||
$this->setStatusCode(206);
|
||||
$this->headers->set('Content-Range', sprintf('bytes %s-%s/%s', $start, $end, $fileSize));
|
||||
$this->headers->set('Content-Range', \sprintf('bytes %s-%s/%s', $start, $end, $fileSize));
|
||||
$this->headers->set('Content-Length', $end - $start + 1);
|
||||
}
|
||||
}
|
||||
@@ -301,19 +313,25 @@ class BinaryFileResponse extends Response
|
||||
}
|
||||
|
||||
$out = fopen('php://output', 'w');
|
||||
$file = fopen($this->file->getPathname(), 'r');
|
||||
|
||||
if ($this->tempFileObject) {
|
||||
$file = $this->tempFileObject;
|
||||
$file->rewind();
|
||||
} else {
|
||||
$file = new \SplFileObject($this->file->getPathname(), 'r');
|
||||
}
|
||||
|
||||
ignore_user_abort(true);
|
||||
|
||||
if (0 !== $this->offset) {
|
||||
fseek($file, $this->offset);
|
||||
$file->fseek($this->offset);
|
||||
}
|
||||
|
||||
$length = $this->maxlen;
|
||||
while ($length && !feof($file)) {
|
||||
while ($length && !$file->eof()) {
|
||||
$read = $length > $this->chunkSize || 0 > $length ? $this->chunkSize : $length;
|
||||
|
||||
if (false === $data = fread($file, $read)) {
|
||||
if (false === $data = $file->fread($read)) {
|
||||
break;
|
||||
}
|
||||
while ('' !== $data) {
|
||||
@@ -329,9 +347,8 @@ class BinaryFileResponse extends Response
|
||||
}
|
||||
|
||||
fclose($out);
|
||||
fclose($file);
|
||||
} finally {
|
||||
if ($this->deleteFileAfterSend && is_file($this->file->getPathname())) {
|
||||
if (null === $this->tempFileObject && $this->deleteFileAfterSend && is_file($this->file->getPathname())) {
|
||||
unlink($this->file->getPathname());
|
||||
}
|
||||
}
|
||||
@@ -358,10 +375,8 @@ class BinaryFileResponse extends Response
|
||||
|
||||
/**
|
||||
* Trust X-Sendfile-Type header.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function trustXSendfileTypeHeader()
|
||||
public static function trustXSendfileTypeHeader(): void
|
||||
{
|
||||
self::$trustXSendfileTypeHeader = true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user