From 78c76d6df435e2f9a435b6efe10eee3d9c5d7b70 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Tue, 14 Jul 2020 21:16:38 -0700 Subject: [PATCH] Ensure errors from daemon are wrapped correctly --- .../Api/Client/Servers/CommandController.php | 13 +- .../Api/Client/Servers/FileController.php | 18 ++ .../Api/Client/Servers/PowerController.php | 2 + .../Wings/DaemonCommandRepository.php | 20 +- .../Wings/DaemonFileRepository.php | 203 ++++++++++++------ .../Wings/DaemonPowerRepository.php | 16 +- 6 files changed, 191 insertions(+), 81 deletions(-) diff --git a/app/Http/Controllers/Api/Client/Servers/CommandController.php b/app/Http/Controllers/Api/Client/Servers/CommandController.php index d4551aa2f..b8390673e 100644 --- a/app/Http/Controllers/Api/Client/Servers/CommandController.php +++ b/app/Http/Controllers/Api/Client/Servers/CommandController.php @@ -5,7 +5,6 @@ namespace Pterodactyl\Http\Controllers\Api\Client\Servers; use Illuminate\Http\Response; use Pterodactyl\Models\Server; use Psr\Http\Message\ResponseInterface; -use GuzzleHttp\Exception\RequestException; use GuzzleHttp\Exception\BadResponseException; use Symfony\Component\HttpKernel\Exception\HttpException; use Pterodactyl\Repositories\Wings\DaemonCommandRepository; @@ -45,11 +44,13 @@ class CommandController extends ClientApiController { try { $this->repository->setServer($server)->send($request->input('command')); - } catch (RequestException $exception) { - if ($exception instanceof BadResponseException) { + } catch (DaemonConnectionException $exception) { + $previous = $exception->getPrevious(); + + if ($previous instanceof BadResponseException) { if ( - $exception->getResponse() instanceof ResponseInterface - && $exception->getResponse()->getStatusCode() === Response::HTTP_BAD_GATEWAY + $previous->getResponse() instanceof ResponseInterface + && $previous->getResponse()->getStatusCode() === Response::HTTP_BAD_GATEWAY ) { throw new HttpException( Response::HTTP_BAD_GATEWAY, 'Server must be online in order to send commands.', $exception @@ -57,7 +58,7 @@ class CommandController extends ClientApiController } } - throw new DaemonConnectionException($exception); + throw $exception; } return $this->returnNoContent(); diff --git a/app/Http/Controllers/Api/Client/Servers/FileController.php b/app/Http/Controllers/Api/Client/Servers/FileController.php index 60fc88777..c4e4a035f 100644 --- a/app/Http/Controllers/Api/Client/Servers/FileController.php +++ b/app/Http/Controllers/Api/Client/Servers/FileController.php @@ -19,6 +19,7 @@ use Pterodactyl\Http\Requests\Api\Client\Servers\Files\DeleteFileRequest; use Pterodactyl\Http\Requests\Api\Client\Servers\Files\RenameFileRequest; use Pterodactyl\Http\Requests\Api\Client\Servers\Files\CreateFolderRequest; use Pterodactyl\Http\Requests\Api\Client\Servers\Files\CompressFilesRequest; +use Pterodactyl\Http\Requests\Api\Client\Servers\Files\DecompressFilesRequest; use Pterodactyl\Http\Requests\Api\Client\Servers\Files\GetFileContentsRequest; use Pterodactyl\Http\Requests\Api\Client\Servers\Files\WriteFileContentRequest; @@ -88,7 +89,9 @@ class FileController extends ClientApiController * @param \Pterodactyl\Http\Requests\Api\Client\Servers\Files\GetFileContentsRequest $request * @param \Pterodactyl\Models\Server $server * @return \Illuminate\Http\Response + * * @throws \Pterodactyl\Exceptions\Http\Server\FileSizeTooLargeException + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException */ public function contents(GetFileContentsRequest $request, Server $server): Response { @@ -139,6 +142,8 @@ class FileController extends ClientApiController * @param \Pterodactyl\Http\Requests\Api\Client\Servers\Files\WriteFileContentRequest $request * @param \Pterodactyl\Models\Server $server * @return \Illuminate\Http\JsonResponse + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException */ public function write(WriteFileContentRequest $request, Server $server): JsonResponse { @@ -156,6 +161,8 @@ class FileController extends ClientApiController * @param \Pterodactyl\Http\Requests\Api\Client\Servers\Files\CreateFolderRequest $request * @param \Pterodactyl\Models\Server $server * @return \Illuminate\Http\JsonResponse + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException */ public function create(CreateFolderRequest $request, Server $server): JsonResponse { @@ -172,6 +179,8 @@ class FileController extends ClientApiController * @param \Pterodactyl\Http\Requests\Api\Client\Servers\Files\RenameFileRequest $request * @param \Pterodactyl\Models\Server $server * @return \Illuminate\Http\JsonResponse + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException */ public function rename(RenameFileRequest $request, Server $server): JsonResponse { @@ -188,6 +197,8 @@ class FileController extends ClientApiController * @param \Pterodactyl\Http\Requests\Api\Client\Servers\Files\CopyFileRequest $request * @param \Pterodactyl\Models\Server $server * @return \Illuminate\Http\JsonResponse + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException */ public function copy(CopyFileRequest $request, Server $server): JsonResponse { @@ -202,9 +213,14 @@ class FileController extends ClientApiController * @param \Pterodactyl\Http\Requests\Api\Client\Servers\Files\CompressFilesRequest $request * @param \Pterodactyl\Models\Server $server * @return array + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException */ public function compress(CompressFilesRequest $request, Server $server): array { + // Allow up to five minutes for this request to process before timing out. + set_time_limit(300); + $file = $this->fileRepository->setServer($server) ->compressFiles( $request->input('root'), $request->input('files') @@ -221,6 +237,8 @@ class FileController extends ClientApiController * @param \Pterodactyl\Http\Requests\Api\Client\Servers\Files\DeleteFileRequest $request * @param \Pterodactyl\Models\Server $server * @return \Illuminate\Http\JsonResponse + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException */ public function delete(DeleteFileRequest $request, Server $server): JsonResponse { diff --git a/app/Http/Controllers/Api/Client/Servers/PowerController.php b/app/Http/Controllers/Api/Client/Servers/PowerController.php index 82cf8b334..12e2d75bb 100644 --- a/app/Http/Controllers/Api/Client/Servers/PowerController.php +++ b/app/Http/Controllers/Api/Client/Servers/PowerController.php @@ -33,6 +33,8 @@ class PowerController extends ClientApiController * @param \Pterodactyl\Http\Requests\Api\Client\Servers\SendPowerRequest $request * @param \Pterodactyl\Models\Server $server * @return \Illuminate\Http\Response + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException */ public function index(SendPowerRequest $request, Server $server): Response { diff --git a/app/Repositories/Wings/DaemonCommandRepository.php b/app/Repositories/Wings/DaemonCommandRepository.php index 644bb024c..38f2fb475 100644 --- a/app/Repositories/Wings/DaemonCommandRepository.php +++ b/app/Repositories/Wings/DaemonCommandRepository.php @@ -5,6 +5,8 @@ namespace Pterodactyl\Repositories\Wings; use Webmozart\Assert\Assert; use Pterodactyl\Models\Server; use Psr\Http\Message\ResponseInterface; +use GuzzleHttp\Exception\TransferException; +use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; class DaemonCommandRepository extends DaemonRepository { @@ -13,16 +15,22 @@ class DaemonCommandRepository extends DaemonRepository * * @param string|string[] $command * @return \Psr\Http\Message\ResponseInterface + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException */ public function send($command): ResponseInterface { Assert::isInstanceOf($this->server, Server::class); - return $this->getHttpClient()->post( - sprintf('/api/servers/%s/commands', $this->server->uuid), - [ - 'json' => ['commands' => is_array($command) ? $command : [$command]], - ] - ); + try { + return $this->getHttpClient()->post( + sprintf('/api/servers/%s/commands', $this->server->uuid), + [ + 'json' => ['commands' => is_array($command) ? $command : [$command]], + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } } } diff --git a/app/Repositories/Wings/DaemonFileRepository.php b/app/Repositories/Wings/DaemonFileRepository.php index 7a0934b10..3a605ea94 100644 --- a/app/Repositories/Wings/DaemonFileRepository.php +++ b/app/Repositories/Wings/DaemonFileRepository.php @@ -5,7 +5,9 @@ namespace Pterodactyl\Repositories\Wings; use Webmozart\Assert\Assert; use Pterodactyl\Models\Server; use Psr\Http\Message\ResponseInterface; +use GuzzleHttp\Exception\TransferException; use Pterodactyl\Exceptions\Http\Server\FileSizeTooLargeException; +use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; class DaemonFileRepository extends DaemonRepository { @@ -18,17 +20,22 @@ class DaemonFileRepository extends DaemonRepository * * @throws \GuzzleHttp\Exception\TransferException * @throws \Pterodactyl\Exceptions\Http\Server\FileSizeTooLargeException + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException */ public function getContent(string $path, int $notLargerThan = null): string { Assert::isInstanceOf($this->server, Server::class); - $response = $this->getHttpClient()->get( - sprintf('/api/servers/%s/files/contents', $this->server->uuid), - [ - 'query' => ['file' => $path], - ] - ); + try { + $response = $this->getHttpClient()->get( + sprintf('/api/servers/%s/files/contents', $this->server->uuid), + [ + 'query' => ['file' => $path], + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } $length = (int) $response->getHeader('Content-Length')[0] ?? 0; @@ -47,19 +54,23 @@ class DaemonFileRepository extends DaemonRepository * @param string $content * @return \Psr\Http\Message\ResponseInterface * - * @throws \GuzzleHttp\Exception\TransferException + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException */ public function putContent(string $path, string $content): ResponseInterface { Assert::isInstanceOf($this->server, Server::class); - return $this->getHttpClient()->post( - sprintf('/api/servers/%s/files/write', $this->server->uuid), - [ - 'query' => ['file' => $path], - 'body' => $content, - ] - ); + try { + return $this->getHttpClient()->post( + sprintf('/api/servers/%s/files/write', $this->server->uuid), + [ + 'query' => ['file' => $path], + 'body' => $content, + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } } /** @@ -68,18 +79,22 @@ class DaemonFileRepository extends DaemonRepository * @param string $path * @return array * - * @throws \GuzzleHttp\Exception\TransferException + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException */ public function getDirectory(string $path): array { Assert::isInstanceOf($this->server, Server::class); - $response = $this->getHttpClient()->get( - sprintf('/api/servers/%s/files/list-directory', $this->server->uuid), - [ - 'query' => ['directory' => $path], - ] - ); + try { + $response = $this->getHttpClient()->get( + sprintf('/api/servers/%s/files/list-directory', $this->server->uuid), + [ + 'query' => ['directory' => $path], + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } return json_decode($response->getBody(), true); } @@ -90,20 +105,26 @@ class DaemonFileRepository extends DaemonRepository * @param string $name * @param string $path * @return \Psr\Http\Message\ResponseInterface + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException */ public function createDirectory(string $name, string $path): ResponseInterface { Assert::isInstanceOf($this->server, Server::class); - return $this->getHttpClient()->post( - sprintf('/api/servers/%s/files/create-directory', $this->server->uuid), - [ - 'json' => [ - 'name' => urldecode($name), - 'path' => urldecode($path), - ], - ] - ); + try { + return $this->getHttpClient()->post( + sprintf('/api/servers/%s/files/create-directory', $this->server->uuid), + [ + 'json' => [ + 'name' => urldecode($name), + 'path' => urldecode($path), + ], + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } } /** @@ -112,20 +133,26 @@ class DaemonFileRepository extends DaemonRepository * @param string|null $root * @param array $files * @return \Psr\Http\Message\ResponseInterface + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException */ public function renameFiles(?string $root, array $files): ResponseInterface { Assert::isInstanceOf($this->server, Server::class); - return $this->getHttpClient()->put( - sprintf('/api/servers/%s/files/rename', $this->server->uuid), - [ - 'json' => [ - 'root' => $root ?? '/', - 'files' => $files, - ], - ] - ); + try { + return $this->getHttpClient()->put( + sprintf('/api/servers/%s/files/rename', $this->server->uuid), + [ + 'json' => [ + 'root' => $root ?? '/', + 'files' => $files, + ], + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } } /** @@ -133,19 +160,25 @@ class DaemonFileRepository extends DaemonRepository * * @param string $location * @return \Psr\Http\Message\ResponseInterface + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException */ public function copyFile(string $location): ResponseInterface { Assert::isInstanceOf($this->server, Server::class); - return $this->getHttpClient()->post( - sprintf('/api/servers/%s/files/copy', $this->server->uuid), - [ - 'json' => [ - 'location' => urldecode($location), - ], - ] - ); + try { + return $this->getHttpClient()->post( + sprintf('/api/servers/%s/files/copy', $this->server->uuid), + [ + 'json' => [ + 'location' => urldecode($location), + ], + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } } /** @@ -154,20 +187,26 @@ class DaemonFileRepository extends DaemonRepository * @param string|null $root * @param array $files * @return \Psr\Http\Message\ResponseInterface + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException */ public function deleteFiles(?string $root, array $files): ResponseInterface { Assert::isInstanceOf($this->server, Server::class); - return $this->getHttpClient()->post( - sprintf('/api/servers/%s/files/delete', $this->server->uuid), - [ - 'json' => [ - 'root' => $root ?? '/', - 'files' => $files, - ], - ] - ); + try { + return $this->getHttpClient()->post( + sprintf('/api/servers/%s/files/delete', $this->server->uuid), + [ + 'json' => [ + 'root' => $root ?? '/', + 'files' => $files, + ], + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } } /** @@ -176,21 +215,55 @@ class DaemonFileRepository extends DaemonRepository * @param string|null $root * @param array $files * @return array + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException */ public function compressFiles(?string $root, array $files): array { Assert::isInstanceOf($this->server, Server::class); - $response = $this->getHttpClient()->post( - sprintf('/api/servers/%s/files/compress', $this->server->uuid), - [ - 'json' => [ - 'root' => $root ?? '/', - 'files' => $files, - ], - ] - ); + try { + $response = $this->getHttpClient()->post( + sprintf('/api/servers/%s/files/compress', $this->server->uuid), + [ + 'json' => [ + 'root' => $root ?? '/', + 'files' => $files, + ], + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } return json_decode($response->getBody(), true); } + + /** + * Decompresses a given archive file. + * + * @param string|null $root + * @param string $file + * @return \Psr\Http\Message\ResponseInterface + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function decompressFile(?string $root, string $file): ResponseInterface + { + Assert::isInstanceOf($this->server, Server::class); + + try { + return $this->getHttpClient()->post( + sprintf('/api/servers/%s/files/decompress', $this->server->uuid), + [ + 'json ' => [ + 'root' => $root ?? '/', + 'file' => $file, + ], + ] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } } diff --git a/app/Repositories/Wings/DaemonPowerRepository.php b/app/Repositories/Wings/DaemonPowerRepository.php index d7ef42c4f..ccbf169ff 100644 --- a/app/Repositories/Wings/DaemonPowerRepository.php +++ b/app/Repositories/Wings/DaemonPowerRepository.php @@ -5,6 +5,8 @@ namespace Pterodactyl\Repositories\Wings; use Webmozart\Assert\Assert; use Pterodactyl\Models\Server; use Psr\Http\Message\ResponseInterface; +use GuzzleHttp\Exception\TransferException; +use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; class DaemonPowerRepository extends DaemonRepository { @@ -13,14 +15,20 @@ class DaemonPowerRepository extends DaemonRepository * * @param string $action * @return \Psr\Http\Message\ResponseInterface + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException */ public function send(string $action): ResponseInterface { Assert::isInstanceOf($this->server, Server::class); - return $this->getHttpClient()->post( - sprintf('/api/servers/%s/power', $this->server->uuid), - ['json' => ['action' => $action]] - ); + try { + return $this->getHttpClient()->post( + sprintf('/api/servers/%s/power', $this->server->uuid), + ['json' => ['action' => $action]] + ); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } } }