Merge pull request #1420 from TheSerapher/issue-1343

[IMPROVED] jsonRPC Error Handling with CURL
This commit is contained in:
Sebastian Grewe 2014-01-16 06:15:42 -08:00
commit 1dfbeea5f7
6 changed files with 153 additions and 211 deletions

View File

@ -68,7 +68,7 @@ if ($setting->getValue('disable_manual_payouts') != 1) {
try {
$txid = $bitcoin->sendtoaddress($aData['coin_address'], $dBalance - $config['txfee_manual']);
} catch (Exception $e) {
$log->logError('Failed to send requested balance to coin address, please check payout process. Does the wallet cover the amount?');
$log->logError('Failed to send requested balance to coin address, please check payout process. Does the wallet cover the amount? Error:' . $e->getMessage());
continue;
}

View File

@ -229,24 +229,7 @@ class Bitcoin {
}
}
/**
* Exception class for BitcoinClient
*
* @author Mike Gogulski
* http://www.gogulski.com/ http://www.nostate.com/
*/
class BitcoinClientException extends ErrorException {
// Redefine the exception so message isn't optional
public function __construct($message, $code = 0, $severity = E_USER_NOTICE, Exception $previous = null) {
parent::__construct($message, $code, $severity, $previous);
}
public function __toString() {
return __CLASS__ . ": [{$this->code}]: {$this->message}\n";
}
}
require_once(INCLUDE_DIR . "/jsonRPCClient.php");
require_once(INCLUDE_DIR . "/lib/jsonRPCClient.php");
/**
* Bitcoin client class for access to a Bitcoin server via JSON-RPC-HTTP[S]
@ -283,21 +266,18 @@ class BitcoinClient extends jsonRPCClient {
* @access public
* @throws BitcoinClientException
*/
public function __construct($scheme, $username, $password, $address = "localhost", $port = 8332, $certificate_path = '', $debug_level = 0) {
public function __construct($scheme, $username, $password, $address = "localhost", $certificate_path = '', $debug = false) {
$scheme = strtolower($scheme);
if ($scheme != "http" && $scheme != "https")
throw new BitcoinClientException("Scheme must be http or https");
throw new Exception("Scheme must be http or https");
if (empty($username))
throw new BitcoinClientException("Username must be non-blank");
throw new Exception("Username must be non-blank");
if (empty($password))
throw new BitcoinClientException("Password must be non-blank");
$port = (string) $port;
if (!$port || empty($port) || !is_numeric($port) || $port < 1 || $port > 65535 || floatval($port) != intval($port))
throw new BitcoinClientException("Port must be an integer and between 1 and 65535");
throw new Exception("Password must be non-blank");
if (!empty($certificate_path) && !is_readable($certificate_path))
throw new BitcoinClientException("Certificate file " . $certificate_path . " is not readable");
throw new Exception("Certificate file " . $certificate_path . " is not readable");
$uri = $scheme . "://" . $username . ":" . $password . "@" . $address . "/";
parent::__construct($uri);
parent::__construct($uri, $debug);
}
/**

View File

@ -9,15 +9,16 @@ if (!defined('SECURITY'))
* some basic caching functionality and some debugging
**/
class BitcoinWrapper extends BitcoinClient {
public function __construct($type, $username, $password, $host, $debug, $memcache) {
public function __construct($type, $username, $password, $host, $debug_level, $debug_object, $memcache) {
$this->type = $type;
$this->username = $username;
$this->password = $password;
$this->host = $host;
// $this->debug is already used
$this->oDebug = $debug;
$this->oDebug = $debug_object;
$this->memcache = $memcache;
return parent::__construct($this->type, $this->username, $this->password, $this->host);
$debug_level > 0 ? $debug_level = true : $debug_level = false;
return parent::__construct($this->type, $this->username, $this->password, $this->host, '', $debug_level);
}
/**
* Wrap variouns methods to add caching
@ -75,4 +76,4 @@ class BitcoinWrapper extends BitcoinClient {
}
// Load this wrapper
$bitcoin = new BitcoinWrapper($config['wallet']['type'], $config['wallet']['username'], $config['wallet']['password'], $config['wallet']['host'], $debug, $memcache);
$bitcoin = new BitcoinWrapper($config['wallet']['type'], $config['wallet']['username'], $config['wallet']['password'], $config['wallet']['host'], DEBUG, $debug, $memcache);

View File

@ -1,167 +0,0 @@
<?php
/*
COPYRIGHT
Copyright 2007 Sergio Vaccaro <sergio@inservibile.org>
This file is part of JSON-RPC PHP.
JSON-RPC PHP is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
JSON-RPC PHP is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with JSON-RPC PHP; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* The object of this class are generic jsonRPC 1.0 clients
* http://json-rpc.org/wiki/specification
*
* @author sergio <jsonrpcphp@inservibile.org>
*/
class jsonRPCClient {
/**
* Debug state
*
* @var boolean
*/
private $debug;
/**
* The server URL
*
* @var string
*/
private $url;
/**
* The request id
*
* @var integer
*/
private $id;
/**
* If true, notifications are performed instead of requests
*
* @var boolean
*/
private $notification = false;
/**
* Takes the connection parameters
*
* @param string $url
* @param boolean $debug
*/
public function __construct($url,$debug = false) {
// server URL
$this->url = $url;
// proxy
empty($proxy) ? $this->proxy = '' : $this->proxy = $proxy;
// debug state
empty($debug) ? $this->debug = false : $this->debug = true;
// message id
$this->id = 1;
}
/**
* Sets the notification state of the object. In this state, notifications are performed, instead of requests.
*
* @param boolean $notification
*/
public function setRPCNotification($notification) {
empty($notification) ?
$this->notification = false
:
$this->notification = true;
}
/**
* Performs a jsonRCP request and gets the results as an array
*
* @param string $method
* @param array $params
* @return array
*/
public function __call($method,$params) {
// check
if (!is_scalar($method)) {
throw new Exception('Method name has no scalar value');
}
// check
if (is_array($params)) {
// no keys
$params = array_values($params);
} else {
throw new Exception('Params must be given as array');
}
// sets notification or request task
if ($this->notification) {
$currentId = NULL;
} else {
$currentId = $this->id;
}
// prepares the request
$request = array(
'method' => $method,
'params' => $params,
'id' => $currentId
);
$request = json_encode($request);
$this->debug && $this->debug.='***** Request *****'."\n".$request."\n".'***** End Of request *****'."\n\n";
// performs the HTTP POST
$opts = array ('http' => array (
'method' => 'POST',
'header' => 'Content-type: application/json',
'content' => $request
));
$context = stream_context_create($opts);
if ($fp = @fopen($this->url, 'r', false, $context)) {
$response = '';
while($row = fgets($fp)) {
$response.= trim($row)."\n";
}
$this->debug && $this->debug.='***** Server response *****'."\n".$response.'***** End of server response *****'."\n";
$response = json_decode($response,true);
} else {
$error = error_get_last();
$error = explode(']: ', $error['message']);
throw new Exception('Unable to connect: ' . $error[1]);
}
// debug output
if ($this->debug) {
echo nl2br($debug);
}
// final checks and return
if (!$this->notification) {
// check
if ($response['id'] != $currentId) {
throw new Exception('Incorrect response id (request id: '.$currentId.', response id: '.$response['id'].')');
}
if (!is_null($response['error'])) {
throw new Exception('Request error: '.$response['error']);
}
return $response['result'];
} else {
return true;
}
}
}
?>

View File

@ -0,0 +1,127 @@
<?php
/*
COPYRIGHT
Copyright 2007 Sergio Vaccaro <sergio@inservibile.org>
This file is part of JSON-RPC PHP.
JSON-RPC PHP is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
JSON-RPC PHP is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with JSON-RPC PHP; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* The object of this class are generic jsonRPC 1.0 clients
* http://json-rpc.org/wiki/specification
*
* @author sergio <jsonrpcphp@inservibile.org>
*/
class jsonRPCClient {
/**
* Debug state
*
* @var boolean
*/
private $debug;
private $debug_output = NULL;
/**
* The server URL
*
* @var string
*/
private $url;
/**
* The request id
*
* @var integer
*/
private $id;
/**
* Takes the connection parameters
*
* @param string $url
* @param boolean $debug
*/
public function __construct($url, $debug = false) {
$this->url = $url;
$this->debug = $debug;
$this->debug_output = '';
$this->id = rand(1, 100);
}
/**
* Fetch debug information
* @param none
* @return array Debug data
**/
public function getDebugData() {
if ($this->debug) return $this->debug_output;
return false;
}
/**
* Performs a jsonRCP request and gets the results as an array
*
* @param string $method
* @param array $params
* @return array
*/
public function __call($method, $params) {
if (!is_scalar($method)) throw new Exception('Method name has no scalar value');
if (is_array($params)) {
// no keys
$params = array_values($params);
} else {
throw new Exception('Params must be given as array');
}
// prepares the request
$request = array(
'method' => $method,
'params' => $params,
'id' => $this->id
);
$request = json_encode($request);
if ($this->debug) $this->debug_output[] = 'Request: '.$request;
// performs the HTTP POST
// extract information from URL for proper authentication
$url = parse_url($this->url);
$ch = curl_init($url['scheme'].'://'.$url['host'].':'.$url['port'].$url['path']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/json'));
curl_setopt($ch, CURLOPT_USERPWD, $url['user'] . ":" . $url['pass']);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $request);
$response = curl_exec($ch);
if ($this->debug) $this->debug_output[] = 'Response: ' . $response;
$response = json_decode($response, true);
$resultStatus = curl_getinfo($ch);
if ($resultStatus['http_code'] != '200') {
if ($resultStatus['http_code'] == '401') throw new Exception('RPC call did not return 200: Authentication failed');
throw new Exception('RPC call did not return 200: HTTP error: ' . $resultStatus['http_code']);
}
if (curl_errno($ch)) throw new Exception('RPC call failed: ' . curl_error($ch));
curl_close($ch);
// final checks and return
if (!is_null($response['error']) || is_null($response)) throw new Exception('Response error or empty: ' . @$response['error']);
if ($response['id'] != $this->id) throw new Exception('Incorrect response id (request id: ' . $this->id . ', response id: ' . $response['id'] . ')');
return $response['result'];
}
}

View File

@ -3,20 +3,21 @@
// Make sure we are called from index.php
if (!defined('SECURITY')) die('Hacking attempt');
// Fetch data from wallet, always run this check
if ($bitcoin->can_connect() === true){
$dDifficulty = $bitcoin->getdifficulty();
$dNetworkHashrate = $bitcoin->getnetworkhashps();
$iBlock = $bitcoin->getblockcount();
is_int($iBlock) && $iBlock > 0 ? $sBlockHash = $bitcoin->getblockhash($iBlock) : $sBlockHash = '';
} else {
$dDifficulty = 1;
$dNetworkHashrate = 1;
$iBlock = 0;
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to wallet RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg');
}
if (!$smarty->isCached('master.tpl', $smarty_cache_key)) {
$debug->append('No cached version available, fetching from backend', 3);
// Fetch data from wallet
if ($bitcoin->can_connect() === true){
$dDifficulty = $bitcoin->getdifficulty();
$dNetworkHashrate = $bitcoin->getnetworkhashps();
$iBlock = $bitcoin->getblockcount();
is_int($iBlock) && $iBlock > 0 ? $sBlockHash = $bitcoin->getblockhash($iBlock) : $sBlockHash = '';
} else {
$dDifficulty = 1;
$dNetworkHashrate = 1;
$iBlock = 0;
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to wallet RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg');
}
// Top share contributors
$aContributorsShares = $statistics->getTopContributors('shares', 15);