#![warn(missing_docs)]
use jsonrpc_core::Error;
use jsonrpc_core::Params;
use jsonrpc_core::Value;
use rings_core::session::SessionSk;
use super::request::parse_response;
use super::request::RequestBuilder;
use crate::prelude::reqwest::Client as HttpClient;
pub struct SimpleClient {
client: HttpClient,
url: String,
delegated_sk: Option<SessionSk>,
}
impl SimpleClient {
pub fn new(url: &str, delegated_sk: Option<SessionSk>) -> Self {
Self {
client: HttpClient::default(),
url: url.to_string(),
delegated_sk,
}
}
pub async fn call_method(&self, method: &str, params: Params) -> RpcResult<Value> {
let msg = CallMessage {
method: method.into(),
params,
};
self.do_request(&RpcMessage::Call(msg)).await
}
pub async fn notify(&self, method: &str, params: Params) -> RpcResult<()> {
let msg = NotifyMessage {
method: method.into(),
params,
};
self.do_request(&RpcMessage::Notify(msg)).await?;
Ok(())
}
async fn do_request(&self, msg: &RpcMessage) -> RpcResult<Value> {
let mut request_builder = RequestBuilder::new();
let request = match msg {
RpcMessage::Call(call) => request_builder.call_request(call).1,
RpcMessage::Notify(notify) => request_builder.notification(notify),
RpcMessage::Subscribe(_) => {
return Err(RpcError::Client(
"Unsupported `RpcMessage` type `Subscribe`.".to_owned(),
));
}
};
let mut req = self
.client
.post(self.url.as_str())
.header(
http::header::CONTENT_TYPE,
http::header::HeaderValue::from_static("application/json"),
)
.header(
http::header::ACCEPT,
http::header::HeaderValue::from_static("application/json"),
)
.body(request.clone());
if let Some(delegated_sk) = &self.delegated_sk {
let sig = delegated_sk
.sign(&request.clone())
.map_err(|e| RpcError::Client(format!("Failed to sign request: {}", e)))?;
let encoded_sig = base64::encode(sig);
req = req.header("X-SIGNATURE", encoded_sig);
}
let resp = req
.send()
.await
.map_err(|e| RpcError::Client(e.to_string()))?;
let resp = resp
.error_for_status()
.map_err(|e| RpcError::Client(e.to_string()))?;
let resp = resp
.bytes()
.await
.map_err(|e| RpcError::ParseError(e.to_string(), Box::new(e)))?;
let resp_str = String::from_utf8_lossy(&resp).into_owned();
parse_response(&resp_str)
.map_err(|e| RpcError::ParseError(e.to_string(), Box::new(e)))?
.1
}
}
#[derive(Debug, thiserror::Error)]
pub enum RpcError {
#[error("Server returned rpc error {0}")]
JsonRpcError(Error),
#[error("Failed to parse server response as {0}: {1}")]
ParseError(String, Box<dyn std::error::Error + Send>),
#[error("Request timed out")]
Timeout,
#[error("Client error: {0}")]
Client(String),
#[error("{0}")]
Other(Box<dyn std::error::Error + Send>),
}
impl From<Error> for RpcError {
fn from(error: Error) -> Self {
RpcError::JsonRpcError(error)
}
}
pub type RpcResult<T> = Result<T, RpcError>;
pub struct CallMessage {
pub method: String,
pub params: Params,
}
pub struct NotifyMessage {
pub method: String,
pub params: Params,
}
pub struct Subscription {
pub subscribe: String,
pub subscribe_params: Params,
pub notification: String,
pub unsubscribe: String,
}
pub struct SubscribeMessage {
pub subscription: Subscription,
}
pub enum RpcMessage {
Call(CallMessage),
Notify(NotifyMessage),
Subscribe(SubscribeMessage),
}
impl From<CallMessage> for RpcMessage {
fn from(msg: CallMessage) -> Self {
RpcMessage::Call(msg)
}
}
impl From<NotifyMessage> for RpcMessage {
fn from(msg: NotifyMessage) -> Self {
RpcMessage::Notify(msg)
}
}
impl From<SubscribeMessage> for RpcMessage {
fn from(msg: SubscribeMessage) -> Self {
RpcMessage::Subscribe(msg)
}
}