1use std::pin::pin;
2
3use crate::RawJsonRpcResponse as Response;
5use crate::jsonrpc::{RawJsonRpcMessage, TransportBatch, TransportBatchEntry, TransportFrame};
6use futures::StreamExt as _;
7use futures::channel::mpsc;
8use serde::Deserialize as _;
9
10enum ParsedIncomingLine {
11 Single(RawJsonRpcMessage),
12 Malformed { raw: String, error: crate::Error },
13 Batch(TransportBatch),
14}
15
16fn parse_incoming_line(line: &str) -> ParsedIncomingLine {
17 let value = match serde_json::from_str::<serde_json::Value>(line) {
18 Ok(value) => value,
19 Err(error) => {
20 tracing::debug!(
22 category = ?error.classify(),
23 line = error.line(),
24 column = error.column(),
25 "Failed to parse incoming JSON-RPC JSON"
26 );
27 return ParsedIncomingLine::Malformed {
28 raw: line.to_owned(),
29 error: crate::Error::parse_error().data(serde_json::json!({ "line": line })),
30 };
31 }
32 };
33
34 match value {
35 serde_json::Value::Array(entries) if entries.is_empty() => ParsedIncomingLine::Malformed {
36 raw: line.to_owned(),
37 error: crate::Error::invalid_request(),
38 },
39 serde_json::Value::Array(entries) => {
40 let entries = entries
41 .into_iter()
42 .map(|entry| match RawJsonRpcMessage::deserialize(&entry) {
43 Ok(message) => TransportBatchEntry::message(message),
44 Err(error) => {
45 tracing::debug!(
46 category = ?error.classify(),
47 "Invalid JSON-RPC batch entry"
48 );
49 TransportBatchEntry::malformed(entry, crate::Error::invalid_request())
50 }
51 })
52 .collect::<Vec<_>>();
53
54 ParsedIncomingLine::Batch(
55 TransportBatch::from_entries(entries)
56 .expect("a parsed non-empty JSON array retains at least one entry"),
57 )
58 }
59 value => match serde_json::from_value(value) {
60 Ok(message) => ParsedIncomingLine::Single(message),
61 Err(error) => {
62 tracing::debug!(category = ?error.classify(), "Invalid JSON-RPC message");
63 ParsedIncomingLine::Malformed {
64 raw: line.to_owned(),
65 error: crate::Error::invalid_request(),
66 }
67 }
68 },
69 }
70}
71
72impl TransportFrame {
73 #[must_use]
81 pub fn parse_json(input: &str) -> Self {
82 match parse_incoming_line(input) {
83 ParsedIncomingLine::Single(message) => Self::Single(message),
84 ParsedIncomingLine::Malformed { raw, error } => Self::Malformed { raw, error },
85 ParsedIncomingLine::Batch(batch) => Self::Batch(batch),
86 }
87 }
88
89 pub fn to_json(&self) -> Result<String, crate::Error> {
96 match self {
97 Self::Single(message) => {
98 serde_json::to_string(message).map_err(crate::Error::into_internal_error)
99 }
100 Self::Malformed { raw, .. } => Ok(raw.clone()),
101 Self::Batch(batch) => {
102 serde_json::to_string(batch).map_err(crate::Error::into_internal_error)
103 }
104 }
105 }
106}
107
108async fn transport_outgoing_frames_actor(
122 transport_rx: impl futures::Stream<Item = TransportFrame>,
123 outgoing_lines: impl futures::Sink<String, Error = std::io::Error>,
124) -> Result<(), crate::Error> {
125 use futures::SinkExt;
126 let mut transport_rx = pin!(transport_rx);
127 let mut outgoing_lines = pin!(outgoing_lines);
128
129 while let Some(frame) = transport_rx.next().await {
130 let json_rpc_message = match frame {
131 TransportFrame::Single(message) => message,
132 TransportFrame::Malformed { raw, .. } => {
133 let raw = malformed_line_value(raw)?;
134 tracing::trace!(
135 message = %crate::util::redacted_json_line(&raw),
136 "Relaying invalid JSON-RPC value"
137 );
138 outgoing_lines
139 .send(raw)
140 .await
141 .map_err(crate::Error::into_internal_error)?;
142 continue;
143 }
144 TransportFrame::Batch(batch) => {
145 let line =
146 serde_json::to_string(&batch).map_err(crate::Error::into_internal_error)?;
147 tracing::trace!(message = %crate::util::redacted_json_line(&line), "Sending JSON-RPC batch");
148 outgoing_lines
149 .send(line)
150 .await
151 .map_err(crate::Error::into_internal_error)?;
152 continue;
153 }
154 };
155 match serde_json::to_string(&json_rpc_message) {
156 Ok(line) => {
157 tracing::trace!(message = %crate::util::redacted_json_line(&line), "Sending JSON-RPC message");
158 outgoing_lines
159 .send(line)
160 .await
161 .map_err(crate::Error::into_internal_error)?;
162 }
163
164 Err(serialization_error) => {
165 match json_rpc_message {
166 RawJsonRpcMessage::Request(_) | RawJsonRpcMessage::Notification(_) => {
167 tracing::error!(
172 ?serialization_error,
173 "Failed to serialize request, ignoring"
174 );
175 }
176 RawJsonRpcMessage::Response(response) => {
177 let id = match response {
180 Response::Result { id, .. } | Response::Error { id, .. } => id,
181 };
182 tracing::error!(
183 ?serialization_error,
184 ?id,
185 "Failed to serialize response, sending internal_error instead"
186 );
187 let error_line = serde_json::to_string(&RawJsonRpcMessage::response(
188 id,
189 Err(crate::Error::internal_error()),
190 ))
191 .unwrap();
192 outgoing_lines
193 .send(error_line)
194 .await
195 .map_err(crate::Error::into_internal_error)?;
196 }
197 }
198 }
199 }
200 }
201 outgoing_lines
202 .close()
203 .await
204 .map_err(crate::Error::into_internal_error)
205}
206
207pub(super) struct LineWriter<W> {
210 writer: std::pin::Pin<Box<W>>,
211 bytes: Vec<u8>,
212 written: usize,
213 closing: bool,
214}
215
216impl<W> LineWriter<W> {
217 pub(super) fn new(writer: W) -> Self {
218 Self {
219 writer: Box::pin(writer),
220 bytes: Vec::new(),
221 written: 0,
222 closing: false,
223 }
224 }
225}
226
227impl<W: futures::AsyncWrite> futures::Sink<String> for LineWriter<W> {
228 type Error = std::io::Error;
229
230 fn poll_ready(
231 self: std::pin::Pin<&mut Self>,
232 cx: &mut std::task::Context<'_>,
233 ) -> std::task::Poll<Result<(), Self::Error>> {
234 self.poll_flush(cx)
235 }
236
237 fn start_send(self: std::pin::Pin<&mut Self>, line: String) -> Result<(), Self::Error> {
238 let this = self.get_mut();
239 this.bytes = line.into_bytes();
240 this.bytes.push(b'\n');
241 this.written = 0;
242 Ok(())
243 }
244
245 fn poll_flush(
246 self: std::pin::Pin<&mut Self>,
247 cx: &mut std::task::Context<'_>,
248 ) -> std::task::Poll<Result<(), Self::Error>> {
249 let this = self.get_mut();
250 while this.written < this.bytes.len() {
251 let count = futures::ready!(
252 this.writer
253 .as_mut()
254 .poll_write(cx, &this.bytes[this.written..])
255 )?;
256 if count == 0 {
257 return std::task::Poll::Ready(Err(std::io::ErrorKind::WriteZero.into()));
258 }
259 this.written += count;
260 }
261 this.bytes.clear();
262 this.written = 0;
263 this.writer.as_mut().poll_flush(cx)
264 }
265
266 fn poll_close(
267 mut self: std::pin::Pin<&mut Self>,
268 cx: &mut std::task::Context<'_>,
269 ) -> std::task::Poll<Result<(), Self::Error>> {
270 if !self.closing {
271 futures::ready!(self.as_mut().poll_flush(cx))?;
272 self.closing = true;
273 }
274 self.get_mut().writer.as_mut().poll_close(cx)
275 }
276}
277
278fn malformed_line_value(raw: String) -> Result<String, crate::Error> {
279 if !raw.contains('\r') && !raw.contains('\n') {
280 return Ok(raw);
281 }
282
283 match serde_json::from_str::<serde_json::Value>(&raw) {
284 Ok(value) => serde_json::to_string(&value),
285 Err(_) => serde_json::to_string(&raw),
286 }
287 .map_err(crate::Error::into_internal_error)
288}
289
290pub(super) async fn transport_outgoing_lines_actor(
291 transport_rx: impl futures::Stream<Item = TransportFrame>,
292 outgoing_lines: impl futures::Sink<String, Error = std::io::Error>,
293) -> Result<(), crate::Error> {
294 transport_outgoing_frames_actor(transport_rx, outgoing_lines).await
295}
296
297pub(super) async fn transport_incoming_lines_actor(
310 incoming_lines: impl futures::Stream<Item = std::io::Result<String>>,
311 transport_tx: mpsc::UnboundedSender<TransportFrame>,
312) -> Result<(), crate::Error> {
313 let mut incoming_lines = pin!(incoming_lines);
314 while let Some(line_result) = incoming_lines.next().await {
315 let line = line_result.map_err(crate::Error::into_internal_error)?;
316 tracing::trace!(message = %crate::util::redacted_json_line(&line), "Received JSON-RPC message");
317
318 match parse_incoming_line(&line) {
319 ParsedIncomingLine::Single(message) => {
320 transport_tx
321 .unbounded_send(TransportFrame::Single(message))
322 .map_err(crate::Error::into_internal_error)?;
323 }
324 ParsedIncomingLine::Malformed { raw, error } => {
325 transport_tx
326 .unbounded_send(TransportFrame::Malformed { raw, error })
327 .map_err(crate::Error::into_internal_error)?;
328 }
329 ParsedIncomingLine::Batch(entries) => {
330 transport_tx
331 .unbounded_send(TransportFrame::Batch(entries))
332 .map_err(crate::Error::into_internal_error)?;
333 }
334 }
335 }
336 Ok(())
337}
338
339#[cfg(test)]
340mod tests {
341 use std::sync::{Arc, Mutex};
342
343 use super::*;
344 use crate::ErrorCode;
345
346 #[derive(Default)]
347 struct PendingCloseWriter {
348 bytes: Vec<u8>,
349 close_polls: usize,
350 }
351
352 impl futures::AsyncWrite for PendingCloseWriter {
353 fn poll_write(
354 mut self: std::pin::Pin<&mut Self>,
355 _: &mut std::task::Context<'_>,
356 bytes: &[u8],
357 ) -> std::task::Poll<std::io::Result<usize>> {
358 self.bytes.extend_from_slice(bytes);
359 std::task::Poll::Ready(Ok(bytes.len()))
360 }
361
362 fn poll_flush(
363 self: std::pin::Pin<&mut Self>,
364 _: &mut std::task::Context<'_>,
365 ) -> std::task::Poll<std::io::Result<()>> {
366 assert_eq!(self.close_polls, 0, "do not flush again during shutdown");
367 std::task::Poll::Ready(Ok(()))
368 }
369
370 fn poll_close(
371 mut self: std::pin::Pin<&mut Self>,
372 cx: &mut std::task::Context<'_>,
373 ) -> std::task::Poll<std::io::Result<()>> {
374 self.close_polls += 1;
375 if self.close_polls == 1 {
376 cx.waker().wake_by_ref();
377 std::task::Poll::Pending
378 } else {
379 std::task::Poll::Ready(Ok(()))
380 }
381 }
382 }
383
384 #[test]
385 fn byte_writer_flushes_then_continues_pending_shutdown_without_reflushing() {
386 use futures::SinkExt as _;
387 let mut sink = LineWriter::new(PendingCloseWriter::default());
388 futures::executor::block_on(async {
389 sink.send("line".to_string()).await.unwrap();
390 sink.close().await.unwrap();
391 });
392 assert_eq!(sink.writer.bytes, b"line\n");
393 assert_eq!(sink.writer.close_polls, 2);
394 }
395
396 #[test]
397 fn parses_batch_entries_independently() {
398 let ParsedIncomingLine::Batch(batch) = parse_incoming_line(
399 r#"[
400 {"jsonrpc":"2.0","id":1,"method":"one","params":{}},
401 17,
402 {"jsonrpc":"2.0","method":"two","params":{}}
403 ]"#,
404 ) else {
405 panic!("expected a JSON-RPC batch");
406 };
407
408 let entries = batch.iter_results().collect::<Vec<_>>();
409 assert_eq!(entries.len(), 3);
410 assert!(matches!(entries[0], Ok(RawJsonRpcMessage::Request(_))));
411 assert_eq!(entries[1].unwrap_err().code, ErrorCode::InvalidRequest);
412 assert!(matches!(entries[2], Ok(RawJsonRpcMessage::Notification(_))));
413 }
414
415 #[test]
416 fn preserves_every_invalid_member_of_response_batches() {
417 let ParsedIncomingLine::Batch(batch) = parse_incoming_line(
418 r#"[
419 {"jsonrpc":"2.0","id":1,"result":{"ok":true}},
420 17,
421 {"jsonrpc":"2.0","id":2,"result":null,"error":{"code":-32603,"message":"Internal error"}},
422 {"jsonrpc":"2.0","id":3,"error":{"code":-32603,"message":"Internal error"}}
423 ]"#,
424 ) else {
425 panic!("expected a JSON-RPC batch");
426 };
427
428 let entries = batch.iter_results().collect::<Vec<_>>();
429 assert_eq!(entries.len(), 4);
430 assert!(matches!(entries[0], Ok(RawJsonRpcMessage::Response(_))));
431 assert_eq!(entries[1].unwrap_err().code, ErrorCode::InvalidRequest);
432 assert_eq!(entries[2].unwrap_err().code, ErrorCode::InvalidRequest);
433 assert!(matches!(entries[3], Ok(RawJsonRpcMessage::Response(_))));
434 }
435
436 #[test]
437 fn preserves_invalid_value_beside_malformed_response() {
438 let ParsedIncomingLine::Batch(batch) = parse_incoming_line(
439 r#"[
440 17,
441 {"jsonrpc":"2.0","id":1,"result":null,"error":{"code":-32603,"message":"Internal error"}}
442 ]"#,
443 ) else {
444 panic!("expected a JSON-RPC batch");
445 };
446
447 let entries = batch.iter_results().collect::<Vec<_>>();
448 assert_eq!(entries.len(), 2);
449 assert_eq!(entries[0].unwrap_err().code, ErrorCode::InvalidRequest);
450 assert_eq!(entries[1].unwrap_err().code, ErrorCode::InvalidRequest);
451 }
452
453 #[test]
454 fn preserves_entirely_malformed_response_shaped_batch() {
455 let ParsedIncomingLine::Batch(batch) = parse_incoming_line(
456 r#"[
457 {"jsonrpc":"2.0","id":1,"result":null,"error":{"code":-32603,"message":"Internal error"}}
458 ]"#,
459 ) else {
460 panic!("expected a retained JSON-RPC batch");
461 };
462
463 let entries = batch.iter_results().collect::<Vec<_>>();
464 assert_eq!(entries.len(), 1);
465 assert_eq!(entries[0].unwrap_err().code, ErrorCode::InvalidRequest);
466 }
467
468 #[test]
469 fn preserves_invalid_call_shaped_member_beside_response() {
470 let ParsedIncomingLine::Batch(batch) = parse_incoming_line(
471 r#"[
472 {"jsonrpc":"2.0","id":1,"result":null},
473 {"jsonrpc":"2.0","method":1}
474 ]"#,
475 ) else {
476 panic!("expected a JSON-RPC batch");
477 };
478
479 let entries = batch.iter_results().collect::<Vec<_>>();
480 assert_eq!(entries.len(), 2);
481 assert!(matches!(entries[0], Ok(RawJsonRpcMessage::Response(_))));
482 assert_eq!(entries[1].unwrap_err().code, ErrorCode::InvalidRequest);
483 }
484
485 #[test]
486 fn preserves_malformed_response_shaped_member_beside_request() {
487 let ParsedIncomingLine::Batch(batch) = parse_incoming_line(
488 r#"[
489 {"jsonrpc":"2.0","id":1,"method":"one","params":{}},
490 {"jsonrpc":"2.0","id":2,"result":null,"error":{"code":-32603,"message":"Internal error"}}
491 ]"#,
492 ) else {
493 panic!("expected a JSON-RPC batch");
494 };
495
496 let entries = batch.iter_results().collect::<Vec<_>>();
497 assert_eq!(entries.len(), 2);
498 assert!(matches!(entries[0], Ok(RawJsonRpcMessage::Request(_))));
499 assert_eq!(entries[1].unwrap_err().code, ErrorCode::InvalidRequest);
500 }
501
502 #[test]
503 fn preserves_malformed_standalone_response() {
504 let ParsedIncomingLine::Malformed { error, .. } = parse_incoming_line(
505 r#"{"jsonrpc":"2.0","id":1,"result":null,"error":{"code":-32603,"message":"Internal error"}}"#,
506 ) else {
507 panic!("expected one retained invalid response");
508 };
509
510 assert_eq!(error.code, ErrorCode::InvalidRequest);
511 }
512
513 #[test]
514 fn preserves_malformed_call_shaped_standalone_message() {
515 let ParsedIncomingLine::Malformed { error, .. } =
516 parse_incoming_line(r#"{"jsonrpc":"2.0","id":1,"method":"one","result":null}"#)
517 else {
518 panic!("expected one invalid-request error");
519 };
520
521 assert_eq!(error.code, ErrorCode::InvalidRequest);
522 }
523
524 #[test]
525 fn parses_valid_standalone_response() {
526 assert!(matches!(
527 parse_incoming_line(r#"{"jsonrpc":"2.0","id":1,"result":{"ok":true}}"#),
528 ParsedIncomingLine::Single(RawJsonRpcMessage::Response(_))
529 ));
530 }
531
532 #[test]
533 fn all_invalid_batch_defaults_to_call_errors() {
534 let ParsedIncomingLine::Batch(batch) = parse_incoming_line("[1, 2, 3]") else {
535 panic!("expected a JSON-RPC batch");
536 };
537
538 assert_eq!(batch.len(), 3);
539 assert!(
540 batch
541 .iter_results()
542 .all(|entry| entry.unwrap_err().code == ErrorCode::InvalidRequest)
543 );
544 }
545
546 #[test]
547 fn empty_batch_is_an_invalid_request() {
548 let ParsedIncomingLine::Malformed { raw, error } = parse_incoming_line("[]") else {
549 panic!("expected one invalid-request error");
550 };
551
552 assert_eq!(raw, "[]");
553 assert_eq!(error.code, ErrorCode::InvalidRequest);
554 }
555
556 #[test]
557 fn malformed_json_is_a_parse_error() {
558 let ParsedIncomingLine::Malformed { raw, error } = parse_incoming_line("[") else {
559 panic!("expected one parse error");
560 };
561
562 assert_eq!(raw, "[");
563 assert_eq!(error.code, ErrorCode::ParseError);
564 }
565
566 #[test]
567 fn valid_json_with_an_invalid_envelope_is_an_invalid_request() {
568 let ParsedIncomingLine::Malformed { raw, error } = parse_incoming_line("17") else {
569 panic!("expected one invalid-request error");
570 };
571
572 assert_eq!(raw, "17");
573 assert_eq!(error.code, ErrorCode::InvalidRequest);
574 }
575
576 #[tokio::test]
577 async fn multiline_malformed_frame_is_written_as_one_line_value() {
578 let raw = "not json\r\n{\"jsonrpc\":\"2.0\",\"method\":\"injected\"}".to_string();
579 let captured = Arc::new(Mutex::new(Vec::new()));
580 let outgoing = futures::sink::unfold(captured.clone(), |captured, line| async move {
581 captured.lock().unwrap().push(line);
582 Ok::<_, std::io::Error>(captured)
583 });
584
585 transport_outgoing_frames_actor(
586 futures::stream::iter([TransportFrame::Malformed {
587 raw: raw.clone(),
588 error: crate::Error::parse_error(),
589 }]),
590 outgoing,
591 )
592 .await
593 .unwrap();
594
595 let lines = captured.lock().unwrap();
596 assert_eq!(lines.len(), 1);
597 assert!(!lines[0].contains('\r') && !lines[0].contains('\n'));
598 assert_eq!(serde_json::from_str::<String>(&lines[0]).unwrap(), raw);
599 }
600
601 #[test]
602 fn multiline_invalid_json_rpc_value_is_compacted_without_changing_value() {
603 let raw = "{\n \"jsonrpc\": \"2.0\",\n \"method\": 1\n}".to_string();
604 let expected = serde_json::from_str::<serde_json::Value>(&raw).unwrap();
605 let line = malformed_line_value(raw).unwrap();
606
607 assert!(!line.contains('\r') && !line.contains('\n'));
608 assert_eq!(
609 serde_json::from_str::<serde_json::Value>(&line).unwrap(),
610 expected
611 );
612 }
613}