use crate::server::rpc_module::MethodSink;
use futures_channel::mpsc;
use futures_util::stream::StreamExt;
use jsonrpsee_types::error::{CallError, Error};
use jsonrpsee_types::to_json_raw_value;
use jsonrpsee_types::v2::error::{OVERSIZED_RESPONSE_CODE, OVERSIZED_RESPONSE_MSG};
use jsonrpsee_types::v2::{
error::{CALL_EXECUTION_FAILED_CODE, UNKNOWN_ERROR_CODE},
ErrorCode, ErrorObject, Id, InvalidRequest, Response, RpcError, TwoPointZero,
};
use serde::Serialize;
use std::io;
#[derive(Debug)]
pub struct BoundedWriter {
max_len: usize,
buf: Vec<u8>,
}
impl BoundedWriter {
pub fn new(max_len: usize) -> Self {
Self { max_len, buf: Vec::with_capacity(128) }
}
pub fn into_bytes(self) -> Vec<u8> {
self.buf
}
}
impl<'a> io::Write for &'a mut BoundedWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let len = self.buf.len() + buf.len();
if self.max_len >= len {
self.buf.extend_from_slice(buf);
Ok(buf.len())
} else {
Err(io::Error::new(io::ErrorKind::OutOfMemory, "Memory capacity exceeded"))
}
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
pub fn send_response(id: Id, tx: &MethodSink, result: impl Serialize, max_response_size: u32) {
let mut writer = BoundedWriter::new(max_response_size as usize);
let json = match serde_json::to_writer(&mut writer, &Response { jsonrpc: TwoPointZero, id: id.clone(), result }) {
Ok(_) => {
unsafe { String::from_utf8_unchecked(writer.into_bytes()) }
}
Err(err) => {
tracing::error!("Error serializing response: {:?}", err);
if err.is_io() {
let data = to_json_raw_value(&format!("Exceeded max limit {}", max_response_size)).ok();
let err = ErrorObject {
code: ErrorCode::ServerError(OVERSIZED_RESPONSE_CODE),
message: OVERSIZED_RESPONSE_MSG,
data: data.as_deref(),
};
return send_error(id, tx, err);
} else {
return send_error(id, tx, ErrorCode::InternalError.into());
}
}
};
if let Err(err) = tx.unbounded_send(json) {
tracing::error!("Error sending response to the client: {:?}", err)
}
}
pub fn send_error(id: Id, tx: &MethodSink, error: ErrorObject) {
let json = match serde_json::to_string(&RpcError { jsonrpc: TwoPointZero, error, id }) {
Ok(json) => json,
Err(err) => {
tracing::error!("Error serializing error message: {:?}", err);
return;
}
};
if let Err(err) = tx.unbounded_send(json) {
tracing::error!("Could not send error response to the client: {:?}", err)
}
}
pub fn send_call_error(id: Id, tx: &MethodSink, err: Error) {
let (code, message, data) = match err {
Error::Call(CallError::InvalidParams(e)) => (ErrorCode::InvalidParams, e.to_string(), None),
Error::Call(CallError::Failed(e)) => (ErrorCode::ServerError(CALL_EXECUTION_FAILED_CODE), e.to_string(), None),
Error::Call(CallError::Custom { code, message, data }) => (code.into(), message, data),
e => (ErrorCode::ServerError(UNKNOWN_ERROR_CODE), e.to_string(), None),
};
let err = ErrorObject { code, message: &message, data: data.as_deref() };
send_error(id, tx, err)
}
pub fn prepare_error(data: &[u8]) -> (Id<'_>, ErrorCode) {
match serde_json::from_slice::<InvalidRequest>(data) {
Ok(InvalidRequest { id }) => (id, ErrorCode::InvalidRequest),
Err(_) => (Id::Null, ErrorCode::ParseError),
}
}
pub async fn collect_batch_response(rx: mpsc::UnboundedReceiver<String>) -> String {
let mut buf = String::with_capacity(2048);
buf.push('[');
let mut buf = rx
.fold(buf, |mut acc, response| async move {
acc.push_str(&response);
acc.push(',');
acc
})
.await;
buf.pop();
buf.push(']');
buf
}
#[cfg(test)]
mod tests {
use super::{BoundedWriter, Id, Response, TwoPointZero};
#[test]
fn bounded_serializer_work() {
let mut writer = BoundedWriter::new(100);
let result = "success";
assert!(
serde_json::to_writer(&mut writer, &Response { jsonrpc: TwoPointZero, id: Id::Number(1), result }).is_ok()
);
assert_eq!(String::from_utf8(writer.into_bytes()).unwrap(), r#"{"jsonrpc":"2.0","result":"success","id":1}"#);
}
#[test]
fn bounded_serializer_cap_works() {
let mut writer = BoundedWriter::new(100);
assert!(serde_json::to_writer(&mut writer, &"x".repeat(99)).is_err());
}
}