Exception handling in RPC & WebApps

This commit is contained in:
Tomasz Drwięga 2016-04-23 12:29:12 +02:00
parent 11b0daf6cb
commit 8956d7e02b
9 changed files with 47 additions and 18 deletions

7
Cargo.lock generated
View File

@ -300,7 +300,7 @@ dependencies = [
"ethminer 1.1.0", "ethminer 1.1.0",
"ethsync 1.1.0", "ethsync 1.1.0",
"jsonrpc-core 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-http-server 5.0.1 (git+https://github.com/debris/jsonrpc-http-server.git)", "jsonrpc-http-server 5.1.0",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -354,7 +354,7 @@ dependencies = [
"ethcore-util 1.1.0", "ethcore-util 1.1.0",
"hyper 0.9.0-mio (git+https://github.com/hyperium/hyper?branch=mio)", "hyper 0.9.0-mio (git+https://github.com/hyperium/hyper?branch=mio)",
"jsonrpc-core 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-http-server 5.0.1 (git+https://github.com/debris/jsonrpc-http-server.git)", "jsonrpc-http-server 5.1.0",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-status 0.2.2 (git+https://github.com/tomusdrw/parity-status.git)", "parity-status 0.2.2 (git+https://github.com/tomusdrw/parity-status.git)",
"parity-wallet 0.1.1 (git+https://github.com/tomusdrw/parity-wallet.git)", "parity-wallet 0.1.1 (git+https://github.com/tomusdrw/parity-wallet.git)",
@ -545,8 +545,7 @@ dependencies = [
[[package]] [[package]]
name = "jsonrpc-http-server" name = "jsonrpc-http-server"
version = "5.0.1" version = "5.1.0"
source = "git+https://github.com/debris/jsonrpc-http-server.git#e728f2e080799b7a62b0b5cf5fa9d4ad65cd8c96"
dependencies = [ dependencies = [
"hyper 0.9.0-mio (git+https://github.com/hyperium/hyper?branch=mio)", "hyper 0.9.0-mio (git+https://github.com/hyperium/hyper?branch=mio)",
"jsonrpc-core 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -166,6 +166,7 @@ fn execute_client(conf: Configuration) {
apis: conf.args.flag_rpcapi.clone().unwrap_or(conf.args.flag_jsonrpc_apis.clone()), apis: conf.args.flag_rpcapi.clone().unwrap_or(conf.args.flag_jsonrpc_apis.clone()),
cors: conf.args.flag_jsonrpc_cors.clone().or(conf.args.flag_rpccorsdomain.clone()), cors: conf.args.flag_jsonrpc_cors.clone().or(conf.args.flag_rpccorsdomain.clone()),
}, rpc::Dependencies { }, rpc::Dependencies {
panic_handler: panic_handler.clone(),
client: client.clone(), client: client.clone(),
sync: sync.clone(), sync: sync.clone(),
secret_store: account_service.clone(), secret_store: account_service.clone(),
@ -181,6 +182,7 @@ fn execute_client(conf: Configuration) {
user: conf.args.flag_webapp_user.clone(), user: conf.args.flag_webapp_user.clone(),
pass: conf.args.flag_webapp_pass.clone(), pass: conf.args.flag_webapp_pass.clone(),
}, webapp::Dependencies { }, webapp::Dependencies {
panic_handler: panic_handler.clone(),
client: client.clone(), client: client.clone(),
sync: sync.clone(), sync: sync.clone(),
secret_store: account_service.clone(), secret_store: account_service.clone(),

View File

@ -22,6 +22,7 @@ use ethcore::client::Client;
use ethsync::EthSync; use ethsync::EthSync;
use ethminer::Miner; use ethminer::Miner;
use util::RotatingLogger; use util::RotatingLogger;
use util::panics::PanicHandler;
use util::keys::store::{AccountService}; use util::keys::store::{AccountService};
use util::network_settings::NetworkSettings; use util::network_settings::NetworkSettings;
use die::*; use die::*;
@ -42,6 +43,7 @@ pub struct Configuration {
} }
pub struct Dependencies { pub struct Dependencies {
pub panic_handler: Arc<PanicHandler>,
pub client: Arc<Client>, pub client: Arc<Client>,
pub sync: Arc<EthSync>, pub sync: Arc<EthSync>,
pub secret_store: Arc<AccountService>, pub secret_store: Arc<AccountService>,
@ -106,7 +108,12 @@ pub fn setup_rpc_server(
match start_result { match start_result {
Err(RpcServerError::IoError(err)) => die_with_io_error(err), Err(RpcServerError::IoError(err)) => die_with_io_error(err),
Err(e) => die!("{:?}", e), Err(e) => die!("{:?}", e),
Ok(server) => server, Ok(server) => {
server.set_panic_handler(move || {
deps.panic_handler.notify_all("Panic in RPC thread.".to_owned());
});
server
},
} }
} }

View File

@ -21,6 +21,7 @@ use ethcore::client::Client;
use ethsync::EthSync; use ethsync::EthSync;
use ethminer::Miner; use ethminer::Miner;
use util::RotatingLogger; use util::RotatingLogger;
use util::panics::PanicHandler;
use util::keys::store::{AccountService}; use util::keys::store::{AccountService};
use util::network_settings::NetworkSettings; use util::network_settings::NetworkSettings;
use die::*; use die::*;
@ -39,6 +40,7 @@ pub struct Configuration {
} }
pub struct Dependencies { pub struct Dependencies {
pub panic_handler: Arc<PanicHandler>,
pub client: Arc<Client>, pub client: Arc<Client>,
pub sync: Arc<EthSync>, pub sync: Arc<EthSync>,
pub secret_store: Arc<AccountService>, pub secret_store: Arc<AccountService>,
@ -98,7 +100,7 @@ pub fn setup_webapp_server(
server.add_delegate(EthClient::new(&deps.client, &deps.sync, &deps.secret_store, &deps.miner).to_delegate()); server.add_delegate(EthClient::new(&deps.client, &deps.sync, &deps.secret_store, &deps.miner).to_delegate());
server.add_delegate(EthFilterClient::new(&deps.client, &deps.miner).to_delegate()); server.add_delegate(EthFilterClient::new(&deps.client, &deps.miner).to_delegate());
server.add_delegate(PersonalClient::new(&deps.secret_store).to_delegate()); server.add_delegate(PersonalClient::new(&deps.secret_store).to_delegate());
server.add_delegate(EthcoreClient::new(&deps.miner, deps.logger, deps.settings).to_delegate()); server.add_delegate(EthcoreClient::new(&deps.miner, deps.logger.clone(), deps.settings.clone()).to_delegate());
let start_result = match auth { let start_result = match auth {
None => { None => {
@ -112,7 +114,12 @@ pub fn setup_webapp_server(
match start_result { match start_result {
Err(webapp::ServerError::IoError(err)) => die_with_io_error(err), Err(webapp::ServerError::IoError(err)) => die_with_io_error(err),
Err(e) => die!("{:?}", e), Err(e) => die!("{:?}", e),
Ok(handle) => handle, Ok(server) => {
server.set_panic_handler(move || {
deps.panic_handler.notify_all("Panic in WebApp thread.".to_owned());
});
server
},
} }
} }

View File

@ -13,7 +13,7 @@ log = "0.3"
serde = "0.7.0" serde = "0.7.0"
serde_json = "0.7.0" serde_json = "0.7.0"
jsonrpc-core = "2.0" jsonrpc-core = "2.0"
jsonrpc-http-server = { git = "https://github.com/debris/jsonrpc-http-server.git" } jsonrpc-http-server = { git = "https://github.com/debris/jsonrpc-http-server.git", version = "5.1.0" }
ethcore-util = { path = "../util" } ethcore-util = { path = "../util" }
ethcore = { path = "../ethcore" } ethcore = { path = "../ethcore" }
ethash = { path = "../ethash" } ethash = { path = "../ethash" }

View File

@ -85,7 +85,9 @@ impl PanicHandler {
Ok(result) Ok(result)
} }
fn notify_all(&self, r: String) { /// Notifies all listeners in case there is a panic.
/// You should use `catch_panic` instead of calling this method explicitly.
pub fn notify_all(&self, r: String) {
let mut listeners = self.listeners.lock().unwrap(); let mut listeners = self.listeners.lock().unwrap();
for listener in listeners.deref_mut() { for listener in listeners.deref_mut() {
listener.call(&r); listener.call(&r);

View File

@ -10,7 +10,7 @@ authors = ["Ethcore <admin@ethcore.io"]
[dependencies] [dependencies]
log = "0.3" log = "0.3"
jsonrpc-core = "2.0" jsonrpc-core = "2.0"
jsonrpc-http-server = { git = "https://github.com/debris/jsonrpc-http-server.git" } jsonrpc-http-server = { git = "https://github.com/debris/jsonrpc-http-server.git", version = "5.1.0" }
hyper = { default-features = false, git = "https://github.com/hyperium/hyper", branch = "mio" } hyper = { default-features = false, git = "https://github.com/hyperium/hyper", branch = "mio" }
url = "0.5" url = "0.5"
ethcore-rpc = { path = "../rpc" } ethcore-rpc = { path = "../rpc" }

View File

@ -58,7 +58,7 @@ mod router;
mod rpc; mod rpc;
mod api; mod api;
use std::sync::Arc; use std::sync::{Arc, Mutex};
use std::net::SocketAddr; use std::net::SocketAddr;
use jsonrpc_core::{IoHandler, IoDelegate}; use jsonrpc_core::{IoHandler, IoDelegate};
use router::auth::{Authorization, NoAuth, HttpBasicAuth}; use router::auth::{Authorization, NoAuth, HttpBasicAuth};
@ -97,13 +97,15 @@ impl ServerBuilder {
/// Webapps HTTP server. /// Webapps HTTP server.
pub struct Server { pub struct Server {
server: Option<hyper::server::Listening>, server: Option<hyper::server::Listening>,
panic_handler: Arc<Mutex<Option<Box<Fn() -> () + Send>>>>,
} }
impl Server { impl Server {
fn start_http<A: Authorization + 'static>(addr: &SocketAddr, authorization: A, handler: Arc<IoHandler>) -> Result<Server, ServerError> { fn start_http<A: Authorization + 'static>(addr: &SocketAddr, authorization: A, handler: Arc<IoHandler>) -> Result<Server, ServerError> {
let panic_handler = Arc::new(Mutex::new(None));
let endpoints = Arc::new(apps::all_endpoints()); let endpoints = Arc::new(apps::all_endpoints());
let authorization = Arc::new(authorization); let authorization = Arc::new(authorization);
let rpc_endpoint = Arc::new(rpc::rpc(handler)); let rpc_endpoint = Arc::new(rpc::rpc(handler, panic_handler.clone()));
let api = Arc::new(api::RestApi::new(endpoints.clone())); let api = Arc::new(api::RestApi::new(endpoints.clone()));
try!(hyper::Server::http(addr)) try!(hyper::Server::http(addr))
@ -114,9 +116,17 @@ impl Server {
api.clone(), api.clone(),
authorization.clone(), authorization.clone(),
)) ))
.map(|l| Server { server: Some(l) }) .map(|l| Server {
server: Some(l),
panic_handler: panic_handler,
})
.map_err(ServerError::from) .map_err(ServerError::from)
} }
/// Set callback for panics.
pub fn set_panic_handler<F>(&self, handler: F) where F : Fn() -> () + Send + 'static {
*self.panic_handler.lock().unwrap() = Some(Box::new(handler));
}
} }
impl Drop for Server { impl Drop for Server {

View File

@ -14,28 +14,30 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc; use std::sync::{Arc, Mutex};
use hyper::server; use hyper::server;
use hyper::net::HttpStream; use hyper::net::HttpStream;
use jsonrpc_core::IoHandler; use jsonrpc_core::IoHandler;
use jsonrpc_http_server::ServerHandler; use jsonrpc_http_server::{ServerHandler, PanicHandler, AccessControlAllowOrigin};
use jsonrpc_http_server::AccessControlAllowOrigin;
use endpoint::Endpoint; use endpoint::Endpoint;
pub fn rpc(handler: Arc<IoHandler>) -> Box<Endpoint> { pub fn rpc(handler: Arc<IoHandler>, panic_handler: Arc<Mutex<Option<Box<Fn() -> () + Send>>>>) -> Box<Endpoint> {
Box::new(RpcEndpoint { Box::new(RpcEndpoint {
handler: handler, handler: handler,
panic_handler: panic_handler,
cors_domain: Some(AccessControlAllowOrigin::Null) cors_domain: Some(AccessControlAllowOrigin::Null)
}) })
} }
struct RpcEndpoint { struct RpcEndpoint {
handler: Arc<IoHandler>, handler: Arc<IoHandler>,
panic_handler: Arc<Mutex<Option<Box<Fn() -> () + Send>>>>,
cors_domain: Option<AccessControlAllowOrigin>, cors_domain: Option<AccessControlAllowOrigin>,
} }
impl Endpoint for RpcEndpoint { impl Endpoint for RpcEndpoint {
fn to_handler(&self, _prefix: &str) -> Box<server::Handler<HttpStream>> { fn to_handler(&self, _prefix: &str) -> Box<server::Handler<HttpStream>> {
Box::new(ServerHandler::new(self.handler.clone(), self.cors_domain.clone())) let panic_handler = PanicHandler { handler: self.panic_handler.clone() };
Box::new(ServerHandler::new(self.handler.clone(), self.cors_domain.clone(), panic_handler))
} }
} }