1use futures::{stream::FuturesOrdered, FutureExt, StreamExt};
4use jsonrpsee::{
5 batch_response_error,
6 core::{server::helpers::prepare_error, JsonRawValue},
7 server::middleware::rpc::RpcServiceT,
8 types::{
9 error::{reject_too_big_request, ErrorCode},
10 ErrorObject, Id, InvalidRequest, Notification, Request,
11 },
12 BatchResponseBuilder, MethodResponse,
13};
14use std::{future::Future, panic::AssertUnwindSafe, sync::Arc};
15use tokio::sync::OwnedSemaphorePermit;
16use tokio_util::either::Either;
17use tracing::instrument;
18
19type Notif<'a> = Notification<'a, Option<&'a JsonRawValue>>;
20
21#[derive(Debug, Clone)]
22pub(crate) struct Batch<S> {
23 data: Vec<u8>,
24 rpc_service: S,
25}
26
27#[instrument(name = "batch", skip(b))]
31pub(crate) async fn process_batch_request<S>(
32 b: Batch<S>,
33 max_response_body_size: usize,
34) -> Option<String>
35where
36 S: RpcServiceT<MethodResponse = MethodResponse> + Send,
37{
38 let Batch { data, rpc_service } = b;
39
40 if let Ok(batch) = serde_json::from_slice::<Vec<&JsonRawValue>>(&data) {
41 let mut got_notif = false;
42 let mut batch_response = BatchResponseBuilder::new_with_limit(max_response_body_size);
43
44 let mut pending_calls: FuturesOrdered<_> = batch
45 .into_iter()
46 .filter_map(|v| {
47 if let Ok(req) = serde_json::from_str::<Request<'_>>(v.get()) {
48 Some(Either::Right(catch_call_panic(req.id(), rpc_service.call(req))))
49 } else if let Ok(_notif) = serde_json::from_str::<Notif<'_>>(v.get()) {
50 got_notif = true;
52 None
53 } else {
54 let id = match serde_json::from_str::<InvalidRequest<'_>>(v.get()) {
56 Ok(err) => err.id,
57 Err(_) => Id::Null,
58 };
59
60 Some(Either::Left(async {
61 MethodResponse::error(id, ErrorObject::from(ErrorCode::InvalidRequest))
62 }))
63 }
64 })
65 .collect();
66
67 while let Some(response) = pending_calls.next().await {
68 if let Err(too_large) = batch_response.append(response) {
69 return Some(raw_response_into_string(too_large.into_json()))
70 }
71 }
72
73 if got_notif && batch_response.is_empty() {
74 None
75 } else {
76 let batch_resp = batch_response.finish();
77 Some(raw_response_into_string(MethodResponse::from_batch(batch_resp).into_json()))
78 }
79 } else {
80 Some(raw_response_into_string(batch_response_error(
81 Id::Null,
82 ErrorObject::from(ErrorCode::ParseError),
83 )))
84 }
85}
86
87pub(crate) async fn process_single_request<S>(
88 data: Vec<u8>,
89 rpc_service: &S,
90) -> Option<MethodResponse>
91where
92 S: RpcServiceT<MethodResponse = MethodResponse> + Send,
93{
94 if let Ok(req) = serde_json::from_slice::<Request<'_>>(&data) {
95 Some(execute_call_with_tracing(req, rpc_service).await)
96 } else if serde_json::from_slice::<Notif<'_>>(&data).is_ok() {
97 None
98 } else {
99 let (id, code) = prepare_error(&data);
100 Some(MethodResponse::error(id, ErrorObject::from(code)))
101 }
102}
103
104#[instrument(name = "method_call", fields(method = req.method.as_ref()), skip(req, rpc_service))]
105pub(crate) async fn execute_call_with_tracing<'a, S>(
106 req: Request<'a>,
107 rpc_service: &S,
108) -> MethodResponse
109where
110 S: RpcServiceT<MethodResponse = MethodResponse> + Send,
111{
112 catch_call_panic(req.id(), rpc_service.call(req)).await
113}
114
115async fn catch_call_panic(
118 id: Id<'_>,
119 call: impl Future<Output = MethodResponse>,
120) -> MethodResponse {
121 AssertUnwindSafe(call)
122 .catch_unwind()
123 .await
124 .unwrap_or_else(|_| MethodResponse::error(id, ErrorObject::from(ErrorCode::InternalError)))
125}
126
127pub(crate) async fn call_with_service<S>(
128 request: String,
129 rpc_service: S,
130 max_response_body_size: usize,
131 max_request_body_size: usize,
132 conn: Arc<OwnedSemaphorePermit>,
133) -> Option<String>
134where
135 S: RpcServiceT<MethodResponse = MethodResponse> + Send,
136{
137 enum Kind {
138 Single,
139 Batch,
140 }
141
142 let request_kind = request
143 .chars()
144 .find_map(|c| match c {
145 '{' => Some(Kind::Single),
146 '[' => Some(Kind::Batch),
147 _ => None,
148 })
149 .unwrap_or(Kind::Single);
150
151 let data = request.into_bytes();
152 if data.len() > max_request_body_size {
153 return Some(raw_response_into_string(batch_response_error(
154 Id::Null,
155 reject_too_big_request(max_request_body_size as u32),
156 )))
157 }
158
159 let res = if matches!(request_kind, Kind::Single) {
161 let response = process_single_request(data, &rpc_service).await;
162 match response {
163 Some(response) if response.is_method_call() => {
164 Some(raw_response_into_string(response.into_json()))
165 }
166 _ => {
167 None
170 }
171 }
172 } else {
173 process_batch_request(Batch { data, rpc_service }, max_response_body_size).await
174 };
175
176 drop(conn);
177
178 res
179}
180
181fn raw_response_into_string(raw: Box<JsonRawValue>) -> String {
186 String::from(Box::<str>::from(raw))
187}