use crate::server::helpers::{send_error, send_response};
use futures_channel::{mpsc, oneshot};
use futures_util::{future::BoxFuture, FutureExt};
use jsonrpsee_types::error::{CallError, Error, SubscriptionClosedError};
use jsonrpsee_types::v2::error::{JsonRpcErrorCode, JsonRpcErrorObject, CALL_EXECUTION_FAILED_CODE};
use jsonrpsee_types::v2::params::{
Id, JsonRpcSubscriptionParams, RpcParams, SubscriptionId as JsonRpcSubscriptionId, TwoPointZero,
};
use jsonrpsee_types::v2::request::{JsonRpcNotification, JsonRpcRequest};
use parking_lot::Mutex;
use rustc_hash::FxHashMap;
use serde::Serialize;
use std::fmt::Debug;
use std::ops::{Deref, DerefMut};
use std::sync::Arc;
pub type SyncMethod = Arc<dyn Send + Sync + Fn(Id, RpcParams, &MethodSink, ConnectionId) -> Result<(), Error>>;
pub type AsyncMethod = Arc<
dyn Send
+ Sync
+ Fn(Id<'static>, RpcParams<'static>, MethodSink, ConnectionId) -> BoxFuture<'static, Result<(), Error>>,
>;
pub type ConnectionId = usize;
pub type SubscriptionId = u64;
pub type MethodSink = mpsc::UnboundedSender<String>;
type Subscribers = Arc<Mutex<FxHashMap<SubscriptionKey, (MethodSink, oneshot::Receiver<()>)>>>;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
struct SubscriptionKey {
conn_id: ConnectionId,
sub_id: SubscriptionId,
}
#[derive(Clone)]
pub enum MethodCallback {
Sync(SyncMethod),
Async(AsyncMethod),
}
impl MethodCallback {
pub async fn execute(&self, tx: &MethodSink, req: JsonRpcRequest<'_>, conn_id: ConnectionId) {
let id = req.id.clone();
let params = RpcParams::new(req.params.map(|params| params.get()));
let result = match self {
MethodCallback::Sync(callback) => (callback)(req.id.clone(), params, tx, conn_id),
MethodCallback::Async(callback) => {
let tx = tx.clone();
let params = params.into_owned();
let id = req.id.into_owned();
(callback)(id, params, tx, conn_id).await
}
};
if let Err(err) = result {
log::error!("execution of method call '{}' failed: {:?}, request id={:?}", req.method, err, id);
send_error(id, tx, JsonRpcErrorCode::ServerError(-1).into());
}
}
}
impl Debug for MethodCallback {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Async(_) => write!(f, "Async"),
Self::Sync(_) => write!(f, "Sync"),
}
}
}
#[derive(Default, Debug, Clone)]
pub struct Methods {
callbacks: Arc<FxHashMap<&'static str, MethodCallback>>,
}
impl Methods {
pub fn new() -> Self {
Self::default()
}
fn verify_method_name(&mut self, name: &str) -> Result<(), Error> {
if self.callbacks.contains_key(name) {
return Err(Error::MethodAlreadyRegistered(name.into()));
}
Ok(())
}
fn mut_callbacks(&mut self) -> &mut FxHashMap<&'static str, MethodCallback> {
Arc::make_mut(&mut self.callbacks)
}
pub fn merge(&mut self, other: impl Into<Methods>) -> Result<(), Error> {
let mut other = other.into();
for name in other.callbacks.keys() {
self.verify_method_name(name)?;
}
let callbacks = self.mut_callbacks();
for (name, callback) in other.mut_callbacks().drain() {
callbacks.insert(name, callback);
}
Ok(())
}
pub fn method(&self, method_name: &str) -> Option<&MethodCallback> {
self.callbacks.get(method_name)
}
pub async fn execute(&self, tx: &MethodSink, req: JsonRpcRequest<'_>, conn_id: ConnectionId) {
match self.callbacks.get(&*req.method) {
Some(callback) => callback.execute(tx, req, conn_id).await,
None => send_error(req.id, tx, JsonRpcErrorCode::MethodNotFound.into()),
}
}
pub fn method_names(&self) -> impl Iterator<Item = &'static str> + '_ {
self.callbacks.keys().copied()
}
}
impl<Context> Deref for RpcModule<Context> {
type Target = Methods;
fn deref(&self) -> &Methods {
&self.methods
}
}
impl<Context> DerefMut for RpcModule<Context> {
fn deref_mut(&mut self) -> &mut Methods {
&mut self.methods
}
}
#[derive(Debug, Clone)]
pub struct RpcModule<Context> {
ctx: Arc<Context>,
methods: Methods,
}
impl<Context> RpcModule<Context> {
pub fn new(ctx: Context) -> Self {
Self { ctx: Arc::new(ctx), methods: Default::default() }
}
}
impl<Context> From<RpcModule<Context>> for Methods {
fn from(module: RpcModule<Context>) -> Methods {
module.methods
}
}
impl<Context: Send + Sync + 'static> RpcModule<Context> {
pub fn register_method<R, F>(&mut self, method_name: &'static str, callback: F) -> Result<(), Error>
where
Context: Send + Sync + 'static,
R: Serialize,
F: Fn(RpcParams, &Context) -> Result<R, CallError> + Send + Sync + 'static,
{
self.methods.verify_method_name(method_name)?;
let ctx = self.ctx.clone();
self.methods.mut_callbacks().insert(
method_name,
MethodCallback::Sync(Arc::new(move |id, params, tx, _| {
match callback(params, &*ctx) {
Ok(res) => send_response(id, tx, res),
Err(CallError::InvalidParams) => send_error(id, tx, JsonRpcErrorCode::InvalidParams.into()),
Err(CallError::Failed(e)) => {
let err = JsonRpcErrorObject {
code: JsonRpcErrorCode::ServerError(CALL_EXECUTION_FAILED_CODE),
message: &e.to_string(),
data: None,
};
send_error(id, tx, err)
}
Err(CallError::Custom { code, message, data }) => {
let err = JsonRpcErrorObject { code: code.into(), message: &message, data: data.as_deref() };
send_error(id, tx, err)
}
};
Ok(())
})),
);
Ok(())
}
pub fn register_async_method<R, F>(&mut self, method_name: &'static str, callback: F) -> Result<(), Error>
where
R: Serialize + Send + Sync + 'static,
F: Fn(RpcParams<'static>, Arc<Context>) -> BoxFuture<'static, Result<R, CallError>>
+ Copy
+ Send
+ Sync
+ 'static,
{
self.methods.verify_method_name(method_name)?;
let ctx = self.ctx.clone();
self.methods.mut_callbacks().insert(
method_name,
MethodCallback::Async(Arc::new(move |id, params, tx, _| {
let ctx = ctx.clone();
let future = async move {
match callback(params, ctx).await {
Ok(res) => send_response(id, &tx, res),
Err(CallError::InvalidParams) => send_error(id, &tx, JsonRpcErrorCode::InvalidParams.into()),
Err(CallError::Failed(e)) => {
let err = JsonRpcErrorObject {
code: JsonRpcErrorCode::ServerError(CALL_EXECUTION_FAILED_CODE),
message: &e.to_string(),
data: None,
};
send_error(id, &tx, err)
}
Err(CallError::Custom { code, message, data }) => {
let err =
JsonRpcErrorObject { code: code.into(), message: &message, data: data.as_deref() };
send_error(id, &tx, err)
}
};
Ok(())
};
future.boxed()
})),
);
Ok(())
}
pub fn register_subscription<F>(
&mut self,
subscribe_method_name: &'static str,
unsubscribe_method_name: &'static str,
callback: F,
) -> Result<(), Error>
where
Context: Send + Sync + 'static,
F: Fn(RpcParams, SubscriptionSink, Arc<Context>) -> Result<(), Error> + Send + Sync + 'static,
{
if subscribe_method_name == unsubscribe_method_name {
return Err(Error::SubscriptionNameConflict(subscribe_method_name.into()));
}
self.methods.verify_method_name(subscribe_method_name)?;
self.methods.verify_method_name(unsubscribe_method_name)?;
let ctx = self.ctx.clone();
let subscribers = Subscribers::default();
{
let subscribers = subscribers.clone();
self.methods.mut_callbacks().insert(
subscribe_method_name,
MethodCallback::Sync(Arc::new(move |id, params, method_sink, conn_id| {
let (conn_tx, conn_rx) = oneshot::channel::<()>();
let sub_id = {
const JS_NUM_MASK: SubscriptionId = !0 >> 11;
let sub_id = rand::random::<SubscriptionId>() & JS_NUM_MASK;
let uniq_sub = SubscriptionKey { conn_id, sub_id };
subscribers.lock().insert(uniq_sub, (method_sink.clone(), conn_rx));
sub_id
};
send_response(id, method_sink, sub_id);
let sink = SubscriptionSink {
inner: method_sink.clone(),
method: subscribe_method_name,
subscribers: subscribers.clone(),
uniq_sub: SubscriptionKey { conn_id, sub_id },
is_connected: Some(conn_tx),
};
callback(params, sink, ctx.clone())
})),
);
}
{
self.methods.mut_callbacks().insert(
unsubscribe_method_name,
MethodCallback::Sync(Arc::new(move |id, params, tx, conn_id| {
let sub_id = params.one()?;
subscribers.lock().remove(&SubscriptionKey { conn_id, sub_id });
send_response(id, tx, "Unsubscribed");
Ok(())
})),
);
}
Ok(())
}
pub fn register_alias(&mut self, alias: &'static str, existing_method: &'static str) -> Result<(), Error> {
self.methods.verify_method_name(alias)?;
let callback = match self.methods.callbacks.get(existing_method) {
Some(callback) => callback.clone(),
None => return Err(Error::MethodNotFound(existing_method.into())),
};
self.methods.mut_callbacks().insert(alias, callback);
Ok(())
}
}
#[derive(Debug)]
pub struct SubscriptionSink {
inner: mpsc::UnboundedSender<String>,
method: &'static str,
uniq_sub: SubscriptionKey,
subscribers: Subscribers,
is_connected: Option<oneshot::Sender<()>>,
}
impl SubscriptionSink {
pub fn send<T: Serialize>(&mut self, result: &T) -> Result<(), Error> {
let msg = self.build_message(result)?;
self.inner_send(msg).map_err(Into::into)
}
fn build_message<T: Serialize>(&self, result: &T) -> Result<String, Error> {
serde_json::to_string(&JsonRpcNotification {
jsonrpc: TwoPointZero,
method: self.method,
params: JsonRpcSubscriptionParams {
subscription: JsonRpcSubscriptionId::Num(self.uniq_sub.sub_id),
result,
},
})
.map_err(Into::into)
}
fn inner_send(&mut self, msg: String) -> Result<(), Error> {
let res = if let Some(conn) = self.is_connected.as_ref() {
if !conn.is_canceled() {
self.inner.unbounded_send(msg).map_err(|_| subscription_closed_err(self.uniq_sub.sub_id))
} else {
Err(subscription_closed_err(self.uniq_sub.sub_id))
}
} else {
Err(subscription_closed_err(self.uniq_sub.sub_id))
};
if let Err(e) = &res {
self.close(e.to_string());
}
res
}
pub fn close(&mut self, close_reason: String) {
self.is_connected.take();
if let Some((sink, _)) = self.subscribers.lock().remove(&self.uniq_sub) {
let msg =
self.build_message(&SubscriptionClosedError::from(close_reason)).expect("valid json infallible; qed");
let _ = sink.unbounded_send(msg);
}
}
}
impl Drop for SubscriptionSink {
fn drop(&mut self) {
self.close(format!("Subscription: {} is closed and dropped", self.uniq_sub.sub_id));
}
}
fn subscription_closed_err(sub_id: u64) -> Error {
Error::SubscriptionClosed(format!("Subscription {} is closed but not yet dropped", sub_id).into())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rpc_modules_with_different_contexts_can_be_merged() {
let cx = Vec::<u8>::new();
let mut mod1 = RpcModule::new(cx);
mod1.register_method("bla with Vec context", |_: RpcParams, _| Ok(())).unwrap();
let mut mod2 = RpcModule::new(String::new());
mod2.register_method("bla with String context", |_: RpcParams, _| Ok(())).unwrap();
mod1.merge(mod2).unwrap();
assert!(mod1.method("bla with Vec context").is_some());
assert!(mod1.method("bla with String context").is_some());
}
#[test]
fn rpc_context_modules_can_register_subscriptions() {
let cx = ();
let mut cxmodule = RpcModule::new(cx);
let _subscription = cxmodule.register_subscription("hi", "goodbye", |_, _, _| Ok(()));
assert!(cxmodule.method("hi").is_some());
assert!(cxmodule.method("goodbye").is_some());
}
#[test]
fn rpc_register_alias() {
let mut module = RpcModule::new(());
module.register_method("hello_world", |_: RpcParams, _| Ok(())).unwrap();
module.register_alias("hello_foobar", "hello_world").unwrap();
assert!(module.method("hello_world").is_some());
assert!(module.method("hello_foobar").is_some());
}
}