PATH:
opt
/
hc_php
/
unversioned
/
JsonRPC
<?php namespace JsonRPC; use RuntimeException; use BadFunctionCallException; use InvalidArgumentException; const TRANSPORT_TCP = 0; const TRANSPORT_HTTP = 1; // Retry config const MAX_RETRIES = 5; const RETRY_DELAY = 1; // seconds class Client { /** * RPC transport type * @var int */ private $transport; /** * Path to the unix socket of the server * @var string */ private $unix_socket_path; /** * HTTP client timeout * @var int */ private $timeout; /** * Enable debug output to the php error log * @var bool */ public $debug = false; /** * User that runs the script * @var string */ private $user; /** * Default HTTP headers to send to the server * @var string */ private $headers = "POST / HTTP/1.1\r\nHost: localhost\r\nContent-Type: application/json\r\nAccept: application/json\r\n"; /** * Stream handle */ private $ch; /** * Constructor * * @param string $unix_socket_path Path to the unix socket * @param int $timeout Timeout * @param int $transport Transport type */ public function __construct($unix_socket_path, $timeout = 5, $transport = TRANSPORT_TCP) { $this->unix_socket_path = 'unix://'.$unix_socket_path; $this->timeout = $timeout; $this->transport = $transport; $this->connect(); } /** * Destructor * * @access public */ public function __destruct() { if (is_resource($this->ch)) { fclose($this->ch); } } /** * (Re)connect socket */ private function connect() { if (is_resource($this->ch)) { @fclose($this->ch); } $this->ch = @stream_socket_client( $this->unix_socket_path, $errno, $errstr, $this->timeout ); if (!$this->ch) { throw new RuntimeException("$errstr ($errno)"); } // Make reads honor timeout stream_set_timeout($this->ch, $this->timeout); } /** * Automatic mapping of methods * * @access public * @param string $method Method name * @param array $params Method arguments * @return array */ public function __call($method, array $params) { return $this->execute($method, $params); } /** * Execute a method */ public function execute($method, array $params = array()) { return $this->parseResponse( $this->doRequest($this->prepareRequest($method, $params)) ); } /** * Prepare the payload * * @access public * @param string $method Method name * @param array $params Method arguments * @return array */ public function prepareRequest($method, array $params = array()) { $payload = array( 'jsonrpc' => '2.0', 'method' => $method, 'id' => mt_rand() ); if (!empty($params)) { $payload['params'] = $params; } return $payload; } /** * Parse the response and return the method result * * @access public * @param array $payload * @return mixed */ public function parseResponse(array $payload) { return $this->getResult($payload); } /** * Get a RPC call result */ public function getResult(array $payload) { if (isset($payload['error']['code'])) { $this->handleRpcErrors($payload['error']); } return isset($payload['result']) ? $payload['result'] : null; } /** * Throw an exception according the RPC error */ public function handleRpcErrors($error) { switch ($error['code']) { case -32601: throw new BadFunctionCallException('Method not found: '.$error['message']); case -32602: throw new InvalidArgumentException('Invalid arguments: '.$error['message']); default: throw new RuntimeException('Invalid request/response: '.$error['message'], $error['code']); } } /** * Prepare http message */ private function prepareHttpMessage($headers, $body) { return $headers . "Content-Length: ".strlen($body)."\r\n\r\n".$body; } /** * Parse http message */ private function parseHttpMessage($text) { $data = array(); $response = explode("\r\n", $text); $header = preg_split("/\s+/", $response[0]); $data["code"] = $header[1]; $data["body"] = ""; $is_body = false; foreach (array_slice($response, 1) as $str) { if ($is_body) { $data["body"] .= $str; continue; } $header = preg_split("/:\s+/", $str); if ($header[0]) { $data[$header[0]] = $header[1]; continue; } $is_body = true; } return $data; } /** * Do the HTTP/TCP request with retry * * Retries up to MAX_RETRIES with RETRY_DELAY seconds between attempts, * reconnecting the socket before each retry. */ public function doRequest($payload) { $json_payload = json_encode($payload); $attempt = 1; while (true) { try { $body = ($this->transport == TRANSPORT_TCP) ? $json_payload : $this->prepareHttpMessage($this->headers, $json_payload); if (@fwrite($this->ch, $body) === false) { throw new RuntimeException('Socket write error'); } $response = ''; while (($buffer = @fgets($this->ch)) !== false) { $response .= $buffer; if ($this->transport == TRANSPORT_TCP) { // For raw TCP we expect a single JSON line break; } } // Check for timeout or empty read $meta = is_resource($this->ch) ? stream_get_meta_data($this->ch) : array(); if (!$response || (!empty($meta['timed_out']) && $meta['timed_out'])) { throw new RuntimeException('Socket read error'); } $data = ($this->transport == TRANSPORT_TCP) ? $response : $this->parseHttpMessage($response)["body"]; if ($this->debug) { error_log('==> Request: '.PHP_EOL.json_encode($payload, JSON_PRETTY_PRINT)); error_log('==> Response: '.PHP_EOL.$response); } $result = json_decode($data, true); return is_array($result) ? $result : array(); } catch (\Throwable $e) { if ($attempt >= MAX_RETRIES) { throw new RuntimeException( "Failed after ".MAX_RETRIES." attempts: ".$e->getMessage(), $e->getCode() ); } if ($this->debug) { error_log(sprintf( '[Attempt %d/%d] Error: %s. Retrying in %ds...', $attempt, MAX_RETRIES, $e->getMessage(), RETRY_DELAY )); } // Wait then reconnect and retry sleep(RETRY_DELAY); $this->connect(); $attempt++; continue; } } } } ?>
[+]
..
[-] Client.php
[edit]