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