Add fileserver support

This commit is contained in:
Marcel Pociot
2020-11-01 17:43:42 +01:00
parent 880259657f
commit ce945e1326
9 changed files with 466 additions and 7 deletions

View File

@@ -0,0 +1,137 @@
<?php
namespace App\Client\Fileserver;
use App\Http\Controllers\Concerns\LoadsViews;
use App\Http\QueryParameters;
use GuzzleHttp\Psr7\ServerRequest;
use Illuminate\Http\Request;
use Psr\Http\Message\ServerRequestInterface;
use Ratchet\ConnectionInterface;
use React\EventLoop\LoopInterface;
use React\Http\Response;
use React\Stream\ReadableResourceStream;
use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\Glob;
use Symfony\Component\Finder\Iterator\FilenameFilterIterator;
use Symfony\Component\Finder\SplFileInfo;
class ConnectionHandler
{
use LoadsViews;
/** @var string */
protected $rootFolder;
/** @var string */
protected $name;
/** @var LoopInterface */
protected $loop;
public function __construct(string $rootFolder, string $name, LoopInterface $loop)
{
$this->rootFolder = $rootFolder;
$this->name = $name;
$this->loop = $loop;
}
public function handle(ServerRequestInterface $request)
{
$request = $this->createLaravelRequest($request);
$targetPath = realpath($this->rootFolder . DIRECTORY_SEPARATOR . $request->path());
if (! $this->isValidTarget($targetPath)) {
return new Response(404);
}
if (is_dir($targetPath)) {
// Directory listing
$directoryContent = Finder::create()
->depth(0)
->sort(function ($a, $b) {
return strcmp(strtolower($a->getRealpath()), strtolower($b->getRealpath()));
})
->in($targetPath);
if ($this->name !== '') {
$directoryContent->name($this->name);
}
$parentPath = explode('/', $request->path());
array_pop($parentPath);
$parentPath = implode('/', $parentPath);
return new Response(
200,
['Content-Type' => 'text/html'],
$this->getView(null, 'client.fileserver', [
'currentPath' => $request->path(),
'parentPath' => $parentPath,
'directory' => $targetPath,
'directoryContent' => $directoryContent
])
);
}
if (is_file($targetPath)) {
return new Response(
200,
['Content-Type' => mime_content_type($targetPath)],
new ReadableResourceStream(fopen($targetPath, 'r'), $this->loop)
);
}
}
protected function isValidTarget(string $targetPath): bool
{
if (! file_exists($targetPath)) {
return false;
}
if ($this->name !== '') {
$filter = new class(basename($targetPath), [$this->name]) extends FilenameFilterIterator {
protected $filename;
public function __construct(string $filename, array $matchPatterns)
{
$this->filename = $filename;
foreach ($matchPatterns as $pattern) {
$this->matchRegexps[] = $this->toRegex($pattern);
}
}
public function accept()
{
return $this->isAccepted($this->filename);
}
};
return $filter->accept();
}
return true;
}
protected function createLaravelRequest(ServerRequestInterface $request): Request
{
try {
parse_str($request->getBody(), $bodyParameters);
} catch (\Throwable $e) {
$bodyParameters = [];
}
$serverRequest = (new ServerRequest(
$request->getMethod(),
$request->getUri(),
$request->getHeaders(),
$request->getBody(),
$request->getProtocolVersion(),
))
->withQueryParams(QueryParameters::create($request)->all())
->withParsedBody($bodyParameters);
return Request::createFromBase((new HttpFoundationFactory)->createRequest($serverRequest));
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Client\Fileserver;
use Psr\Http\Message\ServerRequestInterface;
use React\EventLoop\LoopInterface;
use React\Http\Response;
use React\Http\Server;
use React\Socket\Server as SocketServer;
class Fileserver
{
/** @var SocketServer */
protected $socket;
public function __construct($rootFolder, $name, $port, $address, LoopInterface $loop)
{
$server = new Server(function (ServerRequestInterface $request) use ($rootFolder, $name, $loop) {
return (new ConnectionHandler($rootFolder, $name, $loop))->handle($request);
});
$this->socket = new SocketServer("{$address}:{$port}", $loop);
$server->listen($this->socket);
}
public function getSocket(): SocketServer
{
return $this->socket;
}
}