//! # peach-jsonrpc-server //! //! A JSON-RPC server which exposes an API over HTTP. use std::env; use std::result::Result; use jsonrpc_core::{IoHandler, Value}; use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation, ServerBuilder}; use log::info; use peach_stats::stats; mod error; use crate::error::JsonRpcServerError; /// Create JSON-RPC I/O handler, add RPC methods and launch HTTP server. pub fn run() -> Result<(), JsonRpcServerError> { info!("Starting up."); info!("Creating JSON-RPC I/O handler."); let mut io = IoHandler::default(); io.add_sync_method("ping", |_| Ok(Value::String("success".to_string()))); // TODO: add blocks of methods according to provided flags /* PEACH-STATS RPC METHODS */ io.add_sync_method("cpu_stats", move |_| { info!("Fetching CPU statistics."); let cpu = stats::cpu_stats().map_err(JsonRpcServerError::Stats)?; let json_cpu = serde_json::to_string(&cpu).map_err(JsonRpcServerError::Serde)?; Ok(Value::String(json_cpu)) }); io.add_sync_method("cpu_stats_percent", move |_| { info!("Fetching CPU statistics as percentages."); let cpu = stats::cpu_stats_percent().map_err(JsonRpcServerError::Stats)?; let json_cpu = serde_json::to_string(&cpu).map_err(JsonRpcServerError::Serde)?; Ok(Value::String(json_cpu)) }); io.add_sync_method("disk_usage", move |_| { info!("Fetching disk usage statistics."); let disks = stats::disk_usage().map_err(JsonRpcServerError::Stats)?; let json_disks = serde_json::to_string(&disks).map_err(JsonRpcServerError::Serde)?; Ok(Value::String(json_disks)) }); io.add_sync_method("load_average", move |_| { info!("Fetching system load average statistics."); let avg = stats::load_average().map_err(JsonRpcServerError::Stats)?; let json_avg = serde_json::to_string(&avg).map_err(JsonRpcServerError::Serde)?; Ok(Value::String(json_avg)) }); io.add_sync_method("mem_stats", move |_| { info!("Fetching current memory statistics."); let mem = stats::mem_stats().map_err(JsonRpcServerError::Stats)?; let json_mem = serde_json::to_string(&mem).map_err(JsonRpcServerError::Serde)?; Ok(Value::String(json_mem)) }); io.add_sync_method("uptime", move |_| { info!("Fetching system uptime."); let uptime = stats::uptime().map_err(JsonRpcServerError::Stats)?; let json_uptime = serde_json::to_string(&uptime).map_err(JsonRpcServerError::Serde)?; Ok(Value::String(json_uptime)) }); let http_server = env::var("PEACH_JSONRPC_SERVER").unwrap_or_else(|_| "127.0.0.1:5110".to_string()); info!("Starting JSON-RPC server on {}.", http_server); let server = ServerBuilder::new(io) .cors(DomainsValidation::AllowOnly(vec![ AccessControlAllowOrigin::Null, ])) .start_http( &http_server .parse() .expect("Invalid HTTP address and port combination"), ) .expect("Unable to start RPC server"); server.wait(); Ok(()) } #[cfg(test)] mod tests { use super::*; use jsonrpc_core::{Error as JsonRpcError, ErrorCode}; use jsonrpc_test as test_rpc; #[test] fn rpc_success() { let rpc = { let mut io = IoHandler::new(); io.add_sync_method("rpc_success_response", |_| { Ok(Value::String("success".into())) }); test_rpc::Rpc::from(io) }; assert_eq!(rpc.request("rpc_success_response", &()), r#""success""#); } #[test] fn rpc_parse_error() { let rpc = { let mut io = IoHandler::new(); io.add_sync_method("rpc_parse_error", |_| { let e = JsonRpcError { code: ErrorCode::ParseError, message: String::from("Parse error"), data: None, }; Err(JsonRpcError::from(JsonRpcServerError::MissingParameter(e))) }); test_rpc::Rpc::from(io) }; assert_eq!( rpc.request("rpc_parse_error", &()), r#"{ "code": -32700, "message": "Parse error" }"# ); } }