Vendoring deprecated composer libs
This commit is contained in:
21
libs/jsonrpc/LICENSE
Normal file
21
libs/jsonrpc/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Frederic Guillot
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
412
libs/jsonrpc/README.markdown
Normal file
412
libs/jsonrpc/README.markdown
Normal file
@@ -0,0 +1,412 @@
|
||||
JsonRPC PHP Client and Server
|
||||
=============================
|
||||
|
||||
A simple Json-RPC client/server that just works.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
- JSON-RPC 2.0 only
|
||||
- The server support batch requests and notifications
|
||||
- Authentication and IP based client restrictions
|
||||
- Custom Middleware
|
||||
- Fully unit tested
|
||||
- Requirements: PHP >= 5.3.4
|
||||
- License: MIT
|
||||
|
||||
Author
|
||||
------
|
||||
|
||||
Frédéric Guillot
|
||||
|
||||
Installation with Composer
|
||||
--------------------------
|
||||
|
||||
```bash
|
||||
composer require fguillot/json-rpc @stable
|
||||
```
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
### Server
|
||||
|
||||
Callback binding:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use JsonRPC\Server;
|
||||
|
||||
$server = new Server();
|
||||
$server->getProcedureHandler()
|
||||
->withCallback('addition', function ($a, $b) {
|
||||
return $a + $b;
|
||||
})
|
||||
->withCallback('random', function ($start, $end) {
|
||||
return mt_rand($start, $end);
|
||||
})
|
||||
;
|
||||
|
||||
echo $server->execute();
|
||||
```
|
||||
|
||||
Callback binding from array:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use JsonRPC\Server;
|
||||
|
||||
$callbacks = array(
|
||||
'getA' => function() { return 'A'; },
|
||||
'getB' => function() { return 'B'; },
|
||||
'getC' => function() { return 'C'; }
|
||||
);
|
||||
|
||||
$server = new Server();
|
||||
$server->getProcedureHandler()->withCallbackArray($callbacks);
|
||||
|
||||
echo $server->execute();
|
||||
```
|
||||
|
||||
Class/Method binding:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use JsonRPC\Server;
|
||||
|
||||
class Api
|
||||
{
|
||||
public function doSomething($arg1, $arg2 = 3)
|
||||
{
|
||||
return $arg1 + $arg2;
|
||||
}
|
||||
}
|
||||
|
||||
$server = new Server();
|
||||
$procedureHandler = $server->getProcedureHandler();
|
||||
|
||||
// Bind the method Api::doSomething() to the procedure myProcedure
|
||||
$procedureHandler->withClassAndMethod('myProcedure', 'Api', 'doSomething');
|
||||
|
||||
// Use a class instance instead of the class name
|
||||
$procedureHandler->withClassAndMethod('mySecondProcedure', new Api, 'doSomething');
|
||||
|
||||
// The procedure and the method are the same
|
||||
$procedureHandler->withClassAndMethod('doSomething', 'Api');
|
||||
|
||||
// Attach the class, the client will be able to call directly Api::doSomething()
|
||||
$procedureHandler->withObject(new Api());
|
||||
|
||||
echo $server->execute();
|
||||
```
|
||||
|
||||
Class/Method binding from array:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use JsonRPC\Server;
|
||||
|
||||
class MathApi
|
||||
{
|
||||
public function addition($arg1, $arg2)
|
||||
{
|
||||
return $arg1 + $arg2;
|
||||
}
|
||||
|
||||
public function subtraction($arg1, $arg2)
|
||||
{
|
||||
return $arg1 - $arg2;
|
||||
}
|
||||
|
||||
public function multiplication($arg1, $arg2)
|
||||
{
|
||||
return $arg1 * $arg2;
|
||||
}
|
||||
|
||||
public function division($arg1, $arg2)
|
||||
{
|
||||
return $arg1 / $arg2;
|
||||
}
|
||||
}
|
||||
|
||||
$callbacks = array(
|
||||
'addition' => array( 'MathApi', addition ),
|
||||
'subtraction' => array( 'MathApi', subtraction ),
|
||||
'multiplication' => array( 'MathApi', multiplication ),
|
||||
'division' => array( 'MathApi', division )
|
||||
);
|
||||
|
||||
$server = new Server();
|
||||
$server->getProcedureHandler()->withClassAndMethodArray($callbacks);
|
||||
|
||||
echo $server->execute();
|
||||
```
|
||||
|
||||
Server Middleware:
|
||||
|
||||
Middleware might be used to authenticate and authorize the client.
|
||||
They are executed before each procedure.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use JsonRPC\Server;
|
||||
use JsonRPC\MiddlewareInterface;
|
||||
use JsonRPC\Exception\AuthenticationFailureException;
|
||||
|
||||
class Api
|
||||
{
|
||||
public function doSomething($arg1, $arg2 = 3)
|
||||
{
|
||||
return $arg1 + $arg2;
|
||||
}
|
||||
}
|
||||
|
||||
class MyMiddleware implements MiddlewareInterface
|
||||
{
|
||||
public function execute($username, $password, $procedureName)
|
||||
{
|
||||
if ($username !== 'foobar') {
|
||||
throw new AuthenticationFailureException('Wrong credentials!');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$server = new Server();
|
||||
$server->getMiddlewareHandler()->withMiddleware(new MyMiddleware());
|
||||
$server->getProcedureHandler()->withObject(new Api());
|
||||
echo $server->execute();
|
||||
```
|
||||
|
||||
You can raise a `AuthenticationFailureException` when the API credentials are wrong or a `AccessDeniedException` when the user is not allowed to access to the procedure.
|
||||
|
||||
### Client
|
||||
|
||||
Example with positional parameters:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use JsonRPC\Client;
|
||||
|
||||
$client = new Client('http://localhost/server.php');
|
||||
$result = $client->execute('addition', [3, 5]);
|
||||
```
|
||||
|
||||
Example with named arguments:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use JsonRPC\Client;
|
||||
|
||||
$client = new Client('http://localhost/server.php');
|
||||
$result = $client->execute('random', ['end' => 10, 'start' => 1]);
|
||||
```
|
||||
|
||||
Arguments are called in the right order.
|
||||
|
||||
Examples with the magic method `__call()`:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use JsonRPC\Client;
|
||||
|
||||
$client = new Client('http://localhost/server.php');
|
||||
$result = $client->random(50, 100);
|
||||
```
|
||||
|
||||
The example above use positional arguments for the request and this one use named arguments:
|
||||
|
||||
```php
|
||||
$result = $client->random(['end' => 10, 'start' => 1]);
|
||||
```
|
||||
|
||||
### Client batch requests
|
||||
|
||||
Call several procedures in a single HTTP request:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use JsonRPC\Client;
|
||||
|
||||
$client = new Client('http://localhost/server.php');
|
||||
|
||||
$results = $client->batch()
|
||||
->foo(['arg1' => 'bar'])
|
||||
->random(1, 100)
|
||||
->add(4, 3)
|
||||
->execute('add', [2, 5])
|
||||
->send();
|
||||
|
||||
print_r($results);
|
||||
```
|
||||
|
||||
All results are stored at the same position of the call.
|
||||
|
||||
### Client exceptions
|
||||
|
||||
Client exceptions are normally thrown when an error is returned by the server. You can change this behaviour by
|
||||
using the `$returnException` argument which causes exceptions to be returned. This can be extremely useful when
|
||||
executing the batch request.
|
||||
|
||||
- `BadFunctionCallException`: Procedure not found on the server
|
||||
- `InvalidArgumentException`: Wrong procedure arguments
|
||||
- `JsonRPC\Exception\AccessDeniedException`: Access denied
|
||||
- `JsonRPC\Exception\ConnectionFailureException`: Connection failure
|
||||
- `JsonRPC\Exception\ServerErrorException`: Internal server error
|
||||
|
||||
### Enable client debugging
|
||||
|
||||
You can enable the debug mode to see the JSON request and response:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use JsonRPC\Client;
|
||||
|
||||
$client = new Client('http://localhost/server.php');
|
||||
$client->getHttpClient()->withDebug();
|
||||
```
|
||||
|
||||
The debug output is sent to the PHP system logger.
|
||||
You can configure the log destination in your `php.ini`.
|
||||
|
||||
Output example:
|
||||
|
||||
```json
|
||||
==> Request:
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "removeCategory",
|
||||
"id": 486782327,
|
||||
"params": [
|
||||
1
|
||||
]
|
||||
}
|
||||
==> Response:
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 486782327,
|
||||
"result": true
|
||||
}
|
||||
```
|
||||
|
||||
### IP based client restrictions
|
||||
|
||||
The server can allow only some IP addresses:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use JsonRPC\Server;
|
||||
|
||||
$server = new Server;
|
||||
|
||||
// IP client restrictions
|
||||
$server->allowHosts(['192.168.0.1', '127.0.0.1']);
|
||||
|
||||
[...]
|
||||
|
||||
// Return the response to the client
|
||||
echo $server->execute();
|
||||
```
|
||||
|
||||
If the client is blocked, you got a 403 Forbidden HTTP response.
|
||||
|
||||
### HTTP Basic Authentication
|
||||
|
||||
If you use HTTPS, you can allow client by using a username/password.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use JsonRPC\Server;
|
||||
|
||||
$server = new Server;
|
||||
|
||||
// List of users to allow
|
||||
$server->authentication(['user1' => 'password1', 'user2' => 'password2']);
|
||||
|
||||
[...]
|
||||
|
||||
// Return the response to the client
|
||||
echo $server->execute();
|
||||
```
|
||||
|
||||
On the client, set credentials like that:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use JsonRPC\Client;
|
||||
|
||||
$client = new Client('http://localhost/server.php');
|
||||
$client->getHttpClient()
|
||||
->withUsername('Foo')
|
||||
->withPassword('Bar');
|
||||
```
|
||||
|
||||
If the authentication failed, the client throw a RuntimeException.
|
||||
|
||||
Using an alternative authentication header:
|
||||
|
||||
```php
|
||||
|
||||
use JsonRPC\Server;
|
||||
|
||||
$server = new Server();
|
||||
$server->setAuthenticationHeader('X-Authentication');
|
||||
$server->authentication(['myusername' => 'mypassword']);
|
||||
```
|
||||
|
||||
The example above will use the HTTP header `X-Authentication` instead of the standard `Authorization: Basic [BASE64_CREDENTIALS]`.
|
||||
The username/password values need be encoded in base64: `base64_encode('username:password')`.
|
||||
|
||||
### Local Exceptions
|
||||
|
||||
By default, the server will relay all exceptions to the client.
|
||||
If you would like to relay only some of them, use the method `Server::withLocalException($exception)`:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use JsonRPC\Server;
|
||||
class MyException1 extends Exception {};
|
||||
class MyException2 extends Exception {};
|
||||
|
||||
$server = new Server();
|
||||
|
||||
// Exceptions that should NOT be relayed to the client, if they occurs
|
||||
$server
|
||||
->withLocalException('MyException1')
|
||||
->withLocalException('MyException2')
|
||||
;
|
||||
|
||||
[...]
|
||||
|
||||
echo $server->execute();
|
||||
```
|
||||
|
||||
### Callback before client request
|
||||
|
||||
You can use a callback to change the HTTP headers or the URL before to make the request to the server.
|
||||
|
||||
Example:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
$client = new Client();
|
||||
$client->getHttpClient()->withBeforeRequestCallback(function(HttpClient $client, $payload) {
|
||||
$client->withHeaders(array('Content-Length: '.strlen($payload)));
|
||||
});
|
||||
|
||||
$client->myProcedure(123);
|
||||
```
|
||||
198
libs/jsonrpc/src/JsonRPC/Client.php
Normal file
198
libs/jsonrpc/src/JsonRPC/Client.php
Normal file
@@ -0,0 +1,198 @@
|
||||
<?php
|
||||
|
||||
namespace JsonRPC;
|
||||
|
||||
use Exception;
|
||||
use JsonRPC\Request\RequestBuilder;
|
||||
use JsonRPC\Response\ResponseParser;
|
||||
|
||||
/**
|
||||
* JsonRPC client class
|
||||
*
|
||||
* @package JsonRPC
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Client
|
||||
{
|
||||
/**
|
||||
* If the only argument passed to a function is an array
|
||||
* assume it contains named arguments
|
||||
*
|
||||
* @access private
|
||||
* @var boolean
|
||||
*/
|
||||
private $isNamedArguments = true;
|
||||
|
||||
/**
|
||||
* Do not immediately throw an exception on error. Return it instead.
|
||||
*
|
||||
* @access public
|
||||
* @var boolean
|
||||
*/
|
||||
private $returnException = false;
|
||||
|
||||
/**
|
||||
* True for a batch request
|
||||
*
|
||||
* @access private
|
||||
* @var boolean
|
||||
*/
|
||||
private $isBatch = false;
|
||||
|
||||
/**
|
||||
* Batch payload
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $batch = array();
|
||||
|
||||
/**
|
||||
* Http Client
|
||||
*
|
||||
* @access private
|
||||
* @var HttpClient
|
||||
*/
|
||||
private $httpClient;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param string $url Server URL
|
||||
* @param bool $returnException Return exceptions
|
||||
* @param HttpClient $httpClient HTTP client object
|
||||
*/
|
||||
public function __construct($url = '', $returnException = false, HttpClient $httpClient = null)
|
||||
{
|
||||
$this->httpClient = $httpClient ?: new HttpClient($url);
|
||||
$this->returnException = $returnException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Arguments passed are always positional
|
||||
*
|
||||
* @access public
|
||||
* @return $this
|
||||
*/
|
||||
public function withPositionalArguments()
|
||||
{
|
||||
$this->isNamedArguments = false;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get HTTP Client
|
||||
*
|
||||
* @access public
|
||||
* @return HttpClient
|
||||
*/
|
||||
public function getHttpClient()
|
||||
{
|
||||
return $this->httpClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set username and password
|
||||
*
|
||||
* @access public
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
* @return $this
|
||||
*/
|
||||
public function authentication($username, $password)
|
||||
{
|
||||
$this->httpClient
|
||||
->withUsername($username)
|
||||
->withPassword($password);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatic mapping of procedures
|
||||
*
|
||||
* @access public
|
||||
* @param string $method Procedure name
|
||||
* @param array $params Procedure arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($method, array $params)
|
||||
{
|
||||
if ($this->isNamedArguments && count($params) === 1 && is_array($params[0])) {
|
||||
$params = $params[0];
|
||||
}
|
||||
|
||||
return $this->execute($method, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a batch request
|
||||
*
|
||||
* @access public
|
||||
* @return Client
|
||||
*/
|
||||
public function batch()
|
||||
{
|
||||
$this->isBatch = true;
|
||||
$this->batch = array();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a batch request
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function send()
|
||||
{
|
||||
$this->isBatch = false;
|
||||
return $this->sendPayload('['.implode(', ', $this->batch).']');
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a procedure
|
||||
*
|
||||
* @access public
|
||||
* @param string $procedure Procedure name
|
||||
* @param array $params Procedure arguments
|
||||
* @param array $reqattrs
|
||||
* @param string|null $requestId Request Id
|
||||
* @param string[] $headers Headers for this request
|
||||
* @return mixed
|
||||
*/
|
||||
public function execute($procedure, array $params = array(), array $reqattrs = array(), $requestId = null, array $headers = array())
|
||||
{
|
||||
$payload = RequestBuilder::create()
|
||||
->withProcedure($procedure)
|
||||
->withParams($params)
|
||||
->withRequestAttributes($reqattrs)
|
||||
->withId($requestId)
|
||||
->build();
|
||||
|
||||
if ($this->isBatch) {
|
||||
$this->batch[] = $payload;
|
||||
return $this;
|
||||
}
|
||||
|
||||
return $this->sendPayload($payload, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send payload
|
||||
*
|
||||
* @access private
|
||||
* @throws Exception
|
||||
* @param string $payload
|
||||
* @param string[] $headers
|
||||
* @return Exception|Client
|
||||
*/
|
||||
private function sendPayload($payload, array $headers = array())
|
||||
{
|
||||
return ResponseParser::create()
|
||||
->withReturnException($this->returnException)
|
||||
->withPayload($this->httpClient->execute($payload, $headers))
|
||||
->parse();
|
||||
}
|
||||
}
|
||||
13
libs/jsonrpc/src/JsonRPC/Exception/AccessDeniedException.php
Normal file
13
libs/jsonrpc/src/JsonRPC/Exception/AccessDeniedException.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace JsonRPC\Exception;
|
||||
|
||||
/**
|
||||
* Class AccessDeniedException
|
||||
*
|
||||
* @package JsonRPC\Exception
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class AccessDeniedException extends RpcCallFailedException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace JsonRPC\Exception;
|
||||
|
||||
/**
|
||||
* Class AuthenticationFailureException
|
||||
*
|
||||
* @package JsonRPC\Exception
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class AuthenticationFailureException extends RpcCallFailedException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace JsonRPC\Exception;
|
||||
|
||||
/**
|
||||
* Class ConnectionFailureException
|
||||
*
|
||||
* @package JsonRPC\Exception
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class ConnectionFailureException extends RpcCallFailedException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace JsonRPC\Exception;
|
||||
|
||||
/**
|
||||
* Class InvalidJsonFormatException
|
||||
*
|
||||
* @package JsonRPC\Exception
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class InvalidJsonFormatException extends RpcCallFailedException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace JsonRPC\Exception;
|
||||
|
||||
/**
|
||||
* Class InvalidJsonRpcFormatException
|
||||
*
|
||||
* @package JsonRPC\Exception
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class InvalidJsonRpcFormatException extends RpcCallFailedException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace JsonRPC\Exception;
|
||||
|
||||
/**
|
||||
* Class ResponseEncodingFailureException
|
||||
*
|
||||
* @package JsonRPC\Exception
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class ResponseEncodingFailureException extends RpcCallFailedException
|
||||
{
|
||||
}
|
||||
62
libs/jsonrpc/src/JsonRPC/Exception/ResponseException.php
Normal file
62
libs/jsonrpc/src/JsonRPC/Exception/ResponseException.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace JsonRPC\Exception;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class ResponseException
|
||||
*
|
||||
* @package JsonRPC\Exception
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class ResponseException extends RpcCallFailedException
|
||||
{
|
||||
/**
|
||||
* A value that contains additional information about the error.
|
||||
*
|
||||
* @access protected
|
||||
* @link http://www.jsonrpc.org/specification#error_object
|
||||
* @var mixed
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param string $message [optional] The Exception message to throw.
|
||||
* @param int $code [optional] The Exception code.
|
||||
* @param Exception $previous [optional] The previous exception used for the exception chaining. Since 5.3.0
|
||||
* @param mixed $data [optional] A value that contains additional information about the error.
|
||||
*/
|
||||
public function __construct($message = '', $code = 0, Exception $previous = null, $data = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
$this->setData($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach additional information
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $data [optional] A value that contains additional information about the error.
|
||||
* @return \JsonRPC\Exception\ResponseException
|
||||
*/
|
||||
public function setData($data = null)
|
||||
{
|
||||
$this->data = $data;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get additional information
|
||||
*
|
||||
* @access public
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace JsonRPC\Exception;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class RpcCallFailedException
|
||||
*
|
||||
* @package JsonRPC\Exception
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class RpcCallFailedException extends Exception
|
||||
{
|
||||
}
|
||||
13
libs/jsonrpc/src/JsonRPC/Exception/ServerErrorException.php
Normal file
13
libs/jsonrpc/src/JsonRPC/Exception/ServerErrorException.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace JsonRPC\Exception;
|
||||
|
||||
/**
|
||||
* Class ServerErrorException
|
||||
*
|
||||
* @package JsonRPC\Exception
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class ServerErrorException extends RpcCallFailedException
|
||||
{
|
||||
}
|
||||
449
libs/jsonrpc/src/JsonRPC/HttpClient.php
Normal file
449
libs/jsonrpc/src/JsonRPC/HttpClient.php
Normal file
@@ -0,0 +1,449 @@
|
||||
<?php
|
||||
|
||||
namespace JsonRPC;
|
||||
|
||||
use Closure;
|
||||
use JsonRPC\Exception\AccessDeniedException;
|
||||
use JsonRPC\Exception\ConnectionFailureException;
|
||||
use JsonRPC\Exception\ServerErrorException;
|
||||
|
||||
/**
|
||||
* Class HttpClient
|
||||
*
|
||||
* @package JsonRPC
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class HttpClient
|
||||
{
|
||||
/**
|
||||
* URL of the server
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $url;
|
||||
|
||||
/**
|
||||
* HTTP client timeout
|
||||
*
|
||||
* @access protected
|
||||
* @var integer
|
||||
*/
|
||||
protected $timeout = 5;
|
||||
|
||||
/**
|
||||
* Default HTTP headers to send to the server
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $headers = array(
|
||||
'User-Agent: JSON-RPC PHP Client <https://github.com/fguillot/JsonRPC>',
|
||||
'Content-Type: application/json',
|
||||
'Accept: application/json',
|
||||
'Connection: close',
|
||||
);
|
||||
|
||||
/**
|
||||
* Username for authentication
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $username;
|
||||
|
||||
/**
|
||||
* Password for authentication
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $password;
|
||||
|
||||
/**
|
||||
* Enable debug output to the php error log
|
||||
*
|
||||
* @access protected
|
||||
* @var boolean
|
||||
*/
|
||||
protected $debug = false;
|
||||
|
||||
/**
|
||||
* Cookies
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $cookies = array();
|
||||
|
||||
/**
|
||||
* SSL certificates verification
|
||||
*
|
||||
* @access protected
|
||||
* @var boolean
|
||||
*/
|
||||
protected $verifySslCertificate = true;
|
||||
|
||||
/**
|
||||
* SSL client certificate
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $sslLocalCert;
|
||||
|
||||
/**
|
||||
* Callback called before the doing the request
|
||||
*
|
||||
* @access protected
|
||||
* @var Closure
|
||||
*/
|
||||
protected $beforeRequest;
|
||||
|
||||
/**
|
||||
* HttpClient constructor
|
||||
*
|
||||
* @access public
|
||||
* @param string $url
|
||||
*/
|
||||
public function __construct($url = '')
|
||||
{
|
||||
$this->url = $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set URL
|
||||
*
|
||||
* @access public
|
||||
* @param string $url
|
||||
* @return $this
|
||||
*/
|
||||
public function withUrl($url)
|
||||
{
|
||||
$this->url = $url;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set username
|
||||
*
|
||||
* @access public
|
||||
* @param string $username
|
||||
* @return $this
|
||||
*/
|
||||
public function withUsername($username)
|
||||
{
|
||||
$this->username = $username;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set password
|
||||
*
|
||||
* @access public
|
||||
* @param string $password
|
||||
* @return $this
|
||||
*/
|
||||
public function withPassword($password)
|
||||
{
|
||||
$this->password = $password;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set timeout
|
||||
*
|
||||
* @access public
|
||||
* @param integer $timeout
|
||||
* @return $this
|
||||
*/
|
||||
public function withTimeout($timeout)
|
||||
{
|
||||
$this->timeout = $timeout;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set headers
|
||||
*
|
||||
* @access public
|
||||
* @param array $headers
|
||||
* @return $this
|
||||
*/
|
||||
public function withHeaders(array $headers)
|
||||
{
|
||||
$this->headers = array_merge($this->headers, $headers);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set cookies
|
||||
*
|
||||
* @access public
|
||||
* @param array $cookies
|
||||
* @param boolean $replace
|
||||
*/
|
||||
public function withCookies(array $cookies, $replace = false)
|
||||
{
|
||||
if ($replace) {
|
||||
$this->cookies = $cookies;
|
||||
} else {
|
||||
$this->cookies = array_merge($this->cookies, $cookies);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable debug mode
|
||||
*
|
||||
* @access public
|
||||
* @return $this
|
||||
*/
|
||||
public function withDebug()
|
||||
{
|
||||
$this->debug = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable SSL verification
|
||||
*
|
||||
* @access public
|
||||
* @return $this
|
||||
*/
|
||||
public function withoutSslVerification()
|
||||
{
|
||||
$this->verifySslCertificate = false;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign a certificate to use TLS
|
||||
*
|
||||
* @access public
|
||||
* @return $this
|
||||
*/
|
||||
public function withSslLocalCert($path)
|
||||
{
|
||||
$this->sslLocalCert = $path;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign a callback before the request
|
||||
*
|
||||
* @access public
|
||||
* @param Closure $closure
|
||||
* @return $this
|
||||
*/
|
||||
public function withBeforeRequestCallback(Closure $closure)
|
||||
{
|
||||
$this->beforeRequest = $closure;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cookies
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getCookies()
|
||||
{
|
||||
return $this->cookies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the HTTP request
|
||||
*
|
||||
* @access public
|
||||
* @throws ConnectionFailureException
|
||||
* @param string $payload
|
||||
* @param string[] $headers Headers for this request
|
||||
* @return array
|
||||
*/
|
||||
public function execute($payload, array $headers = array())
|
||||
{
|
||||
if (is_callable($this->beforeRequest)) {
|
||||
call_user_func_array($this->beforeRequest, array($this, $payload, $headers));
|
||||
}
|
||||
|
||||
if ($this->isCurlLoaded()) {
|
||||
$ch = curl_init();
|
||||
$requestHeaders = $this->buildHeaders($headers);
|
||||
$headers = array();
|
||||
curl_setopt_array($ch, array(
|
||||
CURLOPT_URL => trim($this->url),
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_CONNECTTIMEOUT => $this->timeout,
|
||||
CURLOPT_MAXREDIRS => 2,
|
||||
CURLOPT_SSL_VERIFYPEER => $this->verifySslCertificate,
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => $payload,
|
||||
CURLOPT_HTTPHEADER => $requestHeaders,
|
||||
CURLOPT_HEADERFUNCTION => function ($curl, $header) use (&$headers) {
|
||||
$headers[] = $header;
|
||||
return strlen($header);
|
||||
}
|
||||
));
|
||||
if ($this->sslLocalCert !== null) {
|
||||
curl_setopt($ch, CURLOPT_CAINFO, $this->sslLocalCert);
|
||||
}
|
||||
$response = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
if ($response !== false) {
|
||||
$response = json_decode($response, true);
|
||||
} else {
|
||||
throw new ConnectionFailureException('Unable to establish a connection');
|
||||
}
|
||||
} else {
|
||||
$stream = fopen(trim($this->url), 'r', false, $this->buildContext($payload, $headers));
|
||||
|
||||
if (! is_resource($stream)) {
|
||||
throw new ConnectionFailureException('Unable to establish a connection');
|
||||
}
|
||||
|
||||
$metadata = stream_get_meta_data($stream);
|
||||
$headers = $metadata['wrapper_data'];
|
||||
$response = json_decode(stream_get_contents($stream), true);
|
||||
|
||||
fclose($stream);
|
||||
}
|
||||
|
||||
if ($this->debug) {
|
||||
error_log('==> Request: '.PHP_EOL.(is_string($payload) ? $payload : json_encode($payload, JSON_PRETTY_PRINT)));
|
||||
error_log('==> Headers: '.PHP_EOL.var_export($headers, true));
|
||||
error_log('==> Response: '.PHP_EOL.json_encode($response, JSON_PRETTY_PRINT));
|
||||
}
|
||||
|
||||
$this->handleExceptions($headers);
|
||||
$this->parseCookies($headers);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare stream context
|
||||
*
|
||||
* @access protected
|
||||
* @param string $payload
|
||||
* @param string[] $headers
|
||||
* @return resource
|
||||
*/
|
||||
protected function buildContext($payload, array $headers = array())
|
||||
{
|
||||
$headers = $this->buildHeaders($headers);
|
||||
|
||||
$options = array(
|
||||
'http' => array(
|
||||
'method' => 'POST',
|
||||
'protocol_version' => 1.1,
|
||||
'timeout' => $this->timeout,
|
||||
'max_redirects' => 2,
|
||||
'header' => implode("\r\n", $headers),
|
||||
'content' => $payload,
|
||||
'ignore_errors' => true,
|
||||
),
|
||||
'ssl' => array(
|
||||
'verify_peer' => $this->verifySslCertificate,
|
||||
'verify_peer_name' => $this->verifySslCertificate,
|
||||
)
|
||||
);
|
||||
|
||||
if ($this->sslLocalCert !== null) {
|
||||
$options['ssl']['local_cert'] = $this->sslLocalCert;
|
||||
}
|
||||
|
||||
return stream_context_create($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse cookies from response
|
||||
*
|
||||
* @access protected
|
||||
* @param array $headers
|
||||
*/
|
||||
protected function parseCookies(array $headers)
|
||||
{
|
||||
foreach ($headers as $header) {
|
||||
$pos = stripos($header, 'Set-Cookie:');
|
||||
|
||||
if ($pos !== false) {
|
||||
$cookies = explode(';', substr($header, $pos + 11));
|
||||
|
||||
foreach ($cookies as $cookie) {
|
||||
$item = explode('=', $cookie);
|
||||
|
||||
if (count($item) === 2) {
|
||||
$name = trim($item[0]);
|
||||
$value = $item[1];
|
||||
$this->cookies[$name] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw an exception according the HTTP response
|
||||
*
|
||||
* @access public
|
||||
* @param array $headers
|
||||
* @throws AccessDeniedException
|
||||
* @throws ServerErrorException
|
||||
*/
|
||||
public function handleExceptions(array $headers)
|
||||
{
|
||||
$exceptions = array(
|
||||
'401' => '\JsonRPC\Exception\AccessDeniedException',
|
||||
'403' => '\JsonRPC\Exception\AccessDeniedException',
|
||||
'404' => '\JsonRPC\Exception\ConnectionFailureException',
|
||||
'500' => '\JsonRPC\Exception\ServerErrorException',
|
||||
);
|
||||
|
||||
foreach ($headers as $header) {
|
||||
foreach ($exceptions as $code => $exception) {
|
||||
if (strpos($header, 'HTTP/1.0 '.$code) !== false || strpos($header, 'HTTP/1.1 '.$code) !== false) {
|
||||
throw new $exception('Response: '.$header);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the curl extension is loaded
|
||||
*
|
||||
* @access protected
|
||||
* @return bool
|
||||
*/
|
||||
protected function isCurlLoaded()
|
||||
{
|
||||
return extension_loaded('curl');
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare Headers
|
||||
*
|
||||
* @access protected
|
||||
* @param array $headers
|
||||
* @return array
|
||||
*/
|
||||
protected function buildHeaders(array $headers)
|
||||
{
|
||||
$headers = array_merge($this->headers, $headers);
|
||||
|
||||
if (!empty($this->username) && !empty($this->password)) {
|
||||
$headers[] = 'Authorization: Basic ' . base64_encode($this->username . ':' . $this->password);
|
||||
}
|
||||
|
||||
if (!empty($this->cookies)) {
|
||||
$cookies = array();
|
||||
|
||||
foreach ($this->cookies as $key => $value) {
|
||||
$cookies[] = $key . '=' . $value;
|
||||
}
|
||||
|
||||
$headers[] = 'Cookie: ' . implode('; ', $cookies);
|
||||
}
|
||||
return $headers;
|
||||
}
|
||||
}
|
||||
114
libs/jsonrpc/src/JsonRPC/MiddlewareHandler.php
Normal file
114
libs/jsonrpc/src/JsonRPC/MiddlewareHandler.php
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
namespace JsonRPC;
|
||||
|
||||
/**
|
||||
* Class MiddlewareHandler
|
||||
*
|
||||
* @package JsonRPC
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class MiddlewareHandler
|
||||
{
|
||||
/**
|
||||
* Procedure Name
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $procedureName = '';
|
||||
|
||||
/**
|
||||
* Username
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $username = '';
|
||||
|
||||
/**
|
||||
* Password
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $password = '';
|
||||
|
||||
/**
|
||||
* List of middleware to execute before to call the method
|
||||
*
|
||||
* @access protected
|
||||
* @var MiddlewareInterface[]
|
||||
*/
|
||||
protected $middleware = array();
|
||||
|
||||
/**
|
||||
* Set username
|
||||
*
|
||||
* @access public
|
||||
* @param string $username
|
||||
* @return $this
|
||||
*/
|
||||
public function withUsername($username)
|
||||
{
|
||||
if (! empty($username)) {
|
||||
$this->username = $username;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set password
|
||||
*
|
||||
* @access public
|
||||
* @param string $password
|
||||
* @return $this
|
||||
*/
|
||||
public function withPassword($password)
|
||||
{
|
||||
if (! empty($password)) {
|
||||
$this->password = $password;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set procedure name
|
||||
*
|
||||
* @access public
|
||||
* @param string $procedureName
|
||||
* @return $this
|
||||
*/
|
||||
public function withProcedure($procedureName)
|
||||
{
|
||||
$this->procedureName = $procedureName;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new middleware
|
||||
*
|
||||
* @access public
|
||||
* @param MiddlewareInterface $middleware
|
||||
* @return MiddlewareHandler
|
||||
*/
|
||||
public function withMiddleware(MiddlewareInterface $middleware)
|
||||
{
|
||||
$this->middleware[] = $middleware;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute all middleware
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
foreach ($this->middleware as $middleware) {
|
||||
$middleware->execute($this->username, $this->password, $this->procedureName);
|
||||
}
|
||||
}
|
||||
}
|
||||
27
libs/jsonrpc/src/JsonRPC/MiddlewareInterface.php
Normal file
27
libs/jsonrpc/src/JsonRPC/MiddlewareInterface.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace JsonRPC;
|
||||
|
||||
use JsonRPC\Exception\AccessDeniedException;
|
||||
use JsonRPC\Exception\AuthenticationFailureException;
|
||||
|
||||
/**
|
||||
* Interface MiddlewareInterface
|
||||
*
|
||||
* @package JsonRPC
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
interface MiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* Execute Middleware
|
||||
*
|
||||
* @access public
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
* @param string $procedureName
|
||||
* @throws AccessDeniedException
|
||||
* @throws AuthenticationFailureException
|
||||
*/
|
||||
public function execute($username, $password, $procedureName);
|
||||
}
|
||||
296
libs/jsonrpc/src/JsonRPC/ProcedureHandler.php
Normal file
296
libs/jsonrpc/src/JsonRPC/ProcedureHandler.php
Normal file
@@ -0,0 +1,296 @@
|
||||
<?php
|
||||
|
||||
namespace JsonRPC;
|
||||
|
||||
use BadFunctionCallException;
|
||||
use Closure;
|
||||
use InvalidArgumentException;
|
||||
use ReflectionFunction;
|
||||
use ReflectionMethod;
|
||||
|
||||
/**
|
||||
* Class ProcedureHandler
|
||||
*
|
||||
* @package JsonRPC
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class ProcedureHandler
|
||||
{
|
||||
/**
|
||||
* List of procedures
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $callbacks = array();
|
||||
|
||||
/**
|
||||
* List of classes
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $classes = array();
|
||||
|
||||
/**
|
||||
* List of instances
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $instances = array();
|
||||
|
||||
/**
|
||||
* Before method name to call
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $beforeMethodName = '';
|
||||
|
||||
/**
|
||||
* Register a new procedure
|
||||
*
|
||||
* @access public
|
||||
* @param string $procedure Procedure name
|
||||
* @param closure $callback Callback
|
||||
* @return $this
|
||||
*/
|
||||
public function withCallback($procedure, Closure $callback)
|
||||
{
|
||||
$this->callbacks[$procedure] = $callback;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind a procedure to a class
|
||||
*
|
||||
* @access public
|
||||
* @param string $procedure Procedure name
|
||||
* @param mixed $class Class name or instance
|
||||
* @param string $method Procedure name
|
||||
* @return $this
|
||||
*/
|
||||
public function withClassAndMethod($procedure, $class, $method = '')
|
||||
{
|
||||
if ($method === '') {
|
||||
$method = $procedure;
|
||||
}
|
||||
|
||||
$this->classes[$procedure] = array($class, $method);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind a class instance
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $instance
|
||||
* @return $this
|
||||
*/
|
||||
public function withObject($instance)
|
||||
{
|
||||
$this->instances[] = $instance;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a before method to call
|
||||
*
|
||||
* @access public
|
||||
* @param string $methodName
|
||||
* @return $this
|
||||
*/
|
||||
public function withBeforeMethod($methodName)
|
||||
{
|
||||
$this->beforeMethodName = $methodName;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register multiple procedures from array
|
||||
*
|
||||
* @access public
|
||||
* @param array $callbacks Array with procedure names (array keys) and callbacks (array values)
|
||||
* @return $this
|
||||
*/
|
||||
public function withCallbackArray($callbacks)
|
||||
{
|
||||
foreach ($callbacks as $procedure => $callback) {
|
||||
$this->withCallback($procedure, $callback);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind multiple procedures to classes from array
|
||||
*
|
||||
* @access public
|
||||
* @param array $callbacks Array with procedure names (array keys) and class and method names (array values)
|
||||
* @return $this
|
||||
*/
|
||||
public function withClassAndMethodArray($callbacks)
|
||||
{
|
||||
foreach ($callbacks as $procedure => $callback) {
|
||||
$this->withClassAndMethod($procedure, $callback[0], $callback[1]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the procedure
|
||||
*
|
||||
* @access public
|
||||
* @param string $procedure Procedure name
|
||||
* @param array $params Procedure params
|
||||
* @return mixed
|
||||
*/
|
||||
public function executeProcedure($procedure, array $params = array())
|
||||
{
|
||||
if (isset($this->callbacks[$procedure])) {
|
||||
return $this->executeCallback($this->callbacks[$procedure], $params);
|
||||
} elseif (isset($this->classes[$procedure]) && method_exists($this->classes[$procedure][0], $this->classes[$procedure][1])) {
|
||||
return $this->executeMethod($this->classes[$procedure][0], $this->classes[$procedure][1], $params);
|
||||
}
|
||||
|
||||
foreach ($this->instances as $instance) {
|
||||
if (method_exists($instance, $procedure)) {
|
||||
return $this->executeMethod($instance, $procedure, $params);
|
||||
}
|
||||
}
|
||||
|
||||
throw new BadFunctionCallException('Unable to find the procedure');
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a callback
|
||||
*
|
||||
* @access public
|
||||
* @param Closure $callback Callback
|
||||
* @param array $params Procedure params
|
||||
* @return mixed
|
||||
*/
|
||||
public function executeCallback(Closure $callback, $params)
|
||||
{
|
||||
$reflection = new ReflectionFunction($callback);
|
||||
|
||||
$arguments = $this->getArguments(
|
||||
$params,
|
||||
$reflection->getParameters(),
|
||||
$reflection->getNumberOfRequiredParameters(),
|
||||
$reflection->getNumberOfParameters()
|
||||
);
|
||||
|
||||
return $reflection->invokeArgs($arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a method
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $class Class name or instance
|
||||
* @param string $method Method name
|
||||
* @param array $params Procedure params
|
||||
* @return mixed
|
||||
*/
|
||||
public function executeMethod($class, $method, $params)
|
||||
{
|
||||
$instance = is_string($class) ? new $class : $class;
|
||||
$reflection = new ReflectionMethod($class, $method);
|
||||
|
||||
$this->executeBeforeMethod($instance, $method);
|
||||
|
||||
$arguments = $this->getArguments(
|
||||
$params,
|
||||
$reflection->getParameters(),
|
||||
$reflection->getNumberOfRequiredParameters(),
|
||||
$reflection->getNumberOfParameters()
|
||||
);
|
||||
|
||||
return $reflection->invokeArgs($instance, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute before method if defined
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $object
|
||||
* @param string $method
|
||||
*/
|
||||
public function executeBeforeMethod($object, $method)
|
||||
{
|
||||
if ($this->beforeMethodName !== '' && method_exists($object, $this->beforeMethodName)) {
|
||||
call_user_func_array(array($object, $this->beforeMethodName), array($method));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get procedure arguments
|
||||
*
|
||||
* @access public
|
||||
* @param array $requestParams Incoming arguments
|
||||
* @param array $methodParams Procedure arguments
|
||||
* @param integer $nbRequiredParams Number of required parameters
|
||||
* @param integer $nbMaxParams Maximum number of parameters
|
||||
* @return array
|
||||
*/
|
||||
public function getArguments(array $requestParams, array $methodParams, $nbRequiredParams, $nbMaxParams)
|
||||
{
|
||||
$nbParams = count($requestParams);
|
||||
|
||||
if ($nbParams < $nbRequiredParams) {
|
||||
throw new InvalidArgumentException('Wrong number of arguments');
|
||||
}
|
||||
|
||||
if ($nbParams > $nbMaxParams) {
|
||||
throw new InvalidArgumentException('Too many arguments');
|
||||
}
|
||||
|
||||
if ($this->isPositionalArguments($requestParams)) {
|
||||
return $requestParams;
|
||||
}
|
||||
|
||||
return $this->getNamedArguments($requestParams, $methodParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if we have positional parameters
|
||||
*
|
||||
* @access public
|
||||
* @param array $request_params Incoming arguments
|
||||
* @return bool
|
||||
*/
|
||||
public function isPositionalArguments(array $request_params)
|
||||
{
|
||||
return array_keys($request_params) === range(0, count($request_params) - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get named arguments
|
||||
*
|
||||
* @access public
|
||||
* @param array $requestParams Incoming arguments
|
||||
* @param array $methodParams Procedure arguments
|
||||
* @return array
|
||||
*/
|
||||
public function getNamedArguments(array $requestParams, array $methodParams)
|
||||
{
|
||||
$params = array();
|
||||
|
||||
foreach ($methodParams as $p) {
|
||||
$name = $p->getName();
|
||||
|
||||
if (array_key_exists($name, $requestParams)) {
|
||||
$params[$name] = $requestParams[$name];
|
||||
} elseif ($p->isDefaultValueAvailable()) {
|
||||
$params[$name] = $p->getDefaultValue();
|
||||
} else {
|
||||
throw new InvalidArgumentException('Missing argument: '.$name);
|
||||
}
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
}
|
||||
55
libs/jsonrpc/src/JsonRPC/Request/BatchRequestParser.php
Normal file
55
libs/jsonrpc/src/JsonRPC/Request/BatchRequestParser.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace JsonRPC\Request;
|
||||
|
||||
/**
|
||||
* Class BatchRequestParser
|
||||
*
|
||||
* @package JsonRPC\Request
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class BatchRequestParser extends RequestParser
|
||||
{
|
||||
/**
|
||||
* Parse incoming request
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function parse()
|
||||
{
|
||||
$responses = array();
|
||||
|
||||
foreach ($this->payload as $payload) {
|
||||
$responses[] = RequestParser::create()
|
||||
->withPayload($payload)
|
||||
->withProcedureHandler($this->procedureHandler)
|
||||
->withMiddlewareHandler($this->middlewareHandler)
|
||||
->withLocalException($this->localExceptions)
|
||||
->parse();
|
||||
}
|
||||
|
||||
$responses = array_filter($responses);
|
||||
return empty($responses) ? '' : '['.implode(',', $responses).']';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if we have a batch request
|
||||
*
|
||||
* ex : [
|
||||
* 0 => '...',
|
||||
* 1 => '...',
|
||||
* 2 => '...',
|
||||
* 3 => '...',
|
||||
* ]
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @param array $payload
|
||||
* @return bool
|
||||
*/
|
||||
public static function isBatchRequest(array $payload)
|
||||
{
|
||||
return array_keys($payload) === range(0, count($payload) - 1);
|
||||
}
|
||||
}
|
||||
129
libs/jsonrpc/src/JsonRPC/Request/RequestBuilder.php
Normal file
129
libs/jsonrpc/src/JsonRPC/Request/RequestBuilder.php
Normal file
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
namespace JsonRPC\Request;
|
||||
|
||||
/**
|
||||
* Class RequestBuilder
|
||||
*
|
||||
* @package JsonRPC\Request
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class RequestBuilder
|
||||
{
|
||||
/**
|
||||
* Request ID
|
||||
*
|
||||
* @access private
|
||||
* @var mixed
|
||||
*/
|
||||
private $id = null;
|
||||
|
||||
/**
|
||||
* Method name
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $procedure = '';
|
||||
|
||||
/**
|
||||
* Method arguments
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $params = array();
|
||||
|
||||
/**
|
||||
* Additional request attributes
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $reqattrs = array();
|
||||
|
||||
/**
|
||||
* Get new object instance
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @return RequestBuilder
|
||||
*/
|
||||
public static function create()
|
||||
{
|
||||
return new static();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set id
|
||||
*
|
||||
* @access public
|
||||
* @param null $id
|
||||
* @return RequestBuilder
|
||||
*/
|
||||
public function withId($id)
|
||||
{
|
||||
$this->id = $id;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set method
|
||||
*
|
||||
* @access public
|
||||
* @param string $procedure
|
||||
* @return RequestBuilder
|
||||
*/
|
||||
public function withProcedure($procedure)
|
||||
{
|
||||
$this->procedure = $procedure;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set parameters
|
||||
*
|
||||
* @access public
|
||||
* @param array $params
|
||||
* @return RequestBuilder
|
||||
*/
|
||||
public function withParams(array $params)
|
||||
{
|
||||
$this->params = $params;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set additional request attributes
|
||||
*
|
||||
* @access public
|
||||
* @param array $reqattrs
|
||||
* @return RequestBuilder
|
||||
*/
|
||||
public function withRequestAttributes(array $reqattrs)
|
||||
{
|
||||
$this->reqattrs = $reqattrs;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the payload
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
$payload = array_merge_recursive($this->reqattrs, array(
|
||||
'jsonrpc' => '2.0',
|
||||
'method' => $this->procedure,
|
||||
'id' => $this->id ?: mt_rand(),
|
||||
));
|
||||
|
||||
if (! empty($this->params)) {
|
||||
$payload['params'] = $this->params;
|
||||
}
|
||||
|
||||
return json_encode($payload);
|
||||
}
|
||||
}
|
||||
200
libs/jsonrpc/src/JsonRPC/Request/RequestParser.php
Normal file
200
libs/jsonrpc/src/JsonRPC/Request/RequestParser.php
Normal file
@@ -0,0 +1,200 @@
|
||||
<?php
|
||||
|
||||
namespace JsonRPC\Request;
|
||||
|
||||
use Exception;
|
||||
use JsonRPC\Exception\AccessDeniedException;
|
||||
use JsonRPC\Exception\AuthenticationFailureException;
|
||||
use JsonRPC\Exception\InvalidJsonRpcFormatException;
|
||||
use JsonRPC\MiddlewareHandler;
|
||||
use JsonRPC\ProcedureHandler;
|
||||
use JsonRPC\Response\ResponseBuilder;
|
||||
use JsonRPC\Validator\JsonFormatValidator;
|
||||
use JsonRPC\Validator\RpcFormatValidator;
|
||||
|
||||
/**
|
||||
* Class RequestParser
|
||||
*
|
||||
* @package JsonRPC
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class RequestParser
|
||||
{
|
||||
/**
|
||||
* Request payload
|
||||
*
|
||||
* @access protected
|
||||
* @var mixed
|
||||
*/
|
||||
protected $payload;
|
||||
|
||||
/**
|
||||
* List of exceptions that should not be relayed to the client
|
||||
*
|
||||
* @access protected
|
||||
* @var array()
|
||||
*/
|
||||
protected $localExceptions = array(
|
||||
'JsonRPC\Exception\AuthenticationFailureException',
|
||||
'JsonRPC\Exception\AccessDeniedException',
|
||||
);
|
||||
|
||||
/**
|
||||
* ProcedureHandler
|
||||
*
|
||||
* @access protected
|
||||
* @var ProcedureHandler
|
||||
*/
|
||||
protected $procedureHandler;
|
||||
|
||||
/**
|
||||
* MiddlewareHandler
|
||||
*
|
||||
* @access protected
|
||||
* @var MiddlewareHandler
|
||||
*/
|
||||
protected $middlewareHandler;
|
||||
|
||||
/**
|
||||
* Get new object instance
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @return RequestParser
|
||||
*/
|
||||
public static function create()
|
||||
{
|
||||
return new static();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set payload
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $payload
|
||||
* @return $this
|
||||
*/
|
||||
public function withPayload($payload)
|
||||
{
|
||||
$this->payload = $payload;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception classes that should not be relayed to the client
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $exception
|
||||
* @return $this
|
||||
*/
|
||||
public function withLocalException($exception)
|
||||
{
|
||||
if (is_array($exception)) {
|
||||
$this->localExceptions = array_merge($this->localExceptions, $exception);
|
||||
} else {
|
||||
$this->localExceptions[] = $exception;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set procedure handler
|
||||
*
|
||||
* @access public
|
||||
* @param ProcedureHandler $procedureHandler
|
||||
* @return $this
|
||||
*/
|
||||
public function withProcedureHandler(ProcedureHandler $procedureHandler)
|
||||
{
|
||||
$this->procedureHandler = $procedureHandler;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set middleware handler
|
||||
*
|
||||
* @access public
|
||||
* @param MiddlewareHandler $middlewareHandler
|
||||
* @return $this
|
||||
*/
|
||||
public function withMiddlewareHandler(MiddlewareHandler $middlewareHandler)
|
||||
{
|
||||
$this->middlewareHandler = $middlewareHandler;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse incoming request
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
* @throws AccessDeniedException
|
||||
* @throws AuthenticationFailureException
|
||||
*/
|
||||
public function parse()
|
||||
{
|
||||
try {
|
||||
|
||||
JsonFormatValidator::validate($this->payload);
|
||||
RpcFormatValidator::validate($this->payload);
|
||||
|
||||
$this->middlewareHandler
|
||||
->withProcedure($this->payload['method'])
|
||||
->execute();
|
||||
|
||||
$result = $this->procedureHandler->executeProcedure(
|
||||
$this->payload['method'],
|
||||
empty($this->payload['params']) ? array() : $this->payload['params']
|
||||
);
|
||||
|
||||
if (! $this->isNotification()) {
|
||||
return ResponseBuilder::create()
|
||||
->withId($this->payload['id'])
|
||||
->withResult($result)
|
||||
->build();
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return $this->handleExceptions($e);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle exceptions
|
||||
*
|
||||
* @access protected
|
||||
* @param Exception $e
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function handleExceptions(Exception $e)
|
||||
{
|
||||
foreach ($this->localExceptions as $exception) {
|
||||
if ($e instanceof $exception) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
if ($e instanceof InvalidJsonRpcFormatException || ! $this->isNotification()) {
|
||||
return ResponseBuilder::create()
|
||||
->withId(isset($this->payload['id']) ? $this->payload['id'] : null)
|
||||
->withException($e)
|
||||
->build();
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the message is a notification
|
||||
*
|
||||
* @access protected
|
||||
* @return bool
|
||||
*/
|
||||
protected function isNotification()
|
||||
{
|
||||
return is_array($this->payload) && !isset($this->payload['id']);
|
||||
}
|
||||
}
|
||||
336
libs/jsonrpc/src/JsonRPC/Response/ResponseBuilder.php
Normal file
336
libs/jsonrpc/src/JsonRPC/Response/ResponseBuilder.php
Normal file
@@ -0,0 +1,336 @@
|
||||
<?php
|
||||
|
||||
namespace JsonRPC\Response;
|
||||
|
||||
use BadFunctionCallException;
|
||||
use Exception;
|
||||
use InvalidArgumentException;
|
||||
use JsonRPC\Exception\AccessDeniedException;
|
||||
use JsonRPC\Exception\AuthenticationFailureException;
|
||||
use JsonRPC\Exception\InvalidJsonFormatException;
|
||||
use JsonRPC\Exception\InvalidJsonRpcFormatException;
|
||||
use JsonRPC\Exception\ResponseEncodingFailureException;
|
||||
use JsonRPC\Exception\ResponseException;
|
||||
use JsonRPC\Validator\JsonEncodingValidator;
|
||||
|
||||
/**
|
||||
* Class ResponseBuilder
|
||||
*
|
||||
* @package JsonRPC
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class ResponseBuilder
|
||||
{
|
||||
/**
|
||||
* Payload ID
|
||||
*
|
||||
* @access protected
|
||||
* @var mixed
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* Payload ID
|
||||
*
|
||||
* @access protected
|
||||
* @var mixed
|
||||
*/
|
||||
protected $result;
|
||||
|
||||
/**
|
||||
* Payload error code
|
||||
*
|
||||
* @access protected
|
||||
* @var integer
|
||||
*/
|
||||
protected $errorCode;
|
||||
|
||||
/**
|
||||
* Payload error message
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
protected $errorMessage;
|
||||
|
||||
/**
|
||||
* Payload error data
|
||||
*
|
||||
* @access protected
|
||||
* @var mixed
|
||||
*/
|
||||
protected $errorData;
|
||||
|
||||
/**
|
||||
* HTTP Headers
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $headers = array(
|
||||
'Content-Type' => 'application/json',
|
||||
);
|
||||
|
||||
/**
|
||||
* HTTP status
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $status;
|
||||
|
||||
/**
|
||||
* Exception
|
||||
*
|
||||
* @access protected
|
||||
* @var ResponseException
|
||||
*/
|
||||
protected $exception;
|
||||
|
||||
/**
|
||||
* Get new object instance
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @return ResponseBuilder
|
||||
*/
|
||||
public static function create()
|
||||
{
|
||||
return new static();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set id
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $id
|
||||
* @return $this
|
||||
*/
|
||||
public function withId($id)
|
||||
{
|
||||
$this->id = $id;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set result
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $result
|
||||
* @return $this
|
||||
*/
|
||||
public function withResult($result)
|
||||
{
|
||||
$this->result = $result;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set error
|
||||
*
|
||||
* @access public
|
||||
* @param integer $code
|
||||
* @param string $message
|
||||
* @param string $data
|
||||
* @return $this
|
||||
*/
|
||||
public function withError($code, $message, $data = '')
|
||||
{
|
||||
$this->errorCode = $code;
|
||||
$this->errorMessage = $message;
|
||||
$this->errorData = $data;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set exception
|
||||
*
|
||||
* @access public
|
||||
* @param Exception $exception
|
||||
* @return $this
|
||||
*/
|
||||
public function withException(Exception $exception)
|
||||
{
|
||||
$this->exception = $exception;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add HTTP header
|
||||
*
|
||||
* @access public
|
||||
* @param string $name
|
||||
* @param string $value
|
||||
* @return $this
|
||||
*/
|
||||
public function withHeader($name, $value)
|
||||
{
|
||||
$this->headers[$name] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add HTTP Status
|
||||
*
|
||||
* @access public
|
||||
* @param string $status
|
||||
* @return $this
|
||||
*/
|
||||
public function withStatus($status)
|
||||
{
|
||||
$this->status = $status;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get status
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getStatus()
|
||||
{
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get headers
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getHeaders()
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build response
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
$options = 0;
|
||||
if (defined('JSON_UNESCAPED_SLASHES')) {
|
||||
$options |= JSON_UNESCAPED_SLASHES;
|
||||
}
|
||||
if (defined('JSON_UNESCAPED_UNICODE')) {
|
||||
$options |= JSON_UNESCAPED_UNICODE;
|
||||
}
|
||||
$encodedResponse = json_encode($this->buildResponse(), $options);
|
||||
JsonEncodingValidator::validate();
|
||||
|
||||
return $encodedResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send HTTP headers
|
||||
*
|
||||
* @access public
|
||||
* @return $this
|
||||
*/
|
||||
public function sendHeaders()
|
||||
{
|
||||
if (! empty($this->status)) {
|
||||
header($this->status);
|
||||
}
|
||||
|
||||
foreach ($this->headers as $name => $value) {
|
||||
header($name.': '.$value);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build response payload
|
||||
*
|
||||
* @access protected
|
||||
* @return array
|
||||
*/
|
||||
protected function buildResponse()
|
||||
{
|
||||
$response = array('jsonrpc' => '2.0');
|
||||
$this->handleExceptions();
|
||||
|
||||
if (! empty($this->errorMessage)) {
|
||||
$response['error'] = $this->buildErrorResponse();
|
||||
} else {
|
||||
$response['result'] = $this->result;
|
||||
}
|
||||
|
||||
$response['id'] = $this->id;
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build response error payload
|
||||
*
|
||||
* @access protected
|
||||
* @return array
|
||||
*/
|
||||
protected function buildErrorResponse()
|
||||
{
|
||||
$response = array(
|
||||
'code' => $this->errorCode,
|
||||
'message' => $this->errorMessage,
|
||||
);
|
||||
|
||||
if (! empty($this->errorData)) {
|
||||
$response['data'] = $this->errorData;
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform exceptions to JSON-RPC errors
|
||||
*
|
||||
* @access protected
|
||||
*/
|
||||
protected function handleExceptions()
|
||||
{
|
||||
try {
|
||||
if ($this->exception instanceof Exception) {
|
||||
throw $this->exception;
|
||||
}
|
||||
} catch (InvalidJsonFormatException $e) {
|
||||
$this->errorCode = -32700;
|
||||
$this->errorMessage = 'Parse error';
|
||||
$this->id = null;
|
||||
} catch (InvalidJsonRpcFormatException $e) {
|
||||
$this->errorCode = -32600;
|
||||
$this->errorMessage = 'Invalid Request';
|
||||
$this->id = null;
|
||||
} catch (BadFunctionCallException $e) {
|
||||
$this->errorCode = -32601;
|
||||
$this->errorMessage = 'Method not found';
|
||||
} catch (InvalidArgumentException $e) {
|
||||
$this->errorCode = -32602;
|
||||
$this->errorMessage = 'Invalid params';
|
||||
$this->errorData = $this->exception->getMessage();
|
||||
} catch (ResponseEncodingFailureException $e) {
|
||||
$this->errorCode = -32603;
|
||||
$this->errorMessage = 'Internal error';
|
||||
$this->errorData = $this->exception->getMessage();
|
||||
} catch (AuthenticationFailureException $e) {
|
||||
$this->errorCode = 401;
|
||||
$this->errorMessage = 'Unauthorized';
|
||||
$this->status = 'HTTP/1.0 401 Unauthorized';
|
||||
$this->withHeader('WWW-Authenticate', 'Basic realm="JsonRPC"');
|
||||
} catch (AccessDeniedException $e) {
|
||||
$this->errorCode = 403;
|
||||
$this->errorMessage = 'Forbidden';
|
||||
$this->status = 'HTTP/1.0 403 Forbidden';
|
||||
} catch (ResponseException $e) {
|
||||
$this->errorCode = $this->exception->getCode();
|
||||
$this->errorMessage = $this->exception->getMessage();
|
||||
$this->errorData = $this->exception->getData();
|
||||
} catch (Exception $e) {
|
||||
$this->errorCode = $this->exception->getCode();
|
||||
$this->errorMessage = $this->exception->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
154
libs/jsonrpc/src/JsonRPC/Response/ResponseParser.php
Normal file
154
libs/jsonrpc/src/JsonRPC/Response/ResponseParser.php
Normal file
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
namespace JsonRPC\Response;
|
||||
|
||||
use BadFunctionCallException;
|
||||
use InvalidArgumentException;
|
||||
use Exception;
|
||||
use JsonRPC\Exception\InvalidJsonFormatException;
|
||||
use JsonRPC\Exception\InvalidJsonRpcFormatException;
|
||||
use JsonRPC\Exception\ResponseException;
|
||||
use JsonRPC\Validator\JsonFormatValidator;
|
||||
|
||||
/**
|
||||
* Class ResponseParser
|
||||
*
|
||||
* @package JsonRPC\Request
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class ResponseParser
|
||||
{
|
||||
/**
|
||||
* Payload
|
||||
*
|
||||
* @access private
|
||||
* @var mixed
|
||||
*/
|
||||
private $payload;
|
||||
|
||||
/**
|
||||
* Do not immediately throw an exception on error. Return it instead.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $returnException = false;
|
||||
|
||||
/**
|
||||
* Get new object instance
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @return ResponseParser
|
||||
*/
|
||||
public static function create()
|
||||
{
|
||||
return new static();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Return Exception Or Throw It
|
||||
*
|
||||
* @param $returnException
|
||||
* @return ResponseParser
|
||||
*/
|
||||
public function withReturnException($returnException)
|
||||
{
|
||||
$this->returnException = $returnException;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set payload
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $payload
|
||||
* @return $this
|
||||
*/
|
||||
public function withPayload($payload)
|
||||
{
|
||||
$this->payload = $payload;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse response
|
||||
*
|
||||
* @return array|Exception|null
|
||||
* @throws InvalidJsonFormatException
|
||||
* @throws BadFunctionCallException
|
||||
* @throws InvalidJsonRpcFormatException
|
||||
* @throws InvalidArgumentException
|
||||
* @throws Exception
|
||||
* @throws ResponseException
|
||||
*/
|
||||
public function parse()
|
||||
{
|
||||
JsonFormatValidator::validate($this->payload);
|
||||
|
||||
if ($this->isBatchResponse()) {
|
||||
$results = array();
|
||||
|
||||
foreach ($this->payload as $response) {
|
||||
$results[] = self::create()
|
||||
->withReturnException($this->returnException)
|
||||
->withPayload($response)
|
||||
->parse();
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
if (isset($this->payload['error']['code'])) {
|
||||
try {
|
||||
$this->handleExceptions();
|
||||
} catch (Exception $e) {
|
||||
if ($this->returnException) {
|
||||
return $e;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
return isset($this->payload['result']) ? $this->payload['result'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle exceptions
|
||||
*
|
||||
* @access private
|
||||
* @throws InvalidJsonFormatException
|
||||
* @throws InvalidJsonRpcFormatException
|
||||
* @throws ResponseException
|
||||
*/
|
||||
private function handleExceptions()
|
||||
{
|
||||
switch ($this->payload['error']['code']) {
|
||||
case -32700:
|
||||
throw new InvalidJsonFormatException('Parse error: '.$this->payload['error']['message']);
|
||||
case -32600:
|
||||
throw new InvalidJsonRpcFormatException('Invalid Request: '.$this->payload['error']['message']);
|
||||
case -32601:
|
||||
throw new BadFunctionCallException('Procedure not found: '.$this->payload['error']['message']);
|
||||
case -32602:
|
||||
throw new InvalidArgumentException('Invalid arguments: '.$this->payload['error']['message']);
|
||||
default:
|
||||
throw new ResponseException(
|
||||
$this->payload['error']['message'],
|
||||
$this->payload['error']['code'],
|
||||
null,
|
||||
isset($this->payload['error']['data']) ? $this->payload['error']['data'] : null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if we have a batch response
|
||||
*
|
||||
* @access private
|
||||
* @return boolean
|
||||
*/
|
||||
private function isBatchResponse()
|
||||
{
|
||||
return array_keys($this->payload) === range(0, count($this->payload) - 1);
|
||||
}
|
||||
}
|
||||
386
libs/jsonrpc/src/JsonRPC/Server.php
Normal file
386
libs/jsonrpc/src/JsonRPC/Server.php
Normal file
@@ -0,0 +1,386 @@
|
||||
<?php
|
||||
|
||||
namespace JsonRPC;
|
||||
|
||||
use Closure;
|
||||
use Exception;
|
||||
use JsonRPC\Request\BatchRequestParser;
|
||||
use JsonRPC\Request\RequestParser;
|
||||
use JsonRPC\Response\ResponseBuilder;
|
||||
use JsonRPC\Validator\HostValidator;
|
||||
use JsonRPC\Validator\JsonFormatValidator;
|
||||
use JsonRPC\Validator\UserValidator;
|
||||
|
||||
/**
|
||||
* JsonRPC server class
|
||||
*
|
||||
* @package JsonRPC
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Server
|
||||
{
|
||||
/**
|
||||
* Allowed hosts
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $hosts = array();
|
||||
|
||||
/**
|
||||
* Data received from the client
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $payload = array();
|
||||
|
||||
/**
|
||||
* List of exceptions that should not be relayed to the client
|
||||
*
|
||||
* @access protected
|
||||
* @var array()
|
||||
*/
|
||||
protected $localExceptions = array();
|
||||
|
||||
/**
|
||||
* Username
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $username = '';
|
||||
|
||||
/**
|
||||
* Password
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $password = '';
|
||||
|
||||
/**
|
||||
* Allowed users
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $users = array();
|
||||
|
||||
/**
|
||||
* $_SERVER
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $serverVariable;
|
||||
|
||||
/**
|
||||
* ProcedureHandler object
|
||||
*
|
||||
* @access protected
|
||||
* @var ProcedureHandler
|
||||
*/
|
||||
protected $procedureHandler;
|
||||
|
||||
/**
|
||||
* MiddlewareHandler object
|
||||
*
|
||||
* @access protected
|
||||
* @var MiddlewareHandler
|
||||
*/
|
||||
protected $middlewareHandler;
|
||||
|
||||
/**
|
||||
* Response builder
|
||||
*
|
||||
* @access protected
|
||||
* @var ResponseBuilder
|
||||
*/
|
||||
protected $responseBuilder;
|
||||
|
||||
/**
|
||||
* Response builder
|
||||
*
|
||||
* @access protected
|
||||
* @var RequestParser
|
||||
*/
|
||||
protected $requestParser;
|
||||
|
||||
/**
|
||||
*
|
||||
* Batch request parser
|
||||
*
|
||||
* @access protected
|
||||
* @var BatchRequestParser
|
||||
*/
|
||||
protected $batchRequestParser;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param string $request
|
||||
* @param array $server
|
||||
* @param ResponseBuilder $responseBuilder
|
||||
* @param RequestParser $requestParser
|
||||
* @param BatchRequestParser $batchRequestParser
|
||||
* @param ProcedureHandler $procedureHandler
|
||||
* @param MiddlewareHandler $middlewareHandler
|
||||
*/
|
||||
public function __construct(
|
||||
$request = '',
|
||||
array $server = array(),
|
||||
ResponseBuilder $responseBuilder = null,
|
||||
RequestParser $requestParser = null,
|
||||
BatchRequestParser $batchRequestParser = null,
|
||||
ProcedureHandler $procedureHandler = null,
|
||||
MiddlewareHandler $middlewareHandler = null
|
||||
) {
|
||||
if ($request !== '') {
|
||||
$this->payload = json_decode($request, true);
|
||||
} else {
|
||||
$this->payload = json_decode(file_get_contents('php://input'), true);
|
||||
}
|
||||
|
||||
$this->serverVariable = $server ?: $_SERVER;
|
||||
$this->responseBuilder = $responseBuilder ?: ResponseBuilder::create();
|
||||
$this->requestParser = $requestParser ?: RequestParser::create();
|
||||
$this->batchRequestParser = $batchRequestParser ?: BatchRequestParser::create();
|
||||
$this->procedureHandler = $procedureHandler ?: new ProcedureHandler();
|
||||
$this->middlewareHandler = $middlewareHandler ?: new MiddlewareHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define alternative authentication header
|
||||
*
|
||||
* @access public
|
||||
* @param string $header Header name
|
||||
* @return $this
|
||||
*/
|
||||
public function setAuthenticationHeader($header)
|
||||
{
|
||||
if (! empty($header)) {
|
||||
$header = 'HTTP_'.str_replace('-', '_', strtoupper($header));
|
||||
$value = $this->getServerVariable($header);
|
||||
|
||||
if (! empty($value)) {
|
||||
list($this->username, $this->password) = explode(':', base64_decode($value));
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ProcedureHandler
|
||||
*
|
||||
* @access public
|
||||
* @return ProcedureHandler
|
||||
*/
|
||||
public function getProcedureHandler()
|
||||
{
|
||||
return $this->procedureHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get MiddlewareHandler
|
||||
*
|
||||
* @access public
|
||||
* @return MiddlewareHandler
|
||||
*/
|
||||
public function getMiddlewareHandler()
|
||||
{
|
||||
return $this->middlewareHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get username
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getUsername()
|
||||
{
|
||||
return $this->username ?: $this->getServerVariable('PHP_AUTH_USER');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get password
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getPassword()
|
||||
{
|
||||
return $this->password ?: $this->getServerVariable('PHP_AUTH_PW');
|
||||
}
|
||||
|
||||
/**
|
||||
* IP based client restrictions
|
||||
*
|
||||
* @access public
|
||||
* @param array $hosts List of hosts
|
||||
* @return $this
|
||||
*/
|
||||
public function allowHosts(array $hosts)
|
||||
{
|
||||
$this->hosts = $hosts;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP Basic authentication
|
||||
*
|
||||
* @access public
|
||||
* @param array $users Dictionary of username/password
|
||||
* @return $this
|
||||
*/
|
||||
public function authentication(array $users)
|
||||
{
|
||||
$this->users = $users;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new procedure
|
||||
*
|
||||
* @access public
|
||||
* @deprecated Use $server->getProcedureHandler()->withCallback($procedure, $callback)
|
||||
* @param string $procedure Procedure name
|
||||
* @param closure $callback Callback
|
||||
* @return $this
|
||||
*/
|
||||
public function register($procedure, Closure $callback)
|
||||
{
|
||||
$this->procedureHandler->withCallback($procedure, $callback);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind a procedure to a class
|
||||
*
|
||||
* @access public
|
||||
* @deprecated Use $server->getProcedureHandler()->withClassAndMethod($procedure, $class, $method);
|
||||
* @param string $procedure Procedure name
|
||||
* @param mixed $class Class name or instance
|
||||
* @param string $method Procedure name
|
||||
* @return $this
|
||||
*/
|
||||
public function bind($procedure, $class, $method = '')
|
||||
{
|
||||
$this->procedureHandler->withClassAndMethod($procedure, $class, $method);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind a class instance
|
||||
*
|
||||
* @access public
|
||||
* @deprecated Use $server->getProcedureHandler()->withObject($instance);
|
||||
* @param mixed $instance Instance name
|
||||
* @return $this
|
||||
*/
|
||||
public function attach($instance)
|
||||
{
|
||||
$this->procedureHandler->withObject($instance);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception classes that should not be relayed to the client
|
||||
*
|
||||
* @access public
|
||||
* @param Exception|string $exception
|
||||
* @return $this
|
||||
*/
|
||||
public function withLocalException($exception)
|
||||
{
|
||||
$this->localExceptions[] = $exception;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse incoming requests
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
try {
|
||||
JsonFormatValidator::validate($this->payload);
|
||||
HostValidator::validate($this->hosts, $this->getServerVariable('REMOTE_ADDR'));
|
||||
UserValidator::validate($this->users, $this->getUsername(), $this->getPassword());
|
||||
|
||||
$this->middlewareHandler
|
||||
->withUsername($this->getUsername())
|
||||
->withPassword($this->getPassword())
|
||||
;
|
||||
|
||||
$response = $this->parseRequest();
|
||||
|
||||
} catch (Exception $e) {
|
||||
$response = $this->handleExceptions($e);
|
||||
}
|
||||
|
||||
$this->responseBuilder->sendHeaders();
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle exceptions
|
||||
*
|
||||
* @access protected
|
||||
* @param Exception $e
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function handleExceptions(Exception $e)
|
||||
{
|
||||
foreach ($this->localExceptions as $exception) {
|
||||
if ($e instanceof $exception) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->responseBuilder->withException($e)->build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse incoming request
|
||||
*
|
||||
* @access protected
|
||||
* @return string
|
||||
*/
|
||||
protected function parseRequest()
|
||||
{
|
||||
if (BatchRequestParser::isBatchRequest($this->payload)) {
|
||||
return $this->batchRequestParser
|
||||
->withPayload($this->payload)
|
||||
->withProcedureHandler($this->procedureHandler)
|
||||
->withMiddlewareHandler($this->middlewareHandler)
|
||||
->withLocalException($this->localExceptions)
|
||||
->parse();
|
||||
}
|
||||
|
||||
return $this->requestParser
|
||||
->withPayload($this->payload)
|
||||
->withProcedureHandler($this->procedureHandler)
|
||||
->withMiddlewareHandler($this->middlewareHandler)
|
||||
->withLocalException($this->localExceptions)
|
||||
->parse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check existence and get value of server variable
|
||||
*
|
||||
* @access protected
|
||||
* @param string $variable
|
||||
* @return string|null
|
||||
*/
|
||||
protected function getServerVariable($variable)
|
||||
{
|
||||
return isset($this->serverVariable[$variable]) ? $this->serverVariable[$variable] : null;
|
||||
}
|
||||
}
|
||||
73
libs/jsonrpc/src/JsonRPC/Validator/HostValidator.php
Normal file
73
libs/jsonrpc/src/JsonRPC/Validator/HostValidator.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace JsonRPC\Validator;
|
||||
|
||||
use JsonRPC\Exception\AccessDeniedException;
|
||||
|
||||
/**
|
||||
* Class HostValidator
|
||||
*
|
||||
* @package JsonRPC\Validator
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class HostValidator
|
||||
{
|
||||
/**
|
||||
* Validate
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @param array $hosts
|
||||
* @param string $remoteAddress
|
||||
* @throws AccessDeniedException
|
||||
*/
|
||||
public static function validate(array $hosts, $remoteAddress)
|
||||
{
|
||||
if (!empty($hosts)) {
|
||||
foreach ($hosts as $host) {
|
||||
if (self::ipMatch($remoteAddress, $host)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new AccessDeniedException('Access Forbidden');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate remoteAddress match host
|
||||
* @param $remoteAddress
|
||||
* @param $host
|
||||
* @return bool
|
||||
*/
|
||||
public static function ipMatch($remoteAddress, $host)
|
||||
{
|
||||
$host = trim($host);
|
||||
if (strpos($host, '/') !== false) {
|
||||
list($network, $mask) = explode('/', $host);
|
||||
if (self::netMatch($remoteAddress, $network, $mask)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($host === $remoteAddress) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* validate the ipAddress in network
|
||||
* 192.168.1.1/24
|
||||
* @param $clientIp
|
||||
* @param $networkIp
|
||||
* @param $mask
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function netMatch($clientIp, $networkIp, $mask)
|
||||
{
|
||||
$mask1 = 32 - $mask;
|
||||
return ((ip2long($clientIp) >> $mask1) == (ip2long($networkIp) >> $mask1));
|
||||
}
|
||||
}
|
||||
44
libs/jsonrpc/src/JsonRPC/Validator/JsonEncodingValidator.php
Normal file
44
libs/jsonrpc/src/JsonRPC/Validator/JsonEncodingValidator.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace JsonRPC\Validator;
|
||||
|
||||
use JsonRPC\Exception\ResponseEncodingFailureException;
|
||||
|
||||
/**
|
||||
* Class JsonEncodingValidator
|
||||
*
|
||||
* @package JsonRPC\Validator
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class JsonEncodingValidator
|
||||
{
|
||||
public static function validate()
|
||||
{
|
||||
$jsonError = json_last_error();
|
||||
|
||||
if ($jsonError !== JSON_ERROR_NONE) {
|
||||
switch ($jsonError) {
|
||||
case JSON_ERROR_DEPTH:
|
||||
$errorMessage = 'Maximum stack depth exceeded';
|
||||
break;
|
||||
case JSON_ERROR_STATE_MISMATCH:
|
||||
$errorMessage = 'Underflow or the modes mismatch';
|
||||
break;
|
||||
case JSON_ERROR_CTRL_CHAR:
|
||||
$errorMessage = 'Unexpected control character found';
|
||||
break;
|
||||
case JSON_ERROR_SYNTAX:
|
||||
$errorMessage = 'Syntax error, malformed JSON';
|
||||
break;
|
||||
case JSON_ERROR_UTF8:
|
||||
$errorMessage = 'Malformed UTF-8 characters, possibly incorrectly encoded';
|
||||
break;
|
||||
default:
|
||||
$errorMessage = 'Unknown error';
|
||||
break;
|
||||
}
|
||||
|
||||
throw new ResponseEncodingFailureException($errorMessage, $jsonError);
|
||||
}
|
||||
}
|
||||
}
|
||||
30
libs/jsonrpc/src/JsonRPC/Validator/JsonFormatValidator.php
Normal file
30
libs/jsonrpc/src/JsonRPC/Validator/JsonFormatValidator.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace JsonRPC\Validator;
|
||||
|
||||
use JsonRPC\Exception\InvalidJsonFormatException;
|
||||
|
||||
/**
|
||||
* Class JsonFormatValidator
|
||||
*
|
||||
* @package JsonRPC\Validator
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class JsonFormatValidator
|
||||
{
|
||||
/**
|
||||
* Validate
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @param mixed $payload
|
||||
* @throws InvalidJsonFormatException
|
||||
*/
|
||||
public static function validate($payload)
|
||||
{
|
||||
if (! is_array($payload)) {
|
||||
throw new InvalidJsonFormatException('Malformed payload');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
35
libs/jsonrpc/src/JsonRPC/Validator/RpcFormatValidator.php
Normal file
35
libs/jsonrpc/src/JsonRPC/Validator/RpcFormatValidator.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace JsonRPC\Validator;
|
||||
|
||||
use JsonRPC\Exception\InvalidJsonRpcFormatException;
|
||||
|
||||
/**
|
||||
* Class RpcFormatValidator
|
||||
*
|
||||
* @package JsonRPC\Validator
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class RpcFormatValidator
|
||||
{
|
||||
/**
|
||||
* Validate
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @param array $payload
|
||||
* @throws InvalidJsonRpcFormatException
|
||||
*/
|
||||
public static function validate(array $payload)
|
||||
{
|
||||
if (! isset($payload['jsonrpc']) ||
|
||||
! isset($payload['method']) ||
|
||||
! is_string($payload['method']) ||
|
||||
$payload['jsonrpc'] !== '2.0' ||
|
||||
(isset($payload['params']) && ! is_array($payload['params']))) {
|
||||
|
||||
throw new InvalidJsonRpcFormatException('Invalid JSON RPC payload');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
21
libs/jsonrpc/src/JsonRPC/Validator/UserValidator.php
Normal file
21
libs/jsonrpc/src/JsonRPC/Validator/UserValidator.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace JsonRPC\Validator;
|
||||
|
||||
use JsonRPC\Exception\AuthenticationFailureException;
|
||||
|
||||
/**
|
||||
* Class UserValidator
|
||||
*
|
||||
* @package JsonRPC\Validator
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class UserValidator
|
||||
{
|
||||
public static function validate(array $users, $username, $password)
|
||||
{
|
||||
if (! empty($users) && (! isset($users[$username]) || $users[$username] !== $password)) {
|
||||
throw new AuthenticationFailureException('Access not allowed');
|
||||
}
|
||||
}
|
||||
}
|
||||
103
libs/jsonrpc/tests/ClientTest.php
Normal file
103
libs/jsonrpc/tests/ClientTest.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
use JsonRPC\Client;
|
||||
|
||||
require_once __DIR__.'/../../../vendor/autoload.php';
|
||||
|
||||
class ClientTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
private $httpClient;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->httpClient = $this
|
||||
->getMockBuilder('\JsonRPC\HttpClient')
|
||||
->setMethods(array('execute'))
|
||||
->getMock();
|
||||
}
|
||||
|
||||
public function testSendBatch()
|
||||
{
|
||||
$client = new Client('', false, $this->httpClient);
|
||||
$response = array(
|
||||
array(
|
||||
'jsonrpc' => '2.0',
|
||||
'result' => 'c',
|
||||
'id' => 1,
|
||||
),
|
||||
array(
|
||||
'jsonrpc' => '2.0',
|
||||
'result' => 'd',
|
||||
'id' => 2,
|
||||
)
|
||||
);
|
||||
|
||||
$this->httpClient
|
||||
->expects($this->once())
|
||||
->method('execute')
|
||||
->with($this->stringContains('[{"jsonrpc":"2.0","method":"methodA","id":'))
|
||||
->will($this->returnValue($response));
|
||||
|
||||
|
||||
$result = $client->batch()
|
||||
->execute('methodA', array('a' => 'b'))
|
||||
->execute('methodB', array('a' => 'b'))
|
||||
->send();
|
||||
|
||||
$this->assertEquals(array('c', 'd'), $result);
|
||||
}
|
||||
|
||||
public function testSendRequest()
|
||||
{
|
||||
$client = new Client('', false, $this->httpClient);
|
||||
|
||||
$this->httpClient
|
||||
->expects($this->once())
|
||||
->method('execute')
|
||||
->with($this->stringContains('{"jsonrpc":"2.0","method":"methodA","id":'))
|
||||
->will($this->returnValue(array('jsonrpc' => '2.0', 'result' => 'foobar', 'id' => 1)));
|
||||
|
||||
$result = $client->execute('methodA', array('a' => 'b'));
|
||||
$this->assertEquals($result, 'foobar');
|
||||
}
|
||||
|
||||
public function testSendRequestWithError()
|
||||
{
|
||||
$client = new Client('', false, $this->httpClient);
|
||||
|
||||
$this->httpClient
|
||||
->expects($this->once())
|
||||
->method('execute')
|
||||
->with($this->stringContains('{"jsonrpc":"2.0","method":"methodA","id":'))
|
||||
->will($this->returnValue(array(
|
||||
'jsonrpc' => '2.0',
|
||||
'error' => array(
|
||||
'code' => -32601,
|
||||
'message' => 'Method not found',
|
||||
),
|
||||
)));
|
||||
|
||||
$this->setExpectedException('BadFunctionCallException');
|
||||
$client->execute('methodA', array('a' => 'b'));
|
||||
}
|
||||
|
||||
public function testSendRequestWithErrorAndReturnExceptionEnabled()
|
||||
{
|
||||
$client = new Client('', true, $this->httpClient);
|
||||
|
||||
$this->httpClient
|
||||
->expects($this->once())
|
||||
->method('execute')
|
||||
->with($this->stringContains('{"jsonrpc":"2.0","method":"methodA","id":'))
|
||||
->will($this->returnValue(array(
|
||||
'jsonrpc' => '2.0',
|
||||
'error' => array(
|
||||
'code' => -32601,
|
||||
'message' => 'Method not found',
|
||||
),
|
||||
)));
|
||||
|
||||
$result = $client->execute('methodA', array('a' => 'b'));
|
||||
$this->assertInstanceOf('BadFunctionCallException', $result);
|
||||
}
|
||||
}
|
||||
220
libs/jsonrpc/tests/HttpClientTest.php
Normal file
220
libs/jsonrpc/tests/HttpClientTest.php
Normal file
@@ -0,0 +1,220 @@
|
||||
<?php
|
||||
|
||||
namespace JsonRPC;
|
||||
|
||||
require_once __DIR__.'/../../../vendor/autoload.php';
|
||||
|
||||
defined('CURLOPT_URL') or define('CURLOPT_URL', 10002);
|
||||
defined('CURLOPT_RETURNTRANSFER') or define('CURLOPT_RETURNTRANSFER', 19913);
|
||||
defined('CURLOPT_CONNECTTIMEOUT') or define('CURLOPT_CONNECTTIMEOUT', 78);
|
||||
defined('CURLOPT_MAXREDIRS') or define('CURLOPT_MAXREDIRS', 68);
|
||||
defined('CURLOPT_SSL_VERIFYPEER') or define('CURLOPT_SSL_VERIFYPEER', 64);
|
||||
defined('CURLOPT_POST') or define('CURLOPT_POST', 47);
|
||||
defined('CURLOPT_POSTFIELDS') or define('CURLOPT_POSTFIELDS', 10015);
|
||||
defined('CURLOPT_HTTPHEADER') or define('CURLOPT_HTTPHEADER', 10023);
|
||||
defined('CURLOPT_HEADERFUNCTION') or define('CURLOPT_HEADERFUNCTION', 20079);
|
||||
defined('CURLOPT_CAINFO') or define('CURLOPT_CAINFO', 10065);
|
||||
|
||||
function extension_loaded($extension) {
|
||||
return HttpClientTest::$functions->extension_loaded($extension);
|
||||
}
|
||||
|
||||
function fopen($url, $mode, $use_include_path, $context)
|
||||
{
|
||||
return HttpClientTest::$functions->fopen($url, $mode, $use_include_path, $context);
|
||||
}
|
||||
|
||||
function stream_context_create(array $params)
|
||||
{
|
||||
return HttpClientTest::$functions->stream_context_create($params);
|
||||
}
|
||||
|
||||
function curl_init() {
|
||||
return HttpClientTest::$functions->curl_init();
|
||||
}
|
||||
|
||||
function curl_setopt_array($ch, array $params) {
|
||||
HttpClientTest::$functions->curl_setopt_array($ch, $params);
|
||||
}
|
||||
|
||||
function curl_setopt($ch, $option, $value) {
|
||||
HttpClientTest::$functions->curl_setopt($ch, $option, $value);
|
||||
}
|
||||
|
||||
function curl_exec($ch) {
|
||||
return HttpClientTest::$functions->curl_exec($ch);
|
||||
}
|
||||
|
||||
function curl_close($ch) {
|
||||
HttpClientTest::$functions->curl_close($ch);
|
||||
}
|
||||
|
||||
class HttpClientTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public static $functions;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
self::$functions = $this
|
||||
->getMockBuilder('stdClass')
|
||||
->setMethods(array('extension_loaded', 'fopen', 'stream_context_create',
|
||||
'curl_init', 'curl_setopt_array', 'curl_setopt', 'curl_exec', 'curl_close'))
|
||||
->getMock();
|
||||
}
|
||||
|
||||
public function testWithServerError()
|
||||
{
|
||||
$this->setExpectedException('\JsonRPC\Exception\ServerErrorException');
|
||||
|
||||
$httpClient = new HttpClient();
|
||||
$httpClient->handleExceptions(array(
|
||||
'HTTP/1.0 301 Moved Permanently',
|
||||
'Connection: close',
|
||||
'HTTP/1.1 500 Internal Server Error',
|
||||
));
|
||||
}
|
||||
|
||||
public function testWithConnectionFailure()
|
||||
{
|
||||
$this->setExpectedException('\JsonRPC\Exception\ConnectionFailureException');
|
||||
|
||||
$httpClient = new HttpClient();
|
||||
$httpClient->handleExceptions(array(
|
||||
'HTTP/1.1 404 Not Found',
|
||||
));
|
||||
}
|
||||
|
||||
public function testWithAccessForbidden()
|
||||
{
|
||||
$this->setExpectedException('\JsonRPC\Exception\AccessDeniedException');
|
||||
|
||||
$httpClient = new HttpClient();
|
||||
$httpClient->handleExceptions(array(
|
||||
'HTTP/1.1 403 Forbidden',
|
||||
));
|
||||
}
|
||||
|
||||
public function testWithAccessNotAllowed()
|
||||
{
|
||||
$this->setExpectedException('\JsonRPC\Exception\AccessDeniedException');
|
||||
|
||||
$httpClient = new HttpClient();
|
||||
$httpClient->handleExceptions(array(
|
||||
'HTTP/1.0 401 Unauthorized',
|
||||
));
|
||||
}
|
||||
|
||||
public function testWithCallback()
|
||||
{
|
||||
self::$functions
|
||||
->expects($this->at(0))
|
||||
->method('extension_loaded')
|
||||
->with('curl')
|
||||
->will($this->returnValue(false));
|
||||
|
||||
self::$functions
|
||||
->expects($this->at(1))
|
||||
->method('stream_context_create')
|
||||
->with(array(
|
||||
'http' => array(
|
||||
'method' => 'POST',
|
||||
'protocol_version' => 1.1,
|
||||
'timeout' => 5,
|
||||
'max_redirects' => 2,
|
||||
'header' => implode("\r\n", array(
|
||||
'User-Agent: JSON-RPC PHP Client <https://github.com/fguillot/JsonRPC>',
|
||||
'Content-Type: application/json',
|
||||
'Accept: application/json',
|
||||
'Connection: close',
|
||||
'Content-Length: 4',
|
||||
)),
|
||||
'content' => 'test',
|
||||
'ignore_errors' => true,
|
||||
),
|
||||
'ssl' => array(
|
||||
'verify_peer' => true,
|
||||
'verify_peer_name' => true,
|
||||
)
|
||||
))
|
||||
->will($this->returnValue('context'));
|
||||
|
||||
self::$functions
|
||||
->expects($this->at(2))
|
||||
->method('fopen')
|
||||
->with('url', 'r', false, 'context')
|
||||
->will($this->returnValue(false));
|
||||
|
||||
$httpClient = new HttpClient('url');
|
||||
$httpClient->withBeforeRequestCallback(function(HttpClient $client, $payload) {
|
||||
$client->withHeaders(array('Content-Length: '.strlen($payload)));
|
||||
});
|
||||
|
||||
$this->setExpectedException('\JsonRPC\Exception\ConnectionFailureException');
|
||||
$httpClient->execute('test');
|
||||
}
|
||||
|
||||
public function testWithCurl()
|
||||
{
|
||||
self::$functions
|
||||
->expects($this->at(0))
|
||||
->method('extension_loaded')
|
||||
->with('curl')
|
||||
->will($this->returnValue(true));
|
||||
|
||||
self::$functions
|
||||
->expects($this->at(1))
|
||||
->method('curl_init')
|
||||
->will($this->returnValue('curl'));
|
||||
|
||||
self::$functions
|
||||
->expects($this->at(2))
|
||||
->method('curl_setopt_array')
|
||||
->with('curl', array(
|
||||
CURLOPT_URL => 'url',
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_CONNECTTIMEOUT => 5,
|
||||
CURLOPT_MAXREDIRS => 2,
|
||||
CURLOPT_SSL_VERIFYPEER => true,
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => 'test',
|
||||
CURLOPT_HTTPHEADER => array(
|
||||
'User-Agent: JSON-RPC PHP Client <https://github.com/fguillot/JsonRPC>',
|
||||
'Content-Type: application/json',
|
||||
'Accept: application/json',
|
||||
'Connection: close',
|
||||
'Content-Length: 4',
|
||||
),
|
||||
CURLOPT_HEADERFUNCTION => function ($curl, $header) use (&$headers) {
|
||||
$headers[] = $header;
|
||||
return strlen($header);
|
||||
}
|
||||
));
|
||||
|
||||
self::$functions
|
||||
->expects($this->at(3))
|
||||
->method('curl_setopt')
|
||||
->with('curl', CURLOPT_CAINFO, 'test.crt');
|
||||
|
||||
self::$functions
|
||||
->expects($this->at(4))
|
||||
->method('curl_exec')
|
||||
->with('curl')
|
||||
->will($this->returnValue(false));
|
||||
|
||||
self::$functions
|
||||
->expects($this->at(5))
|
||||
->method('curl_close')
|
||||
->with('curl');
|
||||
|
||||
$httpClient = new HttpClient('url');
|
||||
$httpClient
|
||||
->withSslLocalCert('test.crt')
|
||||
->withBeforeRequestCallback(function(HttpClient $client, $payload) {
|
||||
$client->withHeaders(array('Content-Length: '.strlen($payload)));
|
||||
});
|
||||
|
||||
|
||||
$this->setExpectedException('\JsonRPC\Exception\ConnectionFailureException');
|
||||
$httpClient->execute('test');
|
||||
}
|
||||
}
|
||||
40
libs/jsonrpc/tests/MiddlewareHandlerTest.php
Normal file
40
libs/jsonrpc/tests/MiddlewareHandlerTest.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
use JsonRPC\Exception\AuthenticationFailureException;
|
||||
use JsonRPC\MiddlewareHandler;
|
||||
use JsonRPC\MiddlewareInterface;
|
||||
|
||||
require_once __DIR__.'/../../../vendor/autoload.php';
|
||||
|
||||
class FirstMiddleware implements MiddlewareInterface
|
||||
{
|
||||
public function execute($username, $password, $procedureName)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class SecondMiddleware implements MiddlewareInterface
|
||||
{
|
||||
public function execute($username, $password, $procedureName)
|
||||
{
|
||||
if ($username === 'myUsername' && $password === 'myPassword' && $procedureName === 'myProcedure') {
|
||||
throw new AuthenticationFailureException('Bad user');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MiddlewareHandlerTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testMiddlewareCanRaiseException()
|
||||
{
|
||||
$this->setExpectedException('JsonRpc\Exception\AuthenticationFailureException');
|
||||
|
||||
$middlewareHandler = new MiddlewareHandler();
|
||||
$middlewareHandler->withUsername('myUsername');
|
||||
$middlewareHandler->withPassword('myPassword');
|
||||
$middlewareHandler->withProcedure('myProcedure');
|
||||
$middlewareHandler->withMiddleware(new FirstMiddleware());
|
||||
$middlewareHandler->withMiddleware(new SecondMiddleware());
|
||||
$middlewareHandler->execute();
|
||||
}
|
||||
}
|
||||
153
libs/jsonrpc/tests/ProcedureHandlerTest.php
Normal file
153
libs/jsonrpc/tests/ProcedureHandlerTest.php
Normal file
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
use JsonRPC\ProcedureHandler;
|
||||
|
||||
require_once __DIR__.'/../../../vendor/autoload.php';
|
||||
|
||||
class A
|
||||
{
|
||||
public function getAll($p1, $p2, $p3 = 4)
|
||||
{
|
||||
return $p1 + $p2 + $p3;
|
||||
}
|
||||
}
|
||||
|
||||
class B
|
||||
{
|
||||
public function getAll($p1)
|
||||
{
|
||||
return $p1 + 2;
|
||||
}
|
||||
}
|
||||
|
||||
class ClassWithBeforeMethod
|
||||
{
|
||||
private $foobar = '';
|
||||
|
||||
public function before($procedure)
|
||||
{
|
||||
$this->foobar = $procedure;
|
||||
}
|
||||
|
||||
public function myProcedure()
|
||||
{
|
||||
return $this->foobar;
|
||||
}
|
||||
}
|
||||
|
||||
class ProcedureHandlerTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testProcedureNotFound()
|
||||
{
|
||||
$this->setExpectedException('BadFunctionCallException');
|
||||
$handler = new ProcedureHandler;
|
||||
$handler->executeProcedure('a');
|
||||
}
|
||||
|
||||
public function testCallbackNotFound()
|
||||
{
|
||||
$this->setExpectedException('BadFunctionCallException');
|
||||
$handler = new ProcedureHandler;
|
||||
$handler->withCallback('b', function() {});
|
||||
$handler->executeProcedure('a');
|
||||
}
|
||||
|
||||
public function testClassNotFound()
|
||||
{
|
||||
$this->setExpectedException('BadFunctionCallException');
|
||||
$handler = new ProcedureHandler;
|
||||
$handler->withClassAndMethod('getAllTasks', 'c', 'getAll');
|
||||
$handler->executeProcedure('getAllTasks');
|
||||
}
|
||||
|
||||
public function testMethodNotFound()
|
||||
{
|
||||
$this->setExpectedException('BadFunctionCallException');
|
||||
$handler = new ProcedureHandler;
|
||||
$handler->withClassAndMethod('getAllTasks', 'A', 'getNothing');
|
||||
$handler->executeProcedure('getAllTasks');
|
||||
}
|
||||
|
||||
public function testIsPositionalArguments()
|
||||
{
|
||||
$handler = new ProcedureHandler;
|
||||
$this->assertFalse($handler->isPositionalArguments(
|
||||
array('a' => 'b', 'c' => 'd')
|
||||
));
|
||||
|
||||
$handler = new ProcedureHandler;
|
||||
$this->assertTrue($handler->isPositionalArguments(
|
||||
array('a', 'b', 'c')
|
||||
));
|
||||
}
|
||||
|
||||
public function testBindNamedArguments()
|
||||
{
|
||||
$handler = new ProcedureHandler;
|
||||
$handler->withClassAndMethod('getAllA', 'A', 'getAll');
|
||||
$handler->withClassAndMethod('getAllB', 'B', 'getAll');
|
||||
$handler->withClassAndMethod('getAllC', new B, 'getAll');
|
||||
$this->assertEquals(6, $handler->executeProcedure('getAllA', array('p2' => 4, 'p1' => -2)));
|
||||
$this->assertEquals(10, $handler->executeProcedure('getAllA', array('p2' => 4, 'p3' => 8, 'p1' => -2)));
|
||||
$this->assertEquals(6, $handler->executeProcedure('getAllB', array('p1' => 4)));
|
||||
$this->assertEquals(5, $handler->executeProcedure('getAllC', array('p1' => 3)));
|
||||
}
|
||||
|
||||
public function testBindPositionalArguments()
|
||||
{
|
||||
$handler = new ProcedureHandler;
|
||||
$handler->withClassAndMethod('getAllA', 'A', 'getAll');
|
||||
$handler->withClassAndMethod('getAllB', 'B', 'getAll');
|
||||
$this->assertEquals(6, $handler->executeProcedure('getAllA', array(4, -2)));
|
||||
$this->assertEquals(2, $handler->executeProcedure('getAllA', array(4, 0, -2)));
|
||||
$this->assertEquals(4, $handler->executeProcedure('getAllB', array(2)));
|
||||
}
|
||||
|
||||
public function testRegisterNamedArguments()
|
||||
{
|
||||
$handler = new ProcedureHandler;
|
||||
$handler->withCallback('getAllA', function($p1, $p2, $p3 = 4) {
|
||||
return $p1 + $p2 + $p3;
|
||||
});
|
||||
|
||||
$this->assertEquals(6, $handler->executeProcedure('getAllA', array('p2' => 4, 'p1' => -2)));
|
||||
$this->assertEquals(10, $handler->executeProcedure('getAllA', array('p2' => 4, 'p3' => 8, 'p1' => -2)));
|
||||
}
|
||||
|
||||
public function testRegisterPositionalArguments()
|
||||
{
|
||||
$handler = new ProcedureHandler;
|
||||
$handler->withCallback('getAllA', function($p1, $p2, $p3 = 4) {
|
||||
return $p1 + $p2 + $p3;
|
||||
});
|
||||
|
||||
$this->assertEquals(6, $handler->executeProcedure('getAllA', array(4, -2)));
|
||||
$this->assertEquals(2, $handler->executeProcedure('getAllA', array(4, 0, -2)));
|
||||
}
|
||||
|
||||
public function testTooManyArguments()
|
||||
{
|
||||
$this->setExpectedException('InvalidArgumentException');
|
||||
|
||||
$handler = new ProcedureHandler;
|
||||
$handler->withClassAndMethod('getAllC', new B, 'getAll');
|
||||
$handler->executeProcedure('getAllC', array('p1' => 3, 'p2' => 5));
|
||||
}
|
||||
|
||||
public function testNotEnoughArguments()
|
||||
{
|
||||
$this->setExpectedException('InvalidArgumentException');
|
||||
|
||||
$handler = new ProcedureHandler;
|
||||
$handler->withClassAndMethod('getAllC', new B, 'getAll');
|
||||
$handler->executeProcedure('getAllC');
|
||||
}
|
||||
|
||||
public function testBeforeMethod()
|
||||
{
|
||||
$handler = new ProcedureHandler;
|
||||
$handler->withObject(new ClassWithBeforeMethod);
|
||||
$handler->withBeforeMethod('before');
|
||||
$this->assertEquals('myProcedure', $handler->executeProcedure('myProcedure'));
|
||||
}
|
||||
}
|
||||
53
libs/jsonrpc/tests/Request/RequestBuilderTest.php
Normal file
53
libs/jsonrpc/tests/Request/RequestBuilderTest.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
use JsonRPC\Request\RequestBuilder;
|
||||
|
||||
require_once __DIR__.'/../../../../vendor/autoload.php';
|
||||
|
||||
class RequestBuilderTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testBuilder()
|
||||
{
|
||||
$payload = RequestBuilder::create()
|
||||
->withId(123)
|
||||
->withProcedure('foobar')
|
||||
->withParams(array(1, 2, 3))
|
||||
->build();
|
||||
|
||||
$this->assertEquals('{"jsonrpc":"2.0","method":"foobar","id":123,"params":[1,2,3]}', $payload);
|
||||
}
|
||||
|
||||
public function testBuilderWithoutParams()
|
||||
{
|
||||
$payload = RequestBuilder::create()
|
||||
->withId(123)
|
||||
->withProcedure('foobar')
|
||||
->build();
|
||||
|
||||
$this->assertEquals('{"jsonrpc":"2.0","method":"foobar","id":123}', $payload);
|
||||
}
|
||||
|
||||
public function testBuilderWithoutId()
|
||||
{
|
||||
$payload = RequestBuilder::create()
|
||||
->withProcedure('foobar')
|
||||
->withParams(array(1, 2, 3))
|
||||
->build();
|
||||
|
||||
$result = json_decode($payload, true);
|
||||
$this->assertNotNull($result['id']);
|
||||
}
|
||||
|
||||
public function testBuilderWithAdditionalRequestAttributes()
|
||||
{
|
||||
$payload = RequestBuilder::create()
|
||||
->withProcedure('foobar')
|
||||
->withParams(array(1, 2, 3))
|
||||
->withRequestAttributes(array("some-attr" => 42))
|
||||
->build();
|
||||
|
||||
$result = json_decode($payload, true);
|
||||
$this->assertNotNull($result['some-attr']);
|
||||
}
|
||||
|
||||
}
|
||||
25
libs/jsonrpc/tests/Response/HeaderMockTest.php
Normal file
25
libs/jsonrpc/tests/Response/HeaderMockTest.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace JsonRPC\Response;
|
||||
|
||||
use PHPUnit_Framework_TestCase;
|
||||
|
||||
require_once __DIR__.'/../../../../vendor/autoload.php';
|
||||
|
||||
function header($value)
|
||||
{
|
||||
HeaderMockTest::$functions->header($value);
|
||||
}
|
||||
|
||||
abstract class HeaderMockTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
public static $functions;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
self::$functions = $this
|
||||
->getMockBuilder('stdClass')
|
||||
->setMethods(array('header'))
|
||||
->getMock();
|
||||
}
|
||||
}
|
||||
151
libs/jsonrpc/tests/Response/ResponseBuilderTest.php
Normal file
151
libs/jsonrpc/tests/Response/ResponseBuilderTest.php
Normal file
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
use JsonRPC\Exception\AccessDeniedException;
|
||||
use JsonRPC\Exception\AuthenticationFailureException;
|
||||
use JsonRPC\Exception\InvalidJsonFormatException;
|
||||
use JsonRPC\Exception\InvalidJsonRpcFormatException;
|
||||
use JsonRPC\Exception\ResponseEncodingFailureException;
|
||||
use JsonRPC\Exception\ResponseException;
|
||||
use JsonRPC\Response\ResponseBuilder;
|
||||
|
||||
require_once __DIR__.'/../../../../vendor/autoload.php';
|
||||
|
||||
class ResponseBuilderTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testBuildResponse()
|
||||
{
|
||||
$response = ResponseBuilder::create()
|
||||
->withId(123)
|
||||
->withResult('test')
|
||||
->build();
|
||||
|
||||
$this->assertEquals('{"jsonrpc":"2.0","result":"test","id":123}', $response);
|
||||
}
|
||||
|
||||
public function testBuildResponseWithError()
|
||||
{
|
||||
$response = ResponseBuilder::create()
|
||||
->withId(123)
|
||||
->withResult('test')
|
||||
->withError(42, 'Test', 'More info')
|
||||
->build();
|
||||
|
||||
$this->assertEquals('{"jsonrpc":"2.0","error":{"code":42,"message":"Test","data":"More info"},"id":123}', $response);
|
||||
}
|
||||
|
||||
public function testBuildResponseWithException()
|
||||
{
|
||||
$response = ResponseBuilder::create()
|
||||
->withId(123)
|
||||
->withResult('test')
|
||||
->withException(new Exception('Test'))
|
||||
->build();
|
||||
|
||||
$this->assertEquals('{"jsonrpc":"2.0","error":{"code":0,"message":"Test"},"id":123}', $response);
|
||||
}
|
||||
|
||||
public function testBuildResponseWithResponseException()
|
||||
{
|
||||
$exception = new ResponseException('Error', 42);
|
||||
$exception->setData('Data');
|
||||
|
||||
$response = ResponseBuilder::create()
|
||||
->withId(123)
|
||||
->withResult('test')
|
||||
->withException($exception)
|
||||
->build();
|
||||
|
||||
$this->assertEquals('{"jsonrpc":"2.0","error":{"code":42,"message":"Error","data":"Data"},"id":123}', $response);
|
||||
}
|
||||
|
||||
public function testBuildResponseWithAccessDeniedException()
|
||||
{
|
||||
$responseBuilder = ResponseBuilder::create();
|
||||
$response = $responseBuilder
|
||||
->withId(123)
|
||||
->withResult('test')
|
||||
->withException(new AccessDeniedException('Test'))
|
||||
->build();
|
||||
|
||||
$this->assertEquals('{"jsonrpc":"2.0","error":{"code":403,"message":"Forbidden"},"id":123}', $response);
|
||||
$this->assertEquals('HTTP/1.0 403 Forbidden', $responseBuilder->getStatus());
|
||||
|
||||
$this->assertEquals(
|
||||
array('Content-Type' => 'application/json'),
|
||||
$responseBuilder->getHeaders()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuildResponseWithAuthenticationFailureException()
|
||||
{
|
||||
$responseBuilder = ResponseBuilder::create();
|
||||
$response = $responseBuilder
|
||||
->withId(123)
|
||||
->withResult('test')
|
||||
->withException(new AuthenticationFailureException('Test'))
|
||||
->build();
|
||||
|
||||
$this->assertEquals('{"jsonrpc":"2.0","error":{"code":401,"message":"Unauthorized"},"id":123}', $response);
|
||||
$this->assertEquals('HTTP/1.0 401 Unauthorized', $responseBuilder->getStatus());
|
||||
|
||||
$this->assertEquals(
|
||||
array('Content-Type' => 'application/json', 'WWW-Authenticate' => 'Basic realm="JsonRPC"'),
|
||||
$responseBuilder->getHeaders()
|
||||
);
|
||||
}
|
||||
|
||||
public function testBuildResponseWithResponseEncodingFailureException()
|
||||
{
|
||||
$response = ResponseBuilder::create()
|
||||
->withId(123)
|
||||
->withResult('test')
|
||||
->withException(new ResponseEncodingFailureException('Test'))
|
||||
->build();
|
||||
|
||||
$this->assertEquals('{"jsonrpc":"2.0","error":{"code":-32603,"message":"Internal error","data":"Test"},"id":123}', $response);
|
||||
}
|
||||
|
||||
public function testBuildResponseWithInvalidArgumentException()
|
||||
{
|
||||
$response = ResponseBuilder::create()
|
||||
->withId(123)
|
||||
->withResult('test')
|
||||
->withException(new InvalidArgumentException('Test'))
|
||||
->build();
|
||||
|
||||
$this->assertEquals('{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid params","data":"Test"},"id":123}', $response);
|
||||
}
|
||||
|
||||
public function testBuildResponseWithBadFunctionCallException()
|
||||
{
|
||||
$response = ResponseBuilder::create()
|
||||
->withId(123)
|
||||
->withResult('test')
|
||||
->withException(new BadFunctionCallException('Test'))
|
||||
->build();
|
||||
|
||||
$this->assertEquals('{"jsonrpc":"2.0","error":{"code":-32601,"message":"Method not found"},"id":123}', $response);
|
||||
}
|
||||
|
||||
public function testBuildResponseWithInvalidJsonRpcFormatException()
|
||||
{
|
||||
$response = ResponseBuilder::create()
|
||||
->withId(123)
|
||||
->withResult('test')
|
||||
->withException(new InvalidJsonRpcFormatException('Test'))
|
||||
->build();
|
||||
|
||||
$this->assertEquals('{"jsonrpc":"2.0","error":{"code":-32600,"message":"Invalid Request"},"id":null}', $response);
|
||||
}
|
||||
|
||||
public function testBuildResponseWithInvalidJsonFormatException()
|
||||
{
|
||||
$response = ResponseBuilder::create()
|
||||
->withId(123)
|
||||
->withResult('test')
|
||||
->withException(new InvalidJsonFormatException('Test'))
|
||||
->build();
|
||||
|
||||
$this->assertEquals('{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"},"id":null}', $response);
|
||||
}
|
||||
}
|
||||
100
libs/jsonrpc/tests/Response/ResponseParserTest.php
Normal file
100
libs/jsonrpc/tests/Response/ResponseParserTest.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
use JsonRPC\Response\ResponseParser;
|
||||
|
||||
require_once __DIR__.'/../../../../vendor/autoload.php';
|
||||
|
||||
class ResponseParserTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testSingleRequest()
|
||||
{
|
||||
$result = ResponseParser::create()
|
||||
->withPayload(json_decode('{"jsonrpc": "2.0", "result": "foobar", "id": "1"}', true))
|
||||
->parse();
|
||||
|
||||
$this->assertEquals('foobar', $result);
|
||||
}
|
||||
|
||||
public function testWithBadJsonFormat()
|
||||
{
|
||||
$this->setExpectedException('\JsonRPC\Exception\InvalidJsonFormatException');
|
||||
|
||||
ResponseParser::create()
|
||||
->withPayload('foobar')
|
||||
->parse();
|
||||
}
|
||||
|
||||
public function testWithBadProcedure()
|
||||
{
|
||||
$this->setExpectedException('BadFunctionCallException');
|
||||
|
||||
ResponseParser::create()
|
||||
->withPayload(json_decode('{"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": "1"}', true))
|
||||
->parse();
|
||||
}
|
||||
|
||||
public function testWithInvalidArgs()
|
||||
{
|
||||
$this->setExpectedException('InvalidArgumentException');
|
||||
|
||||
ResponseParser::create()
|
||||
->withPayload(json_decode('{"jsonrpc": "2.0", "error": {"code": -32602, "message": "Invalid params"}, "id": "1"}', true))
|
||||
->parse();
|
||||
}
|
||||
|
||||
public function testWithInvalidRequest()
|
||||
{
|
||||
$this->setExpectedException('\JsonRPC\Exception\InvalidJsonRpcFormatException');
|
||||
|
||||
ResponseParser::create()
|
||||
->withPayload(json_decode('{"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null}', true))
|
||||
->parse();
|
||||
}
|
||||
|
||||
public function testWithParseError()
|
||||
{
|
||||
$this->setExpectedException('\JsonRPC\Exception\InvalidJsonFormatException');
|
||||
|
||||
ResponseParser::create()
|
||||
->withPayload(json_decode('{"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error"}, "id": null}', true))
|
||||
->parse();
|
||||
}
|
||||
|
||||
public function testWithOtherError()
|
||||
{
|
||||
$this->setExpectedException('\JsonRPC\Exception\ResponseException');
|
||||
|
||||
ResponseParser::create()
|
||||
->withPayload(json_decode('{"jsonrpc": "2.0", "error": {"code": 42, "message": "Something", "data": "foobar"}, "id": null}', true))
|
||||
->parse();
|
||||
}
|
||||
|
||||
public function testBatch()
|
||||
{
|
||||
$payload = '[
|
||||
{"jsonrpc": "2.0", "result": 7, "id": "1"},
|
||||
{"jsonrpc": "2.0", "result": 19, "id": "2"}
|
||||
]';
|
||||
|
||||
$result = ResponseParser::create()
|
||||
->withPayload(json_decode($payload, true))
|
||||
->parse();
|
||||
|
||||
$this->assertEquals(array(7, 19), $result);
|
||||
}
|
||||
|
||||
public function testBatchWithError()
|
||||
{
|
||||
$payload = '[
|
||||
{"jsonrpc": "2.0", "result": 7, "id": "1"},
|
||||
{"jsonrpc": "2.0", "result": 19, "id": "2"},
|
||||
{"jsonrpc": "2.0", "error": {"code": -32602, "message": "Invalid params"}, "id": "1"}
|
||||
]';
|
||||
|
||||
$this->setExpectedException('InvalidArgumentException');
|
||||
|
||||
ResponseParser::create()
|
||||
->withPayload(json_decode($payload, true))
|
||||
->parse();
|
||||
}
|
||||
}
|
||||
237
libs/jsonrpc/tests/ServerProtocolTest.php
Normal file
237
libs/jsonrpc/tests/ServerProtocolTest.php
Normal file
@@ -0,0 +1,237 @@
|
||||
<?php
|
||||
|
||||
use JsonRPC\Server;
|
||||
|
||||
require_once __DIR__.'/../../../vendor/autoload.php';
|
||||
require_once __DIR__.'/Response/HeaderMockTest.php';
|
||||
|
||||
class C
|
||||
{
|
||||
public function doSomething()
|
||||
{
|
||||
return 'something';
|
||||
}
|
||||
}
|
||||
|
||||
class ServerProtocolTest extends \JsonRPC\Response\HeaderMockTest
|
||||
{
|
||||
public function testPositionalParameters()
|
||||
{
|
||||
$subtract = function ($minuend, $subtrahend) {
|
||||
return $minuend - $subtrahend;
|
||||
};
|
||||
|
||||
$server = new Server('{"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1}');
|
||||
$server->register('subtract', $subtract);
|
||||
|
||||
$this->assertEquals(
|
||||
json_decode('{"jsonrpc": "2.0", "result": 19, "id": 1}', true),
|
||||
json_decode($server->execute(), true)
|
||||
);
|
||||
|
||||
$server = new Server('{"jsonrpc": "2.0", "method": "subtract", "params": [23, 42], "id": 1}');
|
||||
$server->register('subtract', $subtract);
|
||||
|
||||
$this->assertEquals(
|
||||
json_decode('{"jsonrpc": "2.0", "result": -19, "id": 1}', true),
|
||||
json_decode($server->execute(), true)
|
||||
);
|
||||
}
|
||||
|
||||
public function testNamedParameters()
|
||||
{
|
||||
$subtract = function ($minuend, $subtrahend) {
|
||||
return $minuend - $subtrahend;
|
||||
};
|
||||
|
||||
$server = new Server('{"jsonrpc": "2.0", "method": "subtract", "params": {"subtrahend": 23, "minuend": 42}, "id": 3}');
|
||||
$server->register('subtract', $subtract);
|
||||
|
||||
$this->assertEquals(
|
||||
json_decode('{"jsonrpc": "2.0", "result": 19, "id": 3}', true),
|
||||
json_decode($server->execute(), true)
|
||||
);
|
||||
|
||||
$server = new Server('{"jsonrpc": "2.0", "method": "subtract", "params": {"minuend": 42, "subtrahend": 23}, "id": 4}');
|
||||
$server->register('subtract', $subtract);
|
||||
|
||||
$this->assertEquals(
|
||||
json_decode('{"jsonrpc": "2.0", "result": 19, "id": 4}', true),
|
||||
json_decode($server->execute(), true)
|
||||
);
|
||||
}
|
||||
|
||||
public function testNotification()
|
||||
{
|
||||
$update = function($p1, $p2, $p3, $p4, $p5) {};
|
||||
$foobar = function() {};
|
||||
|
||||
$server = new Server('{"jsonrpc": "2.0", "method": "update", "params": [1,2,3,4,5]}');
|
||||
$server->register('update', $update);
|
||||
$server->register('foobar', $foobar);
|
||||
|
||||
$this->assertEquals('', $server->execute());
|
||||
|
||||
$server = new Server('{"jsonrpc": "2.0", "method": "foobar"}');
|
||||
$server->register('update', $update);
|
||||
$server->register('foobar', $foobar);
|
||||
|
||||
$this->assertEquals('', $server->execute());
|
||||
}
|
||||
|
||||
public function testNoMethod()
|
||||
{
|
||||
$server = new Server('{"jsonrpc": "2.0", "method": "foobar", "id": "1"}');
|
||||
|
||||
$this->assertEquals(
|
||||
json_decode('{"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": "1"}', true),
|
||||
json_decode($server->execute(), true)
|
||||
);
|
||||
}
|
||||
|
||||
public function testInvalidJson()
|
||||
{
|
||||
$server = new Server('{"jsonrpc": "2.0", "method": "foobar, "params": "bar", "baz]');
|
||||
|
||||
$this->assertEquals(
|
||||
json_decode('{"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error"}, "id": null}', true),
|
||||
json_decode($server->execute(), true)
|
||||
);
|
||||
}
|
||||
|
||||
public function testInvalidRequest()
|
||||
{
|
||||
$server = new Server('{"jsonrpc": "2.0", "method": 1, "params": "bar", "id": 1}');
|
||||
|
||||
$this->assertEquals(
|
||||
json_decode('{"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null}', true),
|
||||
json_decode($server->execute(), true)
|
||||
);
|
||||
}
|
||||
|
||||
public function testInvalidResponse_MalformedCharacters()
|
||||
{
|
||||
$server = new Server('{"jsonrpc": "2.0", "method": "invalidresponse","id": 1}');
|
||||
|
||||
$invalidresponse = function() {
|
||||
return pack("H*" ,'c32e');
|
||||
};
|
||||
|
||||
$server->register('invalidresponse', $invalidresponse);
|
||||
|
||||
$this->assertEquals(
|
||||
json_decode('{"jsonrpc": "2.0","id": 1, "error": {"code": -32603, "message": "Internal error","data": "Malformed UTF-8 characters, possibly incorrectly encoded"}}', true),
|
||||
json_decode($server->execute(), true)
|
||||
);
|
||||
}
|
||||
|
||||
public function testBatchInvalidJson()
|
||||
{
|
||||
$server = new Server('[
|
||||
{"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"},
|
||||
{"jsonrpc": "2.0", "method"
|
||||
]');
|
||||
|
||||
$this->assertEquals(
|
||||
json_decode('{"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error"}, "id": null}', true),
|
||||
json_decode($server->execute(), true)
|
||||
);
|
||||
}
|
||||
|
||||
public function testBatchEmptyArray()
|
||||
{
|
||||
$server = new Server('[]');
|
||||
|
||||
$this->assertEquals(
|
||||
json_decode('{"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null}', true),
|
||||
json_decode($server->execute(), true)
|
||||
);
|
||||
}
|
||||
|
||||
public function testBatchNotEmptyButInvalid()
|
||||
{
|
||||
$server = new Server('[1]');
|
||||
|
||||
$this->assertEquals(
|
||||
json_decode('[{"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error"}, "id": null}]', true),
|
||||
json_decode($server->execute(), true)
|
||||
);
|
||||
}
|
||||
|
||||
public function testBatchInvalid()
|
||||
{
|
||||
$server = new Server('[1,2,3]');
|
||||
|
||||
$this->assertEquals(
|
||||
json_decode('[
|
||||
{"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error"}, "id": null},
|
||||
{"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error"}, "id": null},
|
||||
{"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error"}, "id": null}
|
||||
]', true),
|
||||
json_decode($server->execute(), true)
|
||||
);
|
||||
}
|
||||
|
||||
public function testBatchOk()
|
||||
{
|
||||
$server = new Server('[
|
||||
{"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"},
|
||||
{"jsonrpc": "2.0", "method": "notify_hello", "params": [7]},
|
||||
{"jsonrpc": "2.0", "method": "subtract", "params": [42,23], "id": "2"},
|
||||
{"foo": "boo"},
|
||||
{"jsonrpc": "2.0", "method": "foo.get", "params": {"name": "myself"}, "id": "5"},
|
||||
{"jsonrpc": "2.0", "method": "get_data", "id": "9"},
|
||||
{"jsonrpc": "2.0", "method": "doSomething", "id": 10},
|
||||
{"jsonrpc": "2.0", "method": "doStuff", "id": 15}
|
||||
]');
|
||||
|
||||
$server->register('sum', function($a, $b, $c) {
|
||||
return $a + $b + $c;
|
||||
});
|
||||
|
||||
$server->register('subtract', function($minuend, $subtrahend) {
|
||||
return $minuend - $subtrahend;
|
||||
});
|
||||
|
||||
$server->register('get_data', function() {
|
||||
return array('hello', 5);
|
||||
});
|
||||
|
||||
$server->attach(new C);
|
||||
|
||||
$server->bind('doStuff', 'C', 'doSomething');
|
||||
|
||||
$response = $server->execute();
|
||||
|
||||
$this->assertEquals(
|
||||
json_decode('[
|
||||
{"jsonrpc": "2.0", "result": 7, "id": "1"},
|
||||
{"jsonrpc": "2.0", "result": 19, "id": "2"},
|
||||
{"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null},
|
||||
{"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": "5"},
|
||||
{"jsonrpc": "2.0", "result": ["hello", 5], "id": "9"},
|
||||
{"jsonrpc": "2.0", "result": "something", "id": "10"},
|
||||
{"jsonrpc": "2.0", "result": "something", "id": "15"}
|
||||
]', true),
|
||||
json_decode($response, true)
|
||||
);
|
||||
}
|
||||
|
||||
public function testBatchNotifications()
|
||||
{
|
||||
$server = new Server('[
|
||||
{"jsonrpc": "2.0", "method": "notify_sum", "params": [1,2,4]},
|
||||
{"jsonrpc": "2.0", "method": "notify_hello", "params": [7]}
|
||||
]');
|
||||
|
||||
$server->register('notify_sum', function($a, $b, $c) {
|
||||
|
||||
});
|
||||
|
||||
$server->register('notify_hello', function($id) {
|
||||
|
||||
});
|
||||
|
||||
$this->assertEquals('', $server->execute());
|
||||
}
|
||||
}
|
||||
258
libs/jsonrpc/tests/ServerTest.php
Normal file
258
libs/jsonrpc/tests/ServerTest.php
Normal file
@@ -0,0 +1,258 @@
|
||||
<?php
|
||||
|
||||
use JsonRPC\Exception\AccessDeniedException;
|
||||
use JsonRPC\Exception\AuthenticationFailureException;
|
||||
use JsonRPC\Exception\ResponseException;
|
||||
use JsonRPC\MiddlewareInterface;
|
||||
use JsonRPC\Response\HeaderMockTest;
|
||||
use JsonRPC\Server;
|
||||
|
||||
require_once __DIR__.'/../../../vendor/autoload.php';
|
||||
require_once __DIR__.'/Response/HeaderMockTest.php';
|
||||
|
||||
class MyException extends Exception
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
class DummyMiddleware implements MiddlewareInterface
|
||||
{
|
||||
public function execute($username, $password, $procedureName)
|
||||
{
|
||||
throw new AuthenticationFailureException('Bad user');
|
||||
}
|
||||
}
|
||||
|
||||
class ServerTest extends HeaderMockTest
|
||||
{
|
||||
private $payload = '{"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"}';
|
||||
|
||||
public function testCustomAuthenticationHeader()
|
||||
{
|
||||
$env = array(
|
||||
'HTTP_X_AUTH' => base64_encode('myuser:mypassword'),
|
||||
);
|
||||
|
||||
$server = new Server($this->payload, $env);
|
||||
$server->setAuthenticationHeader('X-Auth');
|
||||
$this->assertEquals('myuser', $server->getUsername());
|
||||
$this->assertEquals('mypassword', $server->getPassword());
|
||||
}
|
||||
|
||||
public function testCustomAuthenticationHeaderWithEmptyValue()
|
||||
{
|
||||
$server = new Server($this->payload);
|
||||
$server->setAuthenticationHeader('X-Auth');
|
||||
$this->assertNull($server->getUsername());
|
||||
$this->assertNull($server->getPassword());
|
||||
}
|
||||
|
||||
public function testGetUsername()
|
||||
{
|
||||
$server = new Server($this->payload);
|
||||
$this->assertNull($server->getUsername());
|
||||
|
||||
$server = new Server($this->payload, array('PHP_AUTH_USER' => 'username'));
|
||||
$this->assertEquals('username', $server->getUsername());
|
||||
}
|
||||
|
||||
public function testGetPassword()
|
||||
{
|
||||
$server = new Server($this->payload);
|
||||
$this->assertNull($server->getPassword());
|
||||
|
||||
$server = new Server($this->payload, array('PHP_AUTH_PW' => 'password'));
|
||||
$this->assertEquals('password', $server->getPassword());
|
||||
}
|
||||
|
||||
public function testExecute()
|
||||
{
|
||||
$server = new Server($this->payload);
|
||||
$server->getProcedureHandler()->withCallback('sum', function($a, $b, $c) {
|
||||
return $a + $b + $c;
|
||||
});
|
||||
|
||||
self::$functions
|
||||
->expects($this->once())
|
||||
->method('header')
|
||||
->with('Content-Type: application/json');
|
||||
|
||||
$this->assertEquals('{"jsonrpc":"2.0","result":7,"id":"1"}', $server->execute());
|
||||
}
|
||||
|
||||
public function testExecuteRequestParserOverride()
|
||||
{
|
||||
$requestParser = $this->getMockBuilder('JsonRPC\Request\RequestParser')
|
||||
->getMock();
|
||||
|
||||
$requestParser->method('withPayload')->willReturn($requestParser);
|
||||
$requestParser->method('withProcedureHandler')->willReturn($requestParser);
|
||||
$requestParser->method('withMiddlewareHandler')->willReturn($requestParser);
|
||||
$requestParser->method('withLocalException')->willReturn($requestParser);
|
||||
|
||||
$server = new Server($this->payload, array(), null, $requestParser);
|
||||
|
||||
$requestParser->expects($this->once())
|
||||
->method('parse');
|
||||
|
||||
$server->execute();
|
||||
}
|
||||
|
||||
public function testExecuteBatchRequestParserOverride()
|
||||
{
|
||||
$batchRequestParser = $this->getMockBuilder('JsonRPC\Request\BatchRequestParser')
|
||||
->getMock();
|
||||
|
||||
$batchRequestParser->method('withPayload')->willReturn($batchRequestParser);
|
||||
$batchRequestParser->method('withProcedureHandler')->willReturn($batchRequestParser);
|
||||
$batchRequestParser->method('withMiddlewareHandler')->willReturn($batchRequestParser);
|
||||
$batchRequestParser->method('withLocalException')->willReturn($batchRequestParser);
|
||||
|
||||
$server = new Server('["...", "..."]', array(), null, null, $batchRequestParser);
|
||||
|
||||
$batchRequestParser->expects($this->once())
|
||||
->method('parse');
|
||||
|
||||
$server->execute();
|
||||
}
|
||||
|
||||
public function testExecuteResponseBuilderOverride()
|
||||
{
|
||||
$responseBuilder = $this->getMockBuilder('JsonRPC\Response\ResponseBuilder')
|
||||
->getMock();
|
||||
|
||||
$responseBuilder->expects($this->once())
|
||||
->method('sendHeaders');
|
||||
|
||||
$server = new Server($this->payload, array(), $responseBuilder);
|
||||
$server->execute();
|
||||
}
|
||||
|
||||
public function testExecuteProcedureHandlerOverride()
|
||||
{
|
||||
$batchRequestParser = $this->getMockBuilder('JsonRPC\Request\BatchRequestParser')
|
||||
->getMock();
|
||||
|
||||
$procedureHandler = $this->getMockBuilder('JsonRPC\ProcedureHandler')
|
||||
->getMock();
|
||||
|
||||
$batchRequestParser->method('withPayload')->willReturn($batchRequestParser);
|
||||
$batchRequestParser->method('withProcedureHandler')->willReturn($batchRequestParser);
|
||||
$batchRequestParser->method('withMiddlewareHandler')->willReturn($batchRequestParser);
|
||||
$batchRequestParser->method('withLocalException')->willReturn($batchRequestParser);
|
||||
|
||||
$server = new Server('["...", "..."]', array(), null, null, $batchRequestParser, $procedureHandler);
|
||||
|
||||
$batchRequestParser->expects($this->once())
|
||||
->method('parse');
|
||||
|
||||
$batchRequestParser->expects($this->once())
|
||||
->method('withProcedureHandler')
|
||||
->with($this->identicalTo($procedureHandler));
|
||||
|
||||
$server->execute();
|
||||
}
|
||||
|
||||
public function testWhenCallbackRaiseForbiddenException()
|
||||
{
|
||||
$server = new Server($this->payload);
|
||||
$server->getProcedureHandler()->withCallback('sum', function($a, $b, $c) {
|
||||
throw new AccessDeniedException();
|
||||
});
|
||||
|
||||
self::$functions
|
||||
->expects($this->at(0))
|
||||
->method('header')
|
||||
->with('HTTP/1.0 403 Forbidden');
|
||||
|
||||
self::$functions
|
||||
->expects($this->at(1))
|
||||
->method('header')
|
||||
->with('Content-Type: application/json');
|
||||
|
||||
$this->assertEquals('{"jsonrpc":"2.0","error":{"code":403,"message":"Forbidden"},"id":null}', $server->execute());
|
||||
}
|
||||
|
||||
public function testWhenCallbackRaiseUnauthorizedException()
|
||||
{
|
||||
$server = new Server($this->payload);
|
||||
$server->getProcedureHandler()->withCallback('sum', function($a, $b, $c) {
|
||||
throw new AuthenticationFailureException();
|
||||
});
|
||||
|
||||
self::$functions
|
||||
->expects($this->at(0))
|
||||
->method('header')
|
||||
->with('HTTP/1.0 401 Unauthorized');
|
||||
|
||||
self::$functions
|
||||
->expects($this->at(1))
|
||||
->method('header')
|
||||
->with('Content-Type: application/json');
|
||||
|
||||
$this->assertEquals('{"jsonrpc":"2.0","error":{"code":401,"message":"Unauthorized"},"id":null}', $server->execute());
|
||||
}
|
||||
|
||||
public function testWhenMiddlewareRaiseUnauthorizedException()
|
||||
{
|
||||
$server = new Server($this->payload);
|
||||
$server->getMiddlewareHandler()->withMiddleware(new DummyMiddleware());
|
||||
$server->getProcedureHandler()->withCallback('sum', function($a, $b) {
|
||||
return $a + $b;
|
||||
});
|
||||
|
||||
self::$functions
|
||||
->expects($this->at(0))
|
||||
->method('header')
|
||||
->with('HTTP/1.0 401 Unauthorized');
|
||||
|
||||
self::$functions
|
||||
->expects($this->at(1))
|
||||
->method('header')
|
||||
->with('Content-Type: application/json');
|
||||
|
||||
$this->assertEquals('{"jsonrpc":"2.0","error":{"code":401,"message":"Unauthorized"},"id":null}', $server->execute());
|
||||
}
|
||||
|
||||
public function testFilterRelayExceptions()
|
||||
{
|
||||
$server = new Server($this->payload);
|
||||
$server->withLocalException('MyException');
|
||||
$server->getProcedureHandler()->withCallback('sum', function($a, $b, $c) {
|
||||
throw new MyException('test');
|
||||
});
|
||||
|
||||
$this->setExpectedException('MyException');
|
||||
$server->execute();
|
||||
}
|
||||
|
||||
public function testCustomExceptionAreRelayedToClient()
|
||||
{
|
||||
$server = new Server($this->payload);
|
||||
$server->getProcedureHandler()->withCallback('sum', function($a, $b, $c) {
|
||||
throw new MyException('test');
|
||||
});
|
||||
|
||||
self::$functions
|
||||
->expects($this->once())
|
||||
->method('header')
|
||||
->with('Content-Type: application/json');
|
||||
|
||||
$this->assertEquals('{"jsonrpc":"2.0","error":{"code":0,"message":"test"},"id":"1"}', $server->execute());
|
||||
}
|
||||
|
||||
public function testCustomResponseException()
|
||||
{
|
||||
$server = new Server($this->payload);
|
||||
$server->getProcedureHandler()->withCallback('sum', function($a, $b, $c) {
|
||||
throw new ResponseException('test', 123, null, 'more info');
|
||||
});
|
||||
|
||||
self::$functions
|
||||
->expects($this->once())
|
||||
->method('header')
|
||||
->with('Content-Type: application/json');
|
||||
|
||||
$this->assertEquals('{"jsonrpc":"2.0","error":{"code":123,"message":"test","data":"more info"},"id":"1"}', $server->execute());
|
||||
}
|
||||
}
|
||||
32
libs/jsonrpc/tests/Validator/HostValidatorTest.php
Normal file
32
libs/jsonrpc/tests/Validator/HostValidatorTest.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use JsonRPC\Validator\HostValidator;
|
||||
|
||||
require_once __DIR__.'/../../../../vendor/autoload.php';
|
||||
|
||||
class HostValidatorTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testWithEmptyHosts()
|
||||
{
|
||||
$this->assertNull(HostValidator::validate(array(), '127.0.0.1', '127.0.0.1'));
|
||||
}
|
||||
|
||||
public function testWithValidHosts()
|
||||
{
|
||||
$this->assertNull(HostValidator::validate(array('127.0.0.1'), '127.0.0.1', '127.0.0.1'));
|
||||
}
|
||||
|
||||
public function testWithValidNetwork()
|
||||
{
|
||||
$this->assertNull(HostValidator::validate(array('192.168.10.1/24'), '192.168.10.1'),'test ip match');
|
||||
$this->assertNull(HostValidator::validate(array('192.168.10.1/24'), '192.168.10.250'),'test ip match');
|
||||
$this->setExpectedException('\JsonRPC\Exception\AccessDeniedException');
|
||||
HostValidator::validate(array('192.168.10.1/24'), '192.168.11.1');
|
||||
}
|
||||
|
||||
public function testWithNotAuthorizedHosts()
|
||||
{
|
||||
$this->setExpectedException('\JsonRPC\Exception\AccessDeniedException');
|
||||
HostValidator::validate(array('192.168.1.1'), '127.0.0.1', '127.0.0.1');
|
||||
}
|
||||
}
|
||||
22
libs/jsonrpc/tests/Validator/JsonEncodingValidatorTest.php
Normal file
22
libs/jsonrpc/tests/Validator/JsonEncodingValidatorTest.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
use JsonRPC\Validator\JsonEncodingValidator;
|
||||
|
||||
require_once __DIR__.'/../../../../vendor/autoload.php';
|
||||
|
||||
class JsonEncodingValidatorTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testWithValidJson()
|
||||
{
|
||||
json_encode('{"foo": "bar"}');
|
||||
$this->assertNull(JsonEncodingValidator::validate());
|
||||
}
|
||||
|
||||
public function testWithJsonError()
|
||||
{
|
||||
json_encode("\xB1\x31");
|
||||
|
||||
$this->setExpectedException('\JsonRPC\Exception\ResponseEncodingFailureException');
|
||||
JsonEncodingValidator::validate();
|
||||
}
|
||||
}
|
||||
19
libs/jsonrpc/tests/Validator/JsonFormatValidatorTest.php
Normal file
19
libs/jsonrpc/tests/Validator/JsonFormatValidatorTest.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
use JsonRPC\Validator\JsonFormatValidator;
|
||||
|
||||
require_once __DIR__.'/../../../../vendor/autoload.php';
|
||||
|
||||
class JsonFormatValidatorTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testJsonParsedCorrectly()
|
||||
{
|
||||
$this->assertNull(JsonFormatValidator::validate(array('foobar')));
|
||||
}
|
||||
|
||||
public function testJsonNotParsedCorrectly()
|
||||
{
|
||||
$this->setExpectedException('\JsonRPC\Exception\InvalidJsonFormatException');
|
||||
JsonFormatValidator::validate('');
|
||||
}
|
||||
}
|
||||
48
libs/jsonrpc/tests/Validator/RpcFormatValidatorTest.php
Normal file
48
libs/jsonrpc/tests/Validator/RpcFormatValidatorTest.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
use JsonRPC\Validator\RpcFormatValidator;
|
||||
|
||||
require_once __DIR__.'/../../../../vendor/autoload.php';
|
||||
|
||||
class RpcFormatValidatorTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testWithMinimumRequirement()
|
||||
{
|
||||
$this->assertNull(RpcFormatValidator::validate(array('jsonrpc' => '2.0', 'method' => 'foobar')));
|
||||
}
|
||||
|
||||
public function testWithNoVersion()
|
||||
{
|
||||
$this->setExpectedException('\JsonRPC\Exception\InvalidJsonRpcFormatException');
|
||||
RpcFormatValidator::validate(array('method' => 'foobar'));
|
||||
}
|
||||
|
||||
public function testWithNoMethod()
|
||||
{
|
||||
$this->setExpectedException('\JsonRPC\Exception\InvalidJsonRpcFormatException');
|
||||
RpcFormatValidator::validate(array('jsonrpc' => '2.0'));
|
||||
}
|
||||
|
||||
public function testWithMethodNotString()
|
||||
{
|
||||
$this->setExpectedException('\JsonRPC\Exception\InvalidJsonRpcFormatException');
|
||||
RpcFormatValidator::validate(array('jsonrpc' => '2.0', 'method' => array()));
|
||||
}
|
||||
|
||||
public function testWithBadVersion()
|
||||
{
|
||||
$this->setExpectedException('\JsonRPC\Exception\InvalidJsonRpcFormatException');
|
||||
RpcFormatValidator::validate(array('jsonrpc' => '1.0', 'method' => 'abc'));
|
||||
}
|
||||
|
||||
public function testWithBadParams()
|
||||
{
|
||||
$this->setExpectedException('\JsonRPC\Exception\InvalidJsonRpcFormatException');
|
||||
RpcFormatValidator::validate(array('jsonrpc' => '2.0', 'method' => 'abc', 'params' => 'foobar'));
|
||||
}
|
||||
|
||||
public function testWithParams()
|
||||
{
|
||||
$this->assertNull(RpcFormatValidator::validate(array('jsonrpc' => '2.0', 'method' => 'abc', 'params' => array(1, 2))));
|
||||
}
|
||||
}
|
||||
24
libs/jsonrpc/tests/Validator/UserValidatorTest.php
Normal file
24
libs/jsonrpc/tests/Validator/UserValidatorTest.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
use JsonRPC\Validator\UserValidator;
|
||||
|
||||
require_once __DIR__.'/../../../../vendor/autoload.php';
|
||||
|
||||
class UserValidatorTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testWithEmptyHosts()
|
||||
{
|
||||
$this->assertNull(UserValidator::validate(array(), 'user', 'pass'));
|
||||
}
|
||||
|
||||
public function testWithValidHosts()
|
||||
{
|
||||
$this->assertNull(UserValidator::validate(array('user' => 'pass'), 'user', 'pass'));
|
||||
}
|
||||
|
||||
public function testWithNotAuthorizedHosts()
|
||||
{
|
||||
$this->setExpectedException('\JsonRPC\Exception\AuthenticationFailureException');
|
||||
UserValidator::validate(array('user' => 'pass'), 'user', 'wrong password');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user