109 lines
4.1 KiB
PHP
109 lines
4.1 KiB
PHP
<?php
|
|
// RpcRouteHandler.php
|
|
// Created: 2024-08-13
|
|
// Updated: 2024-08-13
|
|
|
|
namespace Aiwass;
|
|
|
|
use Exception;
|
|
use ReflectionFunction;
|
|
use ReflectionIntersectionType;
|
|
use ReflectionNamedType;
|
|
use ReflectionUnionType;
|
|
use Index\Http\{HttpResponseBuilder,HttpRequest};
|
|
use Index\Http\Content\FormContent;
|
|
use Index\Http\Routing\{HttpGet,HttpPost,RouteHandler};
|
|
|
|
/**
|
|
* Provides a router implementation for an Aiwass RPC server.
|
|
*/
|
|
class RpcRouteHandler extends RouteHandler {
|
|
/**
|
|
* @param RpcServer $server An RPC server instance.
|
|
*/
|
|
public function __construct(
|
|
private RpcServer $server
|
|
) {}
|
|
|
|
/**
|
|
* Handles an action request.
|
|
*
|
|
* @param HttpResponseBuilder $response Response that it being built.
|
|
* @param HttpRequest $request Request that is being handled.
|
|
* @param string $action Name of the action specified in the URL.
|
|
* @return int|array{error: string, message?: string}|mixed Response to the request.
|
|
*/
|
|
#[HttpGet('/_aiwass/([A-Za-z0-9\-_\.:]+)')]
|
|
#[HttpPost('/_aiwass/([A-Za-z0-9\-_\.:]+)')]
|
|
public function handleAction(HttpResponseBuilder $response, HttpRequest $request, string $action) {
|
|
if($action === '')
|
|
return 404;
|
|
|
|
if($request->getMethod() === 'POST') {
|
|
$content = $request->getContent();
|
|
if(!($content instanceof FormContent))
|
|
return 400;
|
|
|
|
$expectProcedure = true;
|
|
$paramString = $content->getParamString();
|
|
$params = $content->getParams();
|
|
} elseif($request->getMethod() === 'GET') {
|
|
$expectProcedure = false;
|
|
$paramString = $request->getParamString();
|
|
$params = $request->getParams();
|
|
} else {
|
|
$response->setHeader('Allow', 'GET, POST');
|
|
return 405;
|
|
}
|
|
|
|
$response->setContentType('application/vnd.msgpack');
|
|
|
|
$userToken = (string)$request->getHeaderLine('X-Aiwass-Verify');
|
|
if($userToken === '') {
|
|
$response->setStatusCode(403);
|
|
return AiwassMsgPack::encode(['error' => 'aiwass:verify', 'message' => 'X-Aiwass-Verify header is missing.']);
|
|
}
|
|
|
|
$actInfo = $this->server->getActionInfo($action);
|
|
if($actInfo === null) {
|
|
$response->setStatusCode(404);
|
|
return AiwassMsgPack::encode(['error' => 'aiwass:unknown', 'message' => 'No action with that name exists.']);
|
|
}
|
|
|
|
if($actInfo->isProcedure() !== $expectProcedure) {
|
|
$response->setStatusCode(405);
|
|
$response->setHeader('Allow', $actInfo->isProcedure() ? 'POST' : 'GET');
|
|
return AiwassMsgPack::encode(['error' => 'aiwass:method', 'message' => 'This action cannot be called using this request method.']);
|
|
}
|
|
|
|
if(!$this->server->getVerificationProvider()->verify($userToken, $expectProcedure, $action, $paramString))
|
|
return AiwassMsgPack::encode(['error' => 'aiwass:verify', 'message' => 'Request token verification failed.']);
|
|
|
|
$handlerInfo = new ReflectionFunction($actInfo->getHandler());
|
|
if($handlerInfo->isVariadic())
|
|
return AiwassMsgPack::encode(['error' => 'aiwass:variadic', 'message' => 'Handler was declared as a variadic method and is thus impossible to be called.']);
|
|
|
|
$handlerArgs = [];
|
|
|
|
$handlerParams = $handlerInfo->getParameters();
|
|
foreach($handlerParams as $paramInfo) {
|
|
$paramName = $paramInfo->getName();
|
|
if(!array_key_exists($paramName, $params)) {
|
|
if($paramInfo->isOptional())
|
|
continue;
|
|
|
|
if(!$paramInfo->allowsNull())
|
|
return AiwassMsgPack::encode(['error' => 'aiwass:param:required', 'message' => 'A required parameter is missing.', 'param' => $paramName]);
|
|
}
|
|
|
|
$handlerArgs[$paramName] = $params[$paramName] ?? null;
|
|
}
|
|
|
|
if(count($handlerArgs) !== count($params)) {
|
|
$response->setStatusCode(400);
|
|
return AiwassMsgPack::encode(['error' => 'aiwass:params', 'message' => 'Unsupported arguments were specified.']);
|
|
}
|
|
|
|
return AiwassMsgPack::encode($handlerInfo->invokeArgs($handlerArgs));
|
|
}
|
|
}
|