1use serde_json::{Map, Number, Value, json};
2use std::fmt::{Display, Formatter};
3use std::io;
4use std::process::{ExitStatus, Stdio};
5use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader};
6use tokio::process::{Child, ChildStdin, ChildStdout, Command};
7use tokio::task::JoinHandle;
8
9pub const MAX_INBOUND_LINE_BYTES: usize = 8 * 1024 * 1024;
10pub const MAX_STDERR_CAPTURE_BYTES: usize = 64 * 1024;
11
12#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
13pub struct RequestId(pub u64);
14
15#[derive(Clone, Debug, Eq, PartialEq)]
16pub enum PeerId {
17 Number(Number),
18 String(String),
19}
20
21impl PeerId {
22 fn into_value(self) -> Value {
23 match self {
24 Self::Number(number) => Value::Number(number),
25 Self::String(string) => Value::String(string),
26 }
27 }
28}
29
30#[derive(Clone, Debug, PartialEq)]
31pub struct RpcError {
32 pub code: i64,
33 pub message: String,
34 pub data: Option<Value>,
35}
36
37#[derive(Clone, Debug, PartialEq)]
38pub enum IncomingMessage {
39 Response {
40 id: PeerId,
41 result: Result<Value, RpcError>,
42 },
43 Request {
44 id: PeerId,
45 method: String,
46 params: Value,
47 },
48 Notification {
49 method: String,
50 params: Value,
51 },
52}
53
54#[derive(Clone, Debug)]
55pub struct ProcessExit {
56 pub status: ExitStatus,
57 pub stderr: String,
58}
59
60#[derive(Debug)]
61pub enum Error {
62 Io(io::Error),
63 Json(serde_json::Error),
64 InvalidMessage(String),
65 InboundLineTooLong,
66 ProcessExited(ProcessExit),
67 StderrTask(String),
68}
69
70impl Display for Error {
71 fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
72 match self {
73 Self::Io(error) => write!(formatter, "stdio I/O failed: {error}"),
74 Self::Json(error) => write!(formatter, "JSON failed: {error}"),
75 Self::InvalidMessage(message) => {
76 write!(formatter, "invalid JSON-RPC message: {message}")
77 }
78 Self::InboundLineTooLong => {
79 write!(
80 formatter,
81 "JSON-RPC line exceeds {MAX_INBOUND_LINE_BYTES} bytes"
82 )
83 }
84 Self::ProcessExited(exit) => {
85 write!(
86 formatter,
87 "child exited with {}: {}",
88 exit.status, exit.stderr
89 )
90 }
91 Self::StderrTask(message) => write!(formatter, "stderr reader failed: {message}"),
92 }
93 }
94}
95
96impl std::error::Error for Error {
97 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
98 match self {
99 Self::Io(error) => Some(error),
100 Self::Json(error) => Some(error),
101 _ => None,
102 }
103 }
104}
105
106impl From<io::Error> for Error {
107 fn from(error: io::Error) -> Self {
108 Self::Io(error)
109 }
110}
111
112impl From<serde_json::Error> for Error {
113 fn from(error: serde_json::Error) -> Self {
114 Self::Json(error)
115 }
116}
117
118pub struct StdioRpc {
119 child: Child,
120 stdin: Option<ChildStdin>,
121 stdout: BufReader<ChildStdout>,
122 stderr_task: Option<JoinHandle<io::Result<Vec<u8>>>>,
123 next_id: u64,
124 exit: Option<ProcessExit>,
125}
126
127impl StdioRpc {
128 pub fn spawn(mut command: Command) -> Result<Self, Error> {
129 command
130 .stdin(Stdio::piped())
131 .stdout(Stdio::piped())
132 .stderr(Stdio::piped())
133 .kill_on_drop(true);
134 let mut child = command.spawn()?;
135 let stdin = child
136 .stdin
137 .take()
138 .ok_or_else(|| Error::InvalidMessage("child stdin was not piped".into()))?;
139 let stdout = child
140 .stdout
141 .take()
142 .ok_or_else(|| Error::InvalidMessage("child stdout was not piped".into()))?;
143 let mut stderr = child
144 .stderr
145 .take()
146 .ok_or_else(|| Error::InvalidMessage("child stderr was not piped".into()))?;
147 let stderr_task = tokio::spawn(async move {
148 let mut captured = Vec::new();
149 let mut buffer = [0_u8; 8192];
150 loop {
151 let read = stderr.read(&mut buffer).await?;
152 if read == 0 {
153 return Ok(captured);
154 }
155 let remaining = MAX_STDERR_CAPTURE_BYTES.saturating_sub(captured.len());
156 captured.extend_from_slice(&buffer[..read.min(remaining)]);
157 }
158 });
159 Ok(Self {
160 child,
161 stdin: Some(stdin),
162 stdout: BufReader::new(stdout),
163 stderr_task: Some(stderr_task),
164 next_id: 1,
165 exit: None,
166 })
167 }
168
169 pub async fn send_request(
170 &mut self,
171 method: impl Into<String>,
172 params: Value,
173 ) -> Result<RequestId, Error> {
174 let id = RequestId(self.next_id);
175 self.next_id = self
176 .next_id
177 .checked_add(1)
178 .ok_or_else(|| Error::InvalidMessage("local request ID exhausted".into()))?;
179 self.send(json!({"id": id.0, "method": method.into(), "params": params}))
180 .await?;
181 Ok(id)
182 }
183
184 pub async fn send_notification(
185 &mut self,
186 method: impl Into<String>,
187 params: Value,
188 ) -> Result<(), Error> {
189 self.send(json!({"method": method.into(), "params": params}))
190 .await
191 }
192
193 pub async fn next(&mut self) -> Result<IncomingMessage, Error> {
194 let Some(line) = self.read_line().await? else {
195 return Err(Error::ProcessExited(self.finish_exit().await?));
196 };
197 parse_message(serde_json::from_slice(&line)?)
198 }
199
200 pub async fn respond(&mut self, id: PeerId, result: Value) -> Result<(), Error> {
201 self.send(json!({"id": id.into_value(), "result": result}))
202 .await
203 }
204
205 pub async fn respond_error(
206 &mut self,
207 id: PeerId,
208 code: i64,
209 message: impl Into<String>,
210 data: Option<Value>,
211 ) -> Result<(), Error> {
212 let mut error = Map::new();
213 error.insert("code".into(), Value::Number(code.into()));
214 error.insert("message".into(), Value::String(message.into()));
215 if let Some(data) = data {
216 error.insert("data".into(), data);
217 }
218 self.send(json!({"id": id.into_value(), "error": error}))
219 .await
220 }
221
222 pub async fn shutdown(mut self) -> Result<ProcessExit, Error> {
223 self.stdin.take();
224 if self.child.try_wait()?.is_none()
225 && let Err(error) = self.child.start_kill()
226 && self.child.try_wait()?.is_none()
227 {
228 return Err(Error::Io(error));
229 }
230 self.finish_exit().await
231 }
232
233 async fn send(&mut self, value: Value) -> Result<(), Error> {
234 let mut bytes = serde_json::to_vec(&value)?;
235 bytes.push(b'\n');
236 let stdin = self
237 .stdin
238 .as_mut()
239 .ok_or_else(|| Error::InvalidMessage("child stdin is closed".into()))?;
240 stdin.write_all(&bytes).await?;
241 stdin.flush().await?;
242 Ok(())
243 }
244
245 async fn read_line(&mut self) -> Result<Option<Vec<u8>>, Error> {
246 let mut line = Vec::new();
247 let mut too_long = false;
248 let mut saw_bytes = false;
249 loop {
250 let (consumed, ended, empty) = {
251 let available = self.stdout.fill_buf().await?;
252 if available.is_empty() {
253 (0, false, true)
254 } else {
255 saw_bytes = true;
256 let newline = available.iter().position(|byte| *byte == b'\n');
257 let content = newline.unwrap_or(available.len());
258 if !too_long {
259 if line.len() + content > MAX_INBOUND_LINE_BYTES {
260 too_long = true;
261 } else {
262 line.extend_from_slice(&available[..content]);
263 }
264 }
265 (
266 content + usize::from(newline.is_some()),
267 newline.is_some(),
268 false,
269 )
270 }
271 };
272 if empty {
273 if too_long {
274 return Err(Error::InboundLineTooLong);
275 }
276 return if saw_bytes { Ok(Some(line)) } else { Ok(None) };
277 }
278 self.stdout.consume(consumed);
279 if ended {
280 return if too_long {
281 Err(Error::InboundLineTooLong)
282 } else {
283 Ok(Some(line))
284 };
285 }
286 }
287 }
288
289 async fn finish_exit(&mut self) -> Result<ProcessExit, Error> {
290 if let Some(exit) = &self.exit {
291 return Ok(exit.clone());
292 }
293 let status = self.child.wait().await?;
294 let task = self
295 .stderr_task
296 .take()
297 .ok_or_else(|| Error::StderrTask("stderr result was already consumed".into()))?;
298 let bytes = task
299 .await
300 .map_err(|error| Error::StderrTask(error.to_string()))??;
301 let exit = ProcessExit {
302 status,
303 stderr: String::from_utf8_lossy(&bytes).into_owned(),
304 };
305 self.exit = Some(exit.clone());
306 Ok(exit)
307 }
308}
309
310fn parse_message(value: Value) -> Result<IncomingMessage, Error> {
311 let Value::Object(mut fields) = value else {
312 return Err(Error::InvalidMessage("top level is not an object".into()));
313 };
314 if fields.remove("jsonrpc").is_some() {
315 return Err(Error::InvalidMessage(
316 "jsonrpc version member is not used on this wire".into(),
317 ));
318 }
319 let has_method = fields.contains_key("method");
320 let has_result = fields.contains_key("result");
321 let has_error = fields.contains_key("error");
322 if has_method {
323 if has_result || has_error {
324 return Err(Error::InvalidMessage(
325 "method message also contains a response".into(),
326 ));
327 }
328 let method = fields
329 .remove("method")
330 .and_then(|value| value.as_str().map(str::to_owned))
331 .ok_or_else(|| Error::InvalidMessage("method is not a string".into()))?;
332 let params = fields.remove("params").unwrap_or(Value::Null);
333 return match fields.remove("id") {
334 Some(id) => Ok(IncomingMessage::Request {
335 id: parse_peer_id(id)?,
336 method,
337 params,
338 }),
339 None => Ok(IncomingMessage::Notification { method, params }),
340 };
341 }
342 if fields.contains_key("params") {
343 return Err(Error::InvalidMessage("response contains params".into()));
344 }
345 if has_result == has_error {
346 return Err(Error::InvalidMessage(
347 "response must contain exactly one of result or error".into(),
348 ));
349 }
350 let id = fields
351 .remove("id")
352 .ok_or_else(|| Error::InvalidMessage("response has no id".into()))?;
353 let result = if has_result {
354 Ok(fields.remove("result").expect("result presence checked"))
355 } else {
356 Err(parse_rpc_error(
357 fields.remove("error").expect("error presence checked"),
358 )?)
359 };
360 Ok(IncomingMessage::Response {
361 id: parse_peer_id(id)?,
362 result,
363 })
364}
365
366fn parse_peer_id(value: Value) -> Result<PeerId, Error> {
367 match value {
368 Value::Number(number) if number.as_i64().is_some() || number.as_u64().is_some() => {
369 Ok(PeerId::Number(number))
370 }
371 Value::String(string) => Ok(PeerId::String(string)),
372 _ => Err(Error::InvalidMessage(
373 "id is not an integer or string".into(),
374 )),
375 }
376}
377
378fn parse_rpc_error(value: Value) -> Result<RpcError, Error> {
379 let Value::Object(mut fields) = value else {
380 return Err(Error::InvalidMessage("error is not an object".into()));
381 };
382 let code = fields
383 .remove("code")
384 .and_then(|value| value.as_i64())
385 .ok_or_else(|| Error::InvalidMessage("error code is not an integer".into()))?;
386 let message = fields
387 .remove("message")
388 .and_then(|value| value.as_str().map(str::to_owned))
389 .ok_or_else(|| Error::InvalidMessage("error message is not a string".into()))?;
390 Ok(RpcError {
391 code,
392 message,
393 data: fields.remove("data"),
394 })
395}