From 6fbec7134c8afeb257632c84e093bd325bf3a134 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 9 Feb 2017 13:38:43 +0100 Subject: [PATCH 1/2] Handle invalid Icinga 2 API response types refs #2728 --- .../Command/Transport/ApiCommandTransport.php | 19 +++++-- .../Exception/JsonDecodeException.php | 55 +++++++++++++++++++ .../Monitoring/Web/Rest/RestRequest.php | 26 +-------- 3 files changed, 70 insertions(+), 30 deletions(-) create mode 100644 modules/monitoring/library/Monitoring/Exception/JsonDecodeException.php diff --git a/modules/monitoring/library/Monitoring/Command/Transport/ApiCommandTransport.php b/modules/monitoring/library/Monitoring/Command/Transport/ApiCommandTransport.php index 8b00bd1b6..c3d4cd4c1 100644 --- a/modules/monitoring/library/Monitoring/Command/Transport/ApiCommandTransport.php +++ b/modules/monitoring/library/Monitoring/Command/Transport/ApiCommandTransport.php @@ -8,6 +8,7 @@ use Icinga\Module\Monitoring\Command\IcingaApiCommand; use Icinga\Module\Monitoring\Command\IcingaCommand; use Icinga\Module\Monitoring\Command\Renderer\IcingaApiCommandRenderer; use Icinga\Module\Monitoring\Exception\CommandTransportException; +use Icinga\Module\Monitoring\Exception\JsonDecodeException; use Icinga\Module\Monitoring\Web\Rest\RestRequest; /** @@ -193,12 +194,18 @@ class ApiCommandTransport implements CommandTransportInterface $this->getHost(), $this->getPort() ); - $response = RestRequest::post($this->getUriFor($command->getEndpoint())) - ->authenticateWith($this->getUsername(), $this->getPassword()) - ->sendJson() - ->noStrictSsl() - ->setPayload($command->getData()) - ->send(); + + try { + $response = RestRequest::post($this->getUriFor($command->getEndpoint())) + ->authenticateWith($this->getUsername(), $this->getPassword()) + ->sendJson() + ->noStrictSsl() + ->setPayload($command->getData()) + ->send(); + } catch (JsonDecodeException $e) { + throw new CommandTransportException('Got invalid JSON response from the Icinga 2 API: %s', $e->getMessage()); + } + if (isset($response['error'])) { throw new CommandTransportException( 'Can\'t send external Icinga command: %u %s', diff --git a/modules/monitoring/library/Monitoring/Exception/JsonDecodeException.php b/modules/monitoring/library/Monitoring/Exception/JsonDecodeException.php new file mode 100644 index 000000000..a39370ad1 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Exception/JsonDecodeException.php @@ -0,0 +1,55 @@ +=') + ? json_last_error_msg() + : $this->errorCodeToMessage(json_last_error()); + } else { + $msg = $this->errorCodeToMessage($jsonError); + } + + parent::__construct('%s: %s', $msg, $invalidJson); + } + + /** + * Convert the given error code (from {@link json_last_error()}) to a human readable error message + * + * @param int $jsonError + * @return string + */ + protected function errorCodeToMessage($jsonError) + { + switch ($jsonError) { + case JSON_ERROR_DEPTH: + return 'The maximum stack depth has been exceeded'; + case JSON_ERROR_CTRL_CHAR: + return 'Control character error, possibly incorrectly encoded'; + case JSON_ERROR_STATE_MISMATCH: + return 'Invalid or malformed JSON'; + case JSON_ERROR_SYNTAX: + return 'Syntax error'; + case JSON_ERROR_UTF8: + return 'Malformed UTF-8 characters, possibly incorrectly encoded'; + default: + return 'An error occured when parsing a JSON string'; + } + } +} diff --git a/modules/monitoring/library/Monitoring/Web/Rest/RestRequest.php b/modules/monitoring/library/Monitoring/Web/Rest/RestRequest.php index 2cf25ea0a..b975b68f3 100644 --- a/modules/monitoring/library/Monitoring/Web/Rest/RestRequest.php +++ b/modules/monitoring/library/Monitoring/Web/Rest/RestRequest.php @@ -5,6 +5,7 @@ namespace Icinga\Module\Monitoring\Web\Rest; use Exception; use Icinga\Application\Logger; +use Icinga\Module\Monitoring\Exception\JsonDecodeException; /** * REST Request @@ -264,30 +265,7 @@ class RestRequest $response = @json_decode($result, true); if ($response === null) { - if (version_compare(PHP_VERSION, '5.5.0', '>=')) { - throw new Exception(json_last_error_msg()); - } else { - switch (json_last_error()) { - case JSON_ERROR_DEPTH: - $msg = 'The maximum stack depth has been exceeded'; - break; - case JSON_ERROR_CTRL_CHAR: - $msg = 'Control character error, possibly incorrectly encoded'; - break; - case JSON_ERROR_STATE_MISMATCH: - $msg = 'Invalid or malformed JSON'; - break; - case JSON_ERROR_SYNTAX: - $msg = 'Syntax error'; - break; - case JSON_ERROR_UTF8: - $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded'; - break; - default: - $msg = 'An error occured when parsing a JSON string'; - } - throw new Exception($msg); - } + throw new JsonDecodeException($result); } return $response; From 96ef0dccf8fbb19e3def1668ffca990cbd895622 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 9 Feb 2017 19:50:04 +0100 Subject: [PATCH 2/2] Make JSON error handling logic reusable refs #2728 --- .../Exception/Json/JsonDecodeException.php | 11 +++ .../Exception/Json/JsonEncodeException.php | 11 +++ .../Icinga/Exception/Json/JsonException.php | 13 +++ library/Icinga/Util/Json.php | 80 +++++++++++++++++++ .../Command/Transport/ApiCommandTransport.php | 2 +- .../Exception/JsonDecodeException.php | 55 ------------- .../Monitoring/Web/Rest/RestRequest.php | 10 +-- 7 files changed, 118 insertions(+), 64 deletions(-) create mode 100644 library/Icinga/Exception/Json/JsonDecodeException.php create mode 100644 library/Icinga/Exception/Json/JsonEncodeException.php create mode 100644 library/Icinga/Exception/Json/JsonException.php create mode 100644 library/Icinga/Util/Json.php delete mode 100644 modules/monitoring/library/Monitoring/Exception/JsonDecodeException.php diff --git a/library/Icinga/Exception/Json/JsonDecodeException.php b/library/Icinga/Exception/Json/JsonDecodeException.php new file mode 100644 index 000000000..978eb308b --- /dev/null +++ b/library/Icinga/Exception/Json/JsonDecodeException.php @@ -0,0 +1,11 @@ +=')) { + return json_last_error_msg(); + } + + // All possible error codes before PHP 5.5.0 (except JSON_ERROR_NONE) + switch (json_last_error()) { + case JSON_ERROR_DEPTH: + return 'Maximum stack depth exceeded'; + case JSON_ERROR_STATE_MISMATCH: + return 'State mismatch (invalid or malformed JSON)'; + case JSON_ERROR_CTRL_CHAR: + return 'Control character error, possibly incorrectly encoded'; + case JSON_ERROR_SYNTAX: + return 'Syntax error'; + case JSON_ERROR_UTF8: + return 'Malformed UTF-8 characters, possibly incorrectly encoded'; + default: + return 'Unknown error'; + } + } +} diff --git a/modules/monitoring/library/Monitoring/Command/Transport/ApiCommandTransport.php b/modules/monitoring/library/Monitoring/Command/Transport/ApiCommandTransport.php index c3d4cd4c1..1f88bebd9 100644 --- a/modules/monitoring/library/Monitoring/Command/Transport/ApiCommandTransport.php +++ b/modules/monitoring/library/Monitoring/Command/Transport/ApiCommandTransport.php @@ -4,11 +4,11 @@ namespace Icinga\Module\Monitoring\Command\Transport; use Icinga\Application\Logger; +use Icinga\Exception\Json\JsonDecodeException; use Icinga\Module\Monitoring\Command\IcingaApiCommand; use Icinga\Module\Monitoring\Command\IcingaCommand; use Icinga\Module\Monitoring\Command\Renderer\IcingaApiCommandRenderer; use Icinga\Module\Monitoring\Exception\CommandTransportException; -use Icinga\Module\Monitoring\Exception\JsonDecodeException; use Icinga\Module\Monitoring\Web\Rest\RestRequest; /** diff --git a/modules/monitoring/library/Monitoring/Exception/JsonDecodeException.php b/modules/monitoring/library/Monitoring/Exception/JsonDecodeException.php deleted file mode 100644 index a39370ad1..000000000 --- a/modules/monitoring/library/Monitoring/Exception/JsonDecodeException.php +++ /dev/null @@ -1,55 +0,0 @@ -=') - ? json_last_error_msg() - : $this->errorCodeToMessage(json_last_error()); - } else { - $msg = $this->errorCodeToMessage($jsonError); - } - - parent::__construct('%s: %s', $msg, $invalidJson); - } - - /** - * Convert the given error code (from {@link json_last_error()}) to a human readable error message - * - * @param int $jsonError - * @return string - */ - protected function errorCodeToMessage($jsonError) - { - switch ($jsonError) { - case JSON_ERROR_DEPTH: - return 'The maximum stack depth has been exceeded'; - case JSON_ERROR_CTRL_CHAR: - return 'Control character error, possibly incorrectly encoded'; - case JSON_ERROR_STATE_MISMATCH: - return 'Invalid or malformed JSON'; - case JSON_ERROR_SYNTAX: - return 'Syntax error'; - case JSON_ERROR_UTF8: - return 'Malformed UTF-8 characters, possibly incorrectly encoded'; - default: - return 'An error occured when parsing a JSON string'; - } - } -} diff --git a/modules/monitoring/library/Monitoring/Web/Rest/RestRequest.php b/modules/monitoring/library/Monitoring/Web/Rest/RestRequest.php index b975b68f3..adeb28567 100644 --- a/modules/monitoring/library/Monitoring/Web/Rest/RestRequest.php +++ b/modules/monitoring/library/Monitoring/Web/Rest/RestRequest.php @@ -5,7 +5,7 @@ namespace Icinga\Module\Monitoring\Web\Rest; use Exception; use Icinga\Application\Logger; -use Icinga\Module\Monitoring\Exception\JsonDecodeException; +use Icinga\Util\Json; /** * REST Request @@ -262,12 +262,6 @@ class RestRequest fclose($stream); } - $response = @json_decode($result, true); - - if ($response === null) { - throw new JsonDecodeException($result); - } - - return $response; + return Json::decode($result, true); } }