Skip to content

Commit

Permalink
Merge pull request #2730 from Icinga/bugfix/invalid-icinga-2-api-resp…
Browse files Browse the repository at this point in the history
…onse-2728

Handle invalid Icinga 2 API response types
  • Loading branch information
lippserd committed Feb 13, 2017
2 parents a8984d1 + 96ef0dc commit d234136
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 36 deletions.
11 changes: 11 additions & 0 deletions library/Icinga/Exception/Json/JsonDecodeException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php
/* Icinga Web 2 | (c) 2017 Icinga Development Team | GPLv2+ */

namespace Icinga\Exception\Json;

/**
* Exception thrown by {@link \Icinga\Util\Json::decode()} on failure
*/
class JsonDecodeException extends JsonException
{
}
11 changes: 11 additions & 0 deletions library/Icinga/Exception/Json/JsonEncodeException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php
/* Icinga Web 2 | (c) 2017 Icinga Development Team | GPLv2+ */

namespace Icinga\Exception\Json;

/**
* Exception thrown by {@link \Icinga\Util\Json::encode()} on failure
*/
class JsonEncodeException extends JsonException
{
}
13 changes: 13 additions & 0 deletions library/Icinga/Exception/Json/JsonException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php
/* Icinga Web 2 | (c) 2017 Icinga Development Team | GPLv2+ */

namespace Icinga\Exception\Json;

use Icinga\Exception\IcingaException;

/**
* Exception thrown by {@link \Icinga\Util\Json} on failure
*/
abstract class JsonException extends IcingaException
{
}
80 changes: 80 additions & 0 deletions library/Icinga/Util/Json.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php
/* Icinga Web 2 | (c) 2017 Icinga Development Team | GPLv2+ */

namespace Icinga\Util;

use Icinga\Exception\Json\JsonDecodeException;
use Icinga\Exception\Json\JsonEncodeException;

/**
* Wrap {@link json_encode()} and {@link json_decode()} with error handling
*/
class Json
{
/**
* {@link json_encode()} wrapper
*
* @param mixed $value
* @param int $options
* @param int $depth
*
* @return string
* @throws JsonEncodeException
*/
public static function encode($value, $options = 0, $depth = 512)
{
$encoded = json_encode($value, $options, $depth);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new JsonEncodeException('%s: %s', static::lastErrorMsg(), var_export($value, true));
}
return $encoded;
}

/**
* {@link json_decode()} wrapper
*
* @param string $json
* @param bool $assoc
* @param int $depth
* @param int $options
*
* @return mixed
* @throws JsonDecodeException
*/
public static function decode($json, $assoc = false, $depth = 512, $options = 0)
{
$decoded = json_decode($json, $assoc, $depth, $options);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new JsonDecodeException('%s: %s', static::lastErrorMsg(), var_export($json, true));
}
return $decoded;
}

/**
* {@link json_last_error_msg()} replacement for PHP < 5.5.0
*
* @return string
*/
protected static function lastErrorMsg()
{
if (version_compare(PHP_VERSION, '5.5.0', '>=')) {
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';
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
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;
Expand Down Expand Up @@ -193,12 +194,18 @@ protected function sendCommand(IcingaApiCommand $command)
$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',
Expand Down
32 changes: 2 additions & 30 deletions modules/monitoring/library/Monitoring/Web/Rest/RestRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

use Exception;
use Icinga\Application\Logger;
use Icinga\Util\Json;

/**
* REST Request
Expand Down Expand Up @@ -261,35 +262,6 @@ public function send()
fclose($stream);
}

$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);
}
}

return $response;
return Json::decode($result, true);
}
}

0 comments on commit d234136

Please sign in to comment.