Merge pull request #4113 from dokuwiki/jsonrpc-conformity
JSONRPC Standards
This commit is contained in:
commit
f0319d45ca
|
@ -9,6 +9,9 @@ class JsonRpcServer
|
|||
{
|
||||
protected $remote;
|
||||
|
||||
/** @var float The XML-RPC Version. 0 is our own simplified variant */
|
||||
protected $version = 0;
|
||||
|
||||
/**
|
||||
* JsonRpcServer constructor.
|
||||
*/
|
||||
|
@ -46,16 +49,113 @@ class JsonRpcServer
|
|||
throw new RemoteException("JSON-RPC server only accepts application/json requests.", -32606);
|
||||
}
|
||||
|
||||
$call = $INPUT->server->str('PATH_INFO');
|
||||
$call = trim($call, '/');
|
||||
try {
|
||||
$args = json_decode(file_get_contents('php://input'), true, 512, JSON_THROW_ON_ERROR);
|
||||
$body = file_get_contents('php://input');
|
||||
if($body !== '') {
|
||||
$data = json_decode($body, true, 512, JSON_THROW_ON_ERROR);
|
||||
} else {
|
||||
$data = [];
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$args = [];
|
||||
http_status(400);
|
||||
throw new RemoteException("JSON-RPC server only accepts valid JSON.", -32700);
|
||||
}
|
||||
if (!is_array($args)) $args = [];
|
||||
|
||||
return $this->call($call, $args);
|
||||
return $this->createResponse($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* This executes the method and returns the result
|
||||
*
|
||||
* This should handle all JSON-RPC versions and our simplified version
|
||||
*
|
||||
* @link https://en.wikipedia.org/wiki/JSON-RPC
|
||||
* @link https://www.jsonrpc.org/specification
|
||||
* @param array $data
|
||||
* @return array
|
||||
* @throws RemoteException
|
||||
*/
|
||||
protected function createResponse($data)
|
||||
{
|
||||
global $INPUT;
|
||||
$return = [];
|
||||
|
||||
if (isset($data['method'])) {
|
||||
// this is a standard conform request (at least version 1.0)
|
||||
$method = $data['method'];
|
||||
$params = $data['params'] ?? [];
|
||||
$this->version = 1;
|
||||
|
||||
// always return the same ID
|
||||
if (isset($data['id'])) $return['id'] = $data['id'];
|
||||
|
||||
// version 2.0 request
|
||||
if (isset($data['jsonrpc'])) {
|
||||
$return['jsonrpc'] = $data['jsonrpc'];
|
||||
$this->version = (float)$data['jsonrpc'];
|
||||
}
|
||||
|
||||
// version 1.1 request
|
||||
if (isset($data['version'])) {
|
||||
$return['version'] = $data['version'];
|
||||
$this->version = (float)$data['version'];
|
||||
}
|
||||
} else {
|
||||
// this is a simplified request
|
||||
$method = $INPUT->server->str('PATH_INFO');
|
||||
$method = trim($method, '/');
|
||||
$params = $data;
|
||||
$this->version = 0;
|
||||
}
|
||||
|
||||
// excute the method
|
||||
$return['result'] = $this->call($method, $params);
|
||||
$this->addErrorData($return); // handles non-error info
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an error response
|
||||
*
|
||||
* @param \Exception $exception
|
||||
* @return array
|
||||
*/
|
||||
public function returnError($exception)
|
||||
{
|
||||
$return = [];
|
||||
$this->addErrorData($return, $exception);
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Depending on the requested version, add error data to the response
|
||||
*
|
||||
* @param array $response
|
||||
* @param \Exception|null $e
|
||||
* @return void
|
||||
*/
|
||||
protected function addErrorData(&$response, $e = null)
|
||||
{
|
||||
if ($e !== null) {
|
||||
// error occured, add to response
|
||||
$response['error'] = [
|
||||
'code' => $e->getCode(),
|
||||
'message' => $e->getMessage()
|
||||
];
|
||||
} else {
|
||||
// no error, act according to version
|
||||
if ($this->version > 0 && $this->version < 2) {
|
||||
// version 1.* wants null
|
||||
$response['error'] = null;
|
||||
} elseif ($this->version < 1) {
|
||||
// simplified version wants success
|
||||
$response['error'] = [
|
||||
'code' => 0,
|
||||
'message' => 'success'
|
||||
];
|
||||
}
|
||||
// version 2 wants no error at all
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -68,6 +168,13 @@ class JsonRpcServer
|
|||
*/
|
||||
public function call($methodname, $args)
|
||||
{
|
||||
if (!array_is_list($args)) {
|
||||
throw new RemoteException(
|
||||
"server error. arguments need to passed as list. named arguments not supported",
|
||||
-32602
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
$result = $this->remote->call($methodname, $args);
|
||||
return $result;
|
||||
|
|
|
@ -119,3 +119,17 @@ if (!function_exists('str_ends_with')) {
|
|||
return empty($needle) || substr($haystack, -strlen($needle)) === $needle;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* polyfill for PHP < 8.1
|
||||
* @see https://www.php.net/manual/en/function.array-is-list
|
||||
*/
|
||||
if (!function_exists('array_is_list')) {
|
||||
function array_is_list(array $arr)
|
||||
{
|
||||
if ($arr === []) {
|
||||
return true;
|
||||
}
|
||||
return array_keys($arr) === range(0, count($arr) - 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,21 +11,9 @@ header('Content-Type: application/json');
|
|||
|
||||
$server = new JsonRpcServer();
|
||||
try {
|
||||
$result = [
|
||||
'error' => [
|
||||
'code' => 0,
|
||||
'message' => 'success'
|
||||
],
|
||||
'data' => $server->serve(),
|
||||
];
|
||||
$result = $server->serve();
|
||||
} catch (\Exception $e) {
|
||||
$result = [
|
||||
'error' => [
|
||||
'code' => $e->getCode(),
|
||||
'message' => $e->getMessage()
|
||||
],
|
||||
'data' => null,
|
||||
];
|
||||
$result = $server->returnError($e);
|
||||
}
|
||||
|
||||
echo json_encode($result, JSON_THROW_ON_ERROR);
|
||||
|
|
Loading…
Reference in New Issue