From 318fa9768ae27d6a3e83e84ee8e66d33cff6f44a Mon Sep 17 00:00:00 2001 From: glyph Date: Mon, 3 Jan 2022 10:46:38 +0200 Subject: [PATCH] add get methods for network rpcs --- peach-jsonrpc-server/Cargo.toml | 7 +- peach-jsonrpc-server/src/error.rs | 42 +++- peach-jsonrpc-server/src/lib.rs | 358 ++++++++++++++++++++++++++--- peach-jsonrpc-server/src/params.rs | 23 ++ 4 files changed, 392 insertions(+), 38 deletions(-) create mode 100644 peach-jsonrpc-server/src/params.rs diff --git a/peach-jsonrpc-server/Cargo.toml b/peach-jsonrpc-server/Cargo.toml index 7011cbb..a493a35 100644 --- a/peach-jsonrpc-server/Cargo.toml +++ b/peach-jsonrpc-server/Cargo.toml @@ -14,12 +14,15 @@ publish = false maintenance = { status = "actively-developed" } [dependencies] +anyhow = "1.0.51" env_logger = "0.9" jsonrpc-core = "18" jsonrpc-http-server = "18" log = "0.4" -miniserde = "0.1.15" -peach-stats = { path = "../peach-stats", features = ["miniserde_support"] } +peach-network = { path = "../peach-network", features = ["serde_support"] } +peach-stats = { path = "../peach-stats", features = ["serde_support"] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" [dev-dependencies] jsonrpc-test = "18" diff --git a/peach-jsonrpc-server/src/error.rs b/peach-jsonrpc-server/src/error.rs index 9e14dbf..78e75fe 100644 --- a/peach-jsonrpc-server/src/error.rs +++ b/peach-jsonrpc-server/src/error.rs @@ -1,12 +1,18 @@ use std::fmt; use jsonrpc_core::{Error as JsonRpcError, ErrorCode}; +use peach_network::NetworkError; use peach_stats::StatsError; +use serde_json::Error as SerdeError; /// Custom error type encapsulating all possible errors for a JSON-RPC server /// and associated methods. #[derive(Debug)] -pub enum JsonRpcServerError { +pub enum ServerError { + /// An error returned from the `peach-network` library. + Network(NetworkError), + /// Failed to serialize a data structure. + Serialize(SerdeError), /// An error returned from the `peach-stats` library. Stats(StatsError), /// An expected JSON-RPC method parameter was not provided. @@ -15,32 +21,48 @@ pub enum JsonRpcServerError { ParseParameter(JsonRpcError), } -impl fmt::Display for JsonRpcServerError { +impl fmt::Display for ServerError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - JsonRpcServerError::ParseParameter(ref source) => { + ServerError::ParseParameter(ref source) => { write!(f, "Failed to parse parameter: {}", source) } - JsonRpcServerError::MissingParameter(ref source) => { + ServerError::MissingParameter(ref source) => { write!(f, "Missing expected parameter: {}", source) } - JsonRpcServerError::Stats(ref source) => { + ServerError::Network(ref source) => { + write!(f, "{}", source) + } + ServerError::Serialize(ref source) => { + write!(f, "Serde serialization failure: {}", source) + } + ServerError::Stats(ref source) => { write!(f, "{}", source) } } } } -impl From for JsonRpcError { - fn from(err: JsonRpcServerError) -> Self { +impl From for JsonRpcError { + fn from(err: ServerError) -> Self { match &err { - JsonRpcServerError::Stats(source) => JsonRpcError { + ServerError::Network(source) => JsonRpcError { code: ErrorCode::ServerError(-32001), message: format!("{}", source), data: None, }, - JsonRpcServerError::MissingParameter(source) => source.clone(), - JsonRpcServerError::ParseParameter(source) => source.clone(), + ServerError::Stats(source) => JsonRpcError { + code: ErrorCode::ServerError(-32003), + message: format!("{}", source), + data: None, + }, + ServerError::Serialize(source) => JsonRpcError { + code: ErrorCode::ServerError(-32000), + message: format!("{}", source), + data: None, + }, + ServerError::MissingParameter(source) => source.clone(), + ServerError::ParseParameter(source) => source.clone(), } } } diff --git a/peach-jsonrpc-server/src/lib.rs b/peach-jsonrpc-server/src/lib.rs index 43ecbf7..b86f6a7 100644 --- a/peach-jsonrpc-server/src/lib.rs +++ b/peach-jsonrpc-server/src/lib.rs @@ -5,72 +5,378 @@ use std::env; use std::result::Result; -use jsonrpc_core::{IoHandler, Value}; +use jsonrpc_core::{Error as RpcCoreError, IoHandler, Params, Value}; use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation, ServerBuilder}; use log::info; -use miniserde::json; +use peach_network::network; use peach_stats::stats; mod error; -use crate::error::JsonRpcServerError; +mod params; + +use crate::error::ServerError; +use crate::params::{Iface, IfaceSsid}; /// Create JSON-RPC I/O handler, add RPC methods and launch HTTP server. -pub fn run() -> Result<(), JsonRpcServerError> { +pub fn run() -> Result<(), ServerError> { 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()))); + io.add_method("ping", |_| async { + Ok(Value::String("success".to_string())) + }); // TODO: add blocks of methods according to provided flags + /* PEACH-NETWORK RPC METHODS */ + + // get - all network rpc methods for querying state + + io.add_method("available_networks", |params: Params| async move { + let parsed: Result = params.parse(); + match parsed { + Ok(i) => { + let iface = i.iface; + match network::available_networks(&iface).map_err(ServerError::Network)? { + Some(list) => { + let json_list = + serde_json::to_string(&list).map_err(ServerError::Serialize)?; + Ok(Value::String(json_list)) + } + // return `Null` if no networks were found + None => Ok(Value::Null), + } + } + Err(e) => Err(RpcCoreError::from(ServerError::MissingParameter(e))), + } + }); + + io.add_method("id", |params: Params| async move { + let parsed: Result = params.parse(); + match parsed { + Ok(i) => { + let iface = i.iface; + let ssid = i.ssid; + match network::id(&iface, &ssid).map_err(ServerError::Network)? { + Some(id) => Ok(Value::String(id)), + None => Ok(Value::Null), + } + } + Err(e) => Err(RpcCoreError::from(ServerError::MissingParameter(e))), + } + }); + + io.add_method("ip", |params: Params| async move { + let parsed: Result = params.parse(); + match parsed { + Ok(i) => { + let iface = i.iface; + match network::ip(&iface).map_err(ServerError::Network)? { + Some(ip) => Ok(Value::String(ip)), + None => Ok(Value::Null), + } + } + Err(e) => Err(RpcCoreError::from(ServerError::MissingParameter(e))), + } + }); + + io.add_method("rssi", |params: Params| async move { + let parsed: Result = params.parse(); + match parsed { + Ok(i) => { + let iface = i.iface; + match network::rssi(&iface).map_err(ServerError::Network)? { + Some(rssi) => Ok(Value::String(rssi)), + None => Ok(Value::Null), + } + } + Err(e) => Err(RpcCoreError::from(ServerError::MissingParameter(e))), + } + }); + + io.add_method("rssi_percent", |params: Params| async move { + let parsed: Result = params.parse(); + match parsed { + Ok(i) => { + let iface = i.iface; + match network::rssi_percent(&iface).map_err(ServerError::Network)? { + Some(rssi) => Ok(Value::String(rssi)), + None => Ok(Value::Null), + } + } + Err(e) => Err(RpcCoreError::from(ServerError::MissingParameter(e))), + } + }); + + io.add_method("saved_networks", |_| async { + let list = network::saved_networks().map_err(ServerError::Network)?; + match list { + Some(list) => { + let json_list = serde_json::to_string(&list).map_err(ServerError::Serialize)?; + Ok(Value::String(json_list)) + } + None => Ok(Value::Null), + } + }); + + io.add_method("ssid", |params: Params| async move { + let parsed: Result = params.parse(); + match parsed { + Ok(i) => { + let iface = i.iface; + match network::ssid(&iface).map_err(ServerError::Network)? { + Some(ip) => Ok(Value::String(ip)), + None => Ok(Value::Null), + } + } + Err(e) => Err(RpcCoreError::from(ServerError::MissingParameter(e))), + } + }); + + io.add_method("state", |params: Params| async move { + let parsed: Result = params.parse(); + match parsed { + Ok(i) => { + let iface = i.iface; + match network::state(&iface).map_err(ServerError::Network)? { + Some(state) => Ok(Value::String(state)), + None => Ok(Value::Null), + } + } + Err(e) => Err(RpcCoreError::from(ServerError::MissingParameter(e))), + } + }); + + io.add_method("status", |params: Params| async move { + let parsed: Result = params.parse(); + match parsed { + Ok(i) => { + let iface = i.iface; + match network::status(&iface).map_err(ServerError::Network)? { + Some(status) => { + let json_status = + serde_json::to_string(&status).map_err(ServerError::Serialize)?; + Ok(Value::String(json_status)) + } + None => Ok(Value::Null), + } + } + Err(e) => Err(RpcCoreError::from(ServerError::MissingParameter(e))), + } + }); + + io.add_method("traffic", |params: Params| async move { + let parsed: Result = params.parse(); + match parsed { + Ok(i) => { + let iface = i.iface; + match network::traffic(&iface).map_err(ServerError::Network)? { + Some(traffic) => { + let json_traffic = + serde_json::to_string(&traffic).map_err(ServerError::Serialize)?; + Ok(Value::String(json_traffic)) + } + None => Ok(Value::Null), + } + } + Err(e) => Err(RpcCoreError::from(ServerError::MissingParameter(e))), + } + }); + + /* + // set - all network rpc methods for modifying state + + io.add_method("activate_ap", move |_| { + network::activate_ap()?; + + Ok(Value::String("success".to_string())) + }); + + io.add_method("activate_client", move |_| { + network::activate_client()?; + + Ok(Value::String("success".to_string())) + }); + + io.add_method("add", move |params: Params| { + let w: Result = params.parse(); + match w { + Ok(w) => match network::add(&w) { + Ok(_) => Ok(Value::String("success".to_string())), + Err(e) => Err(Error::from(e)), + }, + Err(e) => Err(Error::from(NetworkError::MissingParams { e })), + } + }); + + io.add_method("check_iface", move |_| { + network::check_iface()?; + + Ok(Value::String("success".to_string())) + }); + + io.add_method("delete", move |params: Params| { + let i: Result = params.parse(); + match i { + Ok(i) => { + let id = i.id; + let iface = i.iface; + match network::delete(&id, &iface) { + Ok(_) => Ok(Value::String("success".to_string())), + Err(_) => Err(Error::from(NetworkError::Delete { id, iface })), + } + } + Err(e) => Err(Error::from(NetworkError::MissingParams { e })), + } + }); + + io.add_method("disable", move |params: Params| { + let i: Result = params.parse(); + match i { + Ok(i) => { + let id = i.id; + let iface = i.iface; + match network::disable(&id, &iface) { + Ok(_) => Ok(Value::String("success".to_string())), + Err(_) => Err(Error::from(NetworkError::Disable { id, iface })), + } + } + Err(e) => Err(Error::from(NetworkError::MissingParams { e })), + } + }); + + io.add_method("disconnect", move |params: Params| { + let i: Result = params.parse(); + match i { + Ok(i) => { + let iface = i.iface; + match network::disconnect(&iface) { + Ok(_) => Ok(Value::String("success".to_string())), + Err(_) => Err(Error::from(NetworkError::Disconnect { iface })), + } + } + Err(e) => Err(Error::from(NetworkError::MissingParams { e })), + } + }); + + io.add_method("modify", move |params: Params| { + let i: Result = params.parse(); + match i { + Ok(i) => { + let iface = i.iface; + let id = i.id; + let pass = i.pass; + match network::modify(&iface, &id, &pass) { + Ok(_) => Ok(Value::String("success".to_string())), + Err(_) => Err(Error::from(NetworkError::Modify { iface, id })), + } + } + Err(e) => Err(Error::from(NetworkError::MissingParams { e })), + } + }); + + io.add_method("reassociate", move |params: Params| { + let i: Result = params.parse(); + match i { + Ok(i) => { + let iface = i.iface; + match network::reassociate(&iface) { + Ok(_) => Ok(Value::String("success".to_string())), + Err(_) => Err(Error::from(NetworkError::Reassociate { iface })), + } + } + Err(e) => Err(Error::from(NetworkError::MissingParams { e })), + } + }); + + io.add_method("reconfigure", move |_| match network::reconfigure() { + Ok(_) => Ok(Value::String("success".to_string())), + Err(_) => Err(Error::from(NetworkError::Reconfigure)), + }); + + io.add_method("reconnect", move |params: Params| { + let i: Result = params.parse(); + match i { + Ok(i) => { + let iface = i.iface; + match network::reconnect(&iface) { + Ok(_) => Ok(Value::String("success".to_string())), + Err(_) => Err(Error::from(NetworkError::Reconnect { iface })), + } + } + Err(e) => Err(Error::from(NetworkError::MissingParams { e })), + } + }); + + io.add_method("save", move |_| match network::save() { + Ok(_) => Ok(Value::String("success".to_string())), + Err(_) => Err(Error::from(NetworkError::Save)), + }); + + io.add_method("connect", move |params: Params| { + let i: Result = params.parse(); + match i { + Ok(i) => { + let id = i.id; + let iface = i.iface; + match network::connect(&id, &iface) { + Ok(_) => Ok(Value::String("success".to_string())), + Err(_) => Err(Error::from(NetworkError::Connect { id, iface })), + } + } + Err(e) => Err(Error::from(NetworkError::MissingParams { e })), + } + }); + */ + /* PEACH-STATS RPC METHODS */ - io.add_sync_method("cpu_stats", move |_| { + io.add_method("cpu_stats", |_| async { info!("Fetching CPU statistics."); - let cpu = stats::cpu_stats().map_err(JsonRpcServerError::Stats)?; - let json_cpu = json::to_string(&cpu); + let cpu = stats::cpu_stats().map_err(ServerError::Stats)?; + let json_cpu = serde_json::to_string(&cpu).map_err(ServerError::Serialize)?; Ok(Value::String(json_cpu)) }); - io.add_sync_method("cpu_stats_percent", move |_| { + io.add_method("cpu_stats_percent", |_| async { info!("Fetching CPU statistics as percentages."); - let cpu = stats::cpu_stats_percent().map_err(JsonRpcServerError::Stats)?; - let json_cpu = json::to_string(&cpu); + let cpu = stats::cpu_stats_percent().map_err(ServerError::Stats)?; + let json_cpu = serde_json::to_string(&cpu).map_err(ServerError::Serialize)?; Ok(Value::String(json_cpu)) }); - io.add_sync_method("disk_usage", move |_| { + io.add_method("disk_usage", |_| async { info!("Fetching disk usage statistics."); - let disks = stats::disk_usage().map_err(JsonRpcServerError::Stats)?; - let json_disks = json::to_string(&disks); + let disks = stats::disk_usage().map_err(ServerError::Stats)?; + let json_disks = serde_json::to_string(&disks).map_err(ServerError::Serialize)?; Ok(Value::String(json_disks)) }); - io.add_sync_method("load_average", move |_| { + io.add_method("load_average", |_| async { info!("Fetching system load average statistics."); - let avg = stats::load_average().map_err(JsonRpcServerError::Stats)?; - let json_avg = json::to_string(&avg); + let avg = stats::load_average().map_err(ServerError::Stats)?; + let json_avg = serde_json::to_string(&avg).map_err(ServerError::Serialize)?; Ok(Value::String(json_avg)) }); - io.add_sync_method("mem_stats", move |_| { + io.add_method("mem_stats", |_| async { info!("Fetching current memory statistics."); - let mem = stats::mem_stats().map_err(JsonRpcServerError::Stats)?; - let json_mem = json::to_string(&mem); + let mem = stats::mem_stats().map_err(ServerError::Stats)?; + let json_mem = serde_json::to_string(&mem).map_err(ServerError::Serialize)?; Ok(Value::String(json_mem)) }); - io.add_sync_method("uptime", move |_| { + io.add_method("uptime", |_| async { info!("Fetching system uptime."); - let uptime = stats::uptime().map_err(JsonRpcServerError::Stats)?; - let json_uptime = json::to_string(&uptime); + let uptime = stats::uptime().map_err(ServerError::Stats)?; + let json_uptime = serde_json::to_string(&uptime).map_err(ServerError::Serialize)?; Ok(Value::String(json_uptime)) }); @@ -106,7 +412,7 @@ mod tests { fn rpc_success() { let rpc = { let mut io = IoHandler::new(); - io.add_sync_method("rpc_success_response", |_| { + io.add_method("rpc_success_response", |_| async { Ok(Value::String("success".into())) }); test_rpc::Rpc::from(io) @@ -119,13 +425,13 @@ mod tests { fn rpc_parse_error() { let rpc = { let mut io = IoHandler::new(); - io.add_sync_method("rpc_parse_error", |_| { + io.add_method("rpc_parse_error", |_| async { let e = JsonRpcError { code: ErrorCode::ParseError, message: String::from("Parse error"), data: None, }; - Err(JsonRpcError::from(JsonRpcServerError::MissingParameter(e))) + Err(JsonRpcError::from(ServerError::MissingParameter(e))) }); test_rpc::Rpc::from(io) }; diff --git a/peach-jsonrpc-server/src/params.rs b/peach-jsonrpc-server/src/params.rs new file mode 100644 index 0000000..8446ebb --- /dev/null +++ b/peach-jsonrpc-server/src/params.rs @@ -0,0 +1,23 @@ +//! Data structures for parsing JSON-RPC method parameters. + +use serde::Deserialize; + +/// Network interface name. +#[derive(Debug, Deserialize)] +pub struct Iface { + pub iface: String, +} + +/// Network interface name and network identifier. +#[derive(Debug, Deserialize)] +pub struct IfaceId { + pub iface: String, + pub id: String, +} + +/// Network interface name and network SSID. +#[derive(Debug, Deserialize)] +pub struct IfaceSsid { + pub iface: String, + pub ssid: String, +}