jsonrpsee_core/server/
helpers.rs1use std::time::Duration;
28
29use jsonrpsee_types::{ErrorCode, ErrorObject, Id, InvalidRequest, Response, ResponsePayload};
30use serde_json::value::RawValue;
31use tokio::sync::mpsc;
32
33use super::{DisconnectError, SendTimeoutError, TrySendError};
34
35#[derive(Clone, Debug)]
37pub struct MethodSink {
38 tx: mpsc::Sender<String>,
40 max_response_size: u32,
42}
43
44impl MethodSink {
45 pub fn new(tx: mpsc::Sender<String>) -> Self {
47 MethodSink { tx, max_response_size: u32::MAX }
48 }
49
50 pub fn new_with_limit(tx: mpsc::Sender<String>, max_response_size: u32) -> Self {
52 MethodSink { tx, max_response_size }
53 }
54
55 pub fn is_closed(&self) -> bool {
57 self.tx.is_closed()
58 }
59
60 pub async fn closed(&self) {
66 self.tx.closed().await
67 }
68
69 pub const fn max_response_size(&self) -> u32 {
71 self.max_response_size
72 }
73
74 pub fn try_send(&mut self, msg: String) -> Result<(), TrySendError> {
79 self.tx.try_send(msg).map_err(Into::into)
80 }
81
82 pub async fn send(&self, msg: String) -> Result<(), DisconnectError> {
85 self.tx.send(msg).await.map_err(Into::into)
86 }
87
88 pub async fn send_error<'a>(
90 &self,
91 id: Id<'a>,
92 err: ErrorObject<'a>,
93 ) -> Result<(), DisconnectError> {
94 let payload = ResponsePayload::<()>::error_borrowed(err);
95 let json = serde_json::to_string(&Response::new(payload, id)).expect("valid JSON; qed");
96
97 self.send(json).await
98 }
99
100 pub async fn send_timeout(
102 &self,
103 msg: String,
104 timeout: Duration,
105 ) -> Result<(), SendTimeoutError> {
106 self.tx.send_timeout(msg, timeout).await.map_err(Into::into)
107 }
108
109 pub fn capacity(&self) -> usize {
111 self.tx.capacity()
112 }
113
114 pub fn max_capacity(&self) -> usize {
116 self.tx.max_capacity()
117 }
118}
119
120pub fn prepare_error(data: &[u8]) -> (Id<'_>, ErrorCode) {
122 let Ok(value) = serde_json::from_slice::<&RawValue>(data) else {
123 return (Id::Null, ErrorCode::ParseError);
124 };
125 let id = if value.get().starts_with('{') {
126 serde_json::from_slice::<InvalidRequest>(data).map(|request| request.id).unwrap_or(Id::Null)
127 } else {
128 Id::Null
129 };
130 (id, ErrorCode::InvalidRequest)
131}
132
133#[cfg(test)]
134mod tests {
135 use super::prepare_error;
136 use jsonrpsee_types::{ErrorCode, Id};
137
138 #[test]
139 fn error_classification_invalid_envelopes_without_id() {
140 for data in
141 [r#"{"jsonrpc":"1.0","method":"example","params":[]}"#, r#"{"jsonrpc":"2.0"}"#, r#"{}"#]
142 {
143 assert_eq!(
144 prepare_error(data.as_bytes()),
145 (Id::Null, ErrorCode::InvalidRequest),
146 "{data}"
147 );
148 }
149 }
150
151 #[test]
152 fn error_classification_malformed_json() {
153 for data in ["", " ", "{", "[", r#"{"id":1,"method":}"#, r#"{"id":1} trailing"#] {
154 assert_eq!(prepare_error(data.as_bytes()), (Id::Null, ErrorCode::ParseError), "{data}");
155 }
156 }
157
158 #[test]
159 fn error_classification_recoverable_ids() {
160 for (data, id) in [
161 (r#"{"jsonrpc":"1.0","method":"example","id":0}"#, Id::Number(0)),
162 (r#"{"jsonrpc":"1.0","id":18446744073709551615}"#, Id::Number(u64::MAX)),
163 (" \n {\"id\":\"keep-id\"} \t ", Id::Str("keep-id".into())),
164 (r#"{"id":"escaped\u002did"}"#, Id::Str("escaped-id".into())),
165 (r#"{"id":null}"#, Id::Null),
166 ] {
167 assert_eq!(prepare_error(data.as_bytes()), (id, ErrorCode::InvalidRequest), "{data}");
168 }
169 }
170
171 #[test]
172 fn error_classification_unrecoverable_ids_and_nonobjects() {
173 for data in [
174 r#"{"id":true}"#,
175 r#"{"id":{}}"#,
176 r#"{"id":[]}"#,
177 r#"{"id":-1}"#,
178 r#"["not-an-object-id"]"#,
179 "[1]",
180 "[]",
181 "true",
182 "null",
183 "42",
184 r#""text""#,
185 ] {
186 assert_eq!(
187 prepare_error(data.as_bytes()),
188 (Id::Null, ErrorCode::InvalidRequest),
189 "{data}"
190 );
191 }
192 }
193}