Skip to content
This repository has been archived by the owner on Jan 17, 2022. It is now read-only.

Commit

Permalink
feat: Add meaningful exceptions (#46)
Browse files Browse the repository at this point in the history
  • Loading branch information
fjogeleit authored and k911 committed Apr 13, 2019
1 parent 0292a43 commit 4e2cc6d
Show file tree
Hide file tree
Showing 15 changed files with 295 additions and 40 deletions.
12 changes: 9 additions & 3 deletions src/Bridge/Symfony/Bundle/Command/ServerStartCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@

namespace K911\Swoole\Bridge\Symfony\Bundle\Command;

use K911\Swoole\Bridge\Symfony\Bundle\Exception\CouldNotCreatePidFileException;
use K911\Swoole\Bridge\Symfony\Bundle\Exception\PidFileNotAccessibleException;
use function K911\Swoole\get_object_property;
use K911\Swoole\Server\HttpServer;
use K911\Swoole\Server\HttpServerConfiguration;
use RuntimeException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\ConsoleOutput;
Expand Down Expand Up @@ -46,8 +47,13 @@ protected function prepareServerConfiguration(HttpServerConfiguration $serverCon
protected function startServer(HttpServerConfiguration $serverConfiguration, HttpServer $server, SymfonyStyle $io): void
{
$pidFile = $serverConfiguration->getPidFile();
if (!\touch($pidFile) || !\is_writable($pidFile)) {
throw new RuntimeException(\sprintf('Could not access or create pid file "%s".', $serverConfiguration->getPidFile()));

if (!\touch($pidFile)) {
throw PidFileNotAccessibleException::forFile($pidFile);
}

if (!\is_writable($pidFile)) {
throw CouldNotCreatePidFileException::forPath($pidFile);
}

$this->closeSymfonyStyle($io);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace K911\Swoole\Bridge\Symfony\Bundle\Exception;

/**
* @internal
*/
final class CouldNotCreatePidFileException extends \RuntimeException
{
public static function forPath(string $pidFile): self
{
throw new self(\sprintf('Could not create pid file "%s".', $pidFile));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace K911\Swoole\Bridge\Symfony\Bundle\Exception;

/**
* @internal
*/
final class PidFileNotAccessibleException extends \RuntimeException
{
public static function forFile(string $pidFile): self
{
throw new self(\sprintf('Could not access pid file "%s".', $pidFile));
}
}
31 changes: 31 additions & 0 deletions src/Client/Exception/ClientConnectionErrorException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace K911\Swoole\Client\Exception;

/**
* @internal
*/
final class ClientConnectionErrorException extends \RuntimeException
{
public static function failed(int $errorCode): self
{
return new self('Connection Failed', $errorCode);
}

public static function requestTimeout(int $errorCode): self
{
return new self('Request Timeout', $errorCode);
}

public static function serverReset(int $errorCode): self
{
return new self('Server Reset', $errorCode);
}

public static function unknown(int $errorCode): self
{
return new self('Unknown', $errorCode);
}
}
23 changes: 23 additions & 0 deletions src/Client/Exception/InvalidContentTypeException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace K911\Swoole\Client\Exception;

use K911\Swoole\Client\Http;

/**
* @internal
*/
final class InvalidContentTypeException extends \InvalidArgumentException
{
private const SUPPORTED_CONTENT_TYPES = [
Http::CONTENT_TYPE_APPLICATION_JSON,
Http::CONTENT_TYPE_TEXT_PLAIN,
];

public static function forContentType(string $contentType): self
{
return new self(\sprintf('Content-Type "%s" is not supported. Only "%s" are supported.', $contentType, \implode(', ', self::SUPPORTED_CONTENT_TYPES)));
}
}
16 changes: 16 additions & 0 deletions src/Client/Exception/InvalidHttpMethodException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace K911\Swoole\Client\Exception;

/**
* @internal
*/
final class InvalidHttpMethodException extends \InvalidArgumentException
{
public static function forMethod(string $method, array $allowed): self
{
return new self(\sprintf('Content-Type "%s" is not supported. Only "%s" are supported.', $method, \implode(', ', $allowed)));
}
}
16 changes: 16 additions & 0 deletions src/Client/Exception/MissingContentTypeException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace K911\Swoole\Client\Exception;

/**
* @internal
*/
final class MissingContentTypeException extends \InvalidArgumentException
{
public static function create(): self
{
return new self(\sprintf('Server response did not contain Content-Type.'));
}
}
70 changes: 44 additions & 26 deletions src/Client/HttpClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@

namespace K911\Swoole\Client;

use Assert\Assertion;
use K911\Swoole\Client\Exception\ClientConnectionErrorException;
use K911\Swoole\Client\Exception\InvalidContentTypeException;
use K911\Swoole\Client\Exception\InvalidHttpMethodException;
use K911\Swoole\Client\Exception\MissingContentTypeException;
use Swoole\Coroutine;
use Swoole\Coroutine\Http\Client;

Expand All @@ -25,10 +28,6 @@ final class HttpClient
Http::METHOD_OPTIONS,
];

private const SUPPORTED_CONTENT_TYPES = [
Http::CONTENT_TYPE_APPLICATION_JSON,
Http::CONTENT_TYPE_TEXT_PLAIN,
];
private const ACCEPTABLE_CONNECTING_EXIT_CODES = [
111 => true,
61 => true,
Expand Down Expand Up @@ -72,7 +71,7 @@ public function connect(int $timeout = 3, float $step = 0.1): bool

public function send(string $path, string $method = Http::METHOD_GET, array $headers = [], $data = null, int $timeout = 3): array
{
Assertion::inArray($method, self::SUPPORTED_HTTP_METHODS, 'Method "%s" is not supported. Supported ones are: %s.');
$this->validHttpMethod($method);

$this->client->setMethod($method);
$this->client->setHeaders($headers);
Expand Down Expand Up @@ -134,7 +133,7 @@ private function resolveResponseBody(Client $client)
return [];
}

Assertion::keyExists($client->headers, Http::HEADER_CONTENT_TYPE, 'Server response did not contain Content-Type.');
$this->hasContentType($client);
$fullContentType = $client->headers[Http::HEADER_CONTENT_TYPE];
$contentType = \explode(';', $fullContentType)[0];

Expand All @@ -154,32 +153,15 @@ private function resolveResponseBody(Client $client)
case Http::CONTENT_TYPE_TEXT_PLAIN:
return $client->body;
default:
throw new \RuntimeException(\sprintf('Content-Type "%s" is not supported. Only "%s" are supported.', $contentType, \implode(', ', self::SUPPORTED_CONTENT_TYPES)));
throw InvalidContentTypeException::forContentType($contentType);
}
}

private function resolveResponse(Client $client, int $timeout): array
{
$client->recv($timeout);

if ($client->statusCode < 0) {
switch ($client->statusCode) {
case -1:
$error = 'Connection Failed';
break;
case -2:
$error = 'Request Timeout';
break;
case -3:
$error = 'Server Reset';
break;
default:
$error = 'Unknown';
break;
}

throw new \RuntimeException($error, $client->errCode);
}
$this->validStatusCode($client);

return [
'request' => [
Expand All @@ -199,4 +181,40 @@ private function resolveResponse(Client $client, int $timeout): array
],
];
}

private function validStatusCode(Client $client): void
{
if ($client->statusCode >= 0) {
return;
}

switch ($client->statusCode) {
case -1:
throw ClientConnectionErrorException::failed($client->errCode);
case -2:
throw ClientConnectionErrorException::requestTimeout($client->errCode);
case -3:
throw ClientConnectionErrorException::serverReset($client->errCode);
default:
throw ClientConnectionErrorException::unknown($client->errCode);
}
}

private function validHttpMethod(string $method): void
{
if (true === \in_array($method, self::SUPPORTED_HTTP_METHODS)) {
return;
}

throw InvalidHttpMethodException::forMethod($method, self::SUPPORTED_HTTP_METHODS);
}

private function hasContentType(Client $client): void
{
if (true === \array_key_exists(Http::HEADER_CONTENT_TYPE, $client->headers)) {
return;
}

throw MissingContentTypeException::create();
}
}
16 changes: 16 additions & 0 deletions src/Server/Exception/AlreadyAttachedException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace K911\Swoole\Server\Exception;

/**
* @internal
*/
final class AlreadyAttachedException extends \RuntimeException
{
public static function create(): self
{
return new self('Swoole HTTP Server has been already attached. Cannot attach server or listeners multiple times.');
}
}
16 changes: 16 additions & 0 deletions src/Server/Exception/InvalidServerPortException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace K911\Swoole\Server\Exception;

/**
* @internal
*/
final class InvalidServerPortException extends \InvalidArgumentException
{
public static function with(int $port, int $expectedPort): self
{
return new self(\sprintf('Attached Swoole HTTP Server has different port (%s), than expected (%s).', $port, $expectedPort));
}
}
16 changes: 16 additions & 0 deletions src/Server/Exception/NoServerAttachedException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace K911\Swoole\Server\Exception;

/**
* @internal
*/
final class NoServerAttachedException extends \RuntimeException
{
public static function create(): self
{
return new self('Swoole HTTP Server has not been setup yet. Please use attach method.');
}
}
16 changes: 16 additions & 0 deletions src/Server/Exception/NotRunningException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace K911\Swoole\Server\Exception;

/**
* @internal
*/
final class NotRunningException extends \RuntimeException
{
public static function create(): self
{
return new self('Swoole HTTP Server has not been running.');
}
}
16 changes: 16 additions & 0 deletions src/Server/Exception/PortUnavailableException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace K911\Swoole\Server\Exception;

/**
* @internal
*/
final class PortUnavailableException extends \InvalidArgumentException
{
public static function fortPort(int $port): self
{
return new self(\sprintf('Cannot attach listener on port (%s). It is already registered.', $port));
}
}

0 comments on commit 4e2cc6d

Please sign in to comment.