use std::io::BufRead;
use serde::{Deserialize, Serialize};
use tokio_util::{bytes::BytesMut, codec::Decoder};
use super::{DapCodec, Frame};
impl<T: Serialize + for<'a> Deserialize<'a> + PartialEq> Decoder for DapCodec<T> {
type Item = Frame<T>;
type Error = std::io::Error;
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
if !self.header_received {
while let Some(line) = read_line(src) {
let line = line.inspect_err(|_| {
let _ = src.split_to((&src[..]).skip_until(b'\n').unwrap_or(0));
})?;
if line.is_empty() {
let _ = src.split_to(line.len() + b"\r\n".len());
if self.length.is_some() {
self.header_received = true;
break;
}
} else if let Some(length) = get_content_len(&line) {
self.length = Some(length);
src.reserve(length + b"\r\n".len());
let _ = src.split_to(line.len() + b"\r\n".len());
} else {
tracing::warn!("Unknown header payload: {line}");
let _ = src.split_to(line.len() + b"\r\n".len());
}
}
}
if self.header_received {
let Some(length) = self.length else {
return Ok(None);
};
if src.len() < length {
return Ok(None);
}
self.header_received = false;
self.length = None;
return Ok(Some(Frame {
content: serde_json::from_slice::<T>(&src.split_to(length))?,
}));
}
Ok(None)
}
}
fn read_line(bytes: &mut BytesMut) -> Option<Result<String, std::io::Error>> {
let mut buf = String::new();
match (&bytes[..]).read_line(&mut buf) {
Ok(0) => None,
Ok(_n) => {
if buf.ends_with('\n') {
buf.pop();
if buf.ends_with('\r') {
buf.pop();
}
} else {
return None;
}
Some(Ok(buf))
}
Err(e) => Some(Err(e)),
}
}
pub(crate) fn get_content_len(header: &str) -> Option<usize> {
let mut parts = header.trim_end().split_ascii_whitespace();
let name = parts.next()?;
if name.ends_with("Content-Length:") {
parts.next()?.parse::<usize>().ok()
} else {
None
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use std::io::ErrorKind;
use pretty_assertions::assert_eq;
use serde_json::Value;
use serde_json::json;
use tokio_util::{bytes::BytesMut, codec::Decoder};
use crate::cmd::dap_server::debug_adapter::codec::decoder::Frame;
use super::DapCodec;
use super::get_content_len;
#[test]
fn good_single_empty_frame() {
let mut decoder = DapCodec::<Value>::new();
let mut buf = BytesMut::new();
let content = json!({"seq": 3, "type": "request", "command": "test"});
let payload = serde_json::to_string_pretty(&content).unwrap();
buf.extend_from_slice(format!("Content-Length: {}\r\n", payload.len()).as_bytes());
buf.extend_from_slice("\r\n".as_bytes());
buf.extend_from_slice(payload.as_bytes());
let result = decoder.decode(&mut buf);
assert_eq!(result.unwrap().unwrap(), Frame { content });
assert_eq!(buf.len(), 0)
}
#[test]
fn good_split_frame() {
let mut decoder = DapCodec::<Value>::new();
let mut buf = BytesMut::new();
let content = json!({"seq": 3, "type": "request", "command": "test"});
let payload = serde_json::to_string_pretty(&content).unwrap();
buf.extend_from_slice(format!("Content-Length: {}\r\n", payload.len()).as_bytes());
buf.extend_from_slice("\r\n".as_bytes());
let result = decoder.decode(&mut buf);
assert_eq!(result.unwrap(), None);
buf.extend_from_slice(payload.as_bytes());
let result = decoder.decode(&mut buf);
assert_eq!(result.unwrap().unwrap(), Frame { content });
assert_eq!(buf.len(), 0)
}
#[test]
fn good_split_frame_2() {
let mut decoder = DapCodec::<Value>::new();
let mut buf = BytesMut::new();
let content = json!({"seq": 3, "type": "request", "command": "test"});
let payload = serde_json::to_string_pretty(&content).unwrap();
buf.extend_from_slice(format!("Content-Length: {}\r\n", payload.len()).as_bytes());
buf.extend_from_slice("\r\n".as_bytes());
buf.extend_from_slice(&payload.as_bytes()[..payload.len() / 2]);
let result = decoder.decode(&mut buf);
assert_eq!(result.unwrap(), None);
buf.extend_from_slice(&payload.as_bytes()[payload.len() / 2..]);
let result = decoder.decode(&mut buf);
assert_eq!(result.unwrap().unwrap(), Frame { content });
assert_eq!(buf.len(), 0)
}
#[test]
fn bad_frame_wrong_length() {
let mut decoder = DapCodec::<Value>::new();
let mut buf = BytesMut::new();
let content = json!({"seq": 3, "type": "request", "command": "test"});
let payload = serde_json::to_string_pretty(&content).unwrap();
buf.extend_from_slice(format!("Content-Length: {}\r\n", payload.len() + 10).as_bytes());
buf.extend_from_slice("\r\n".as_bytes());
buf.extend_from_slice(payload.as_bytes());
let result = decoder.decode(&mut buf);
assert_eq!(result.unwrap(), None);
assert_eq!(buf.len(), 56)
}
#[test]
fn bad_frame_invalid_json() {
let mut decoder = DapCodec::<Value>::new();
let mut buf = BytesMut::new();
let payload = "{\"test:}";
buf.extend_from_slice(format!("Content-Length: {}\r\n", payload.len()).as_bytes());
buf.extend_from_slice("\r\n".as_bytes());
buf.extend_from_slice(payload.as_bytes());
let result = decoder.decode(&mut buf);
assert_eq!(result.unwrap_err().kind(), ErrorKind::UnexpectedEof);
assert_eq!(buf.len(), 0)
}
#[test]
fn bad_frame_no_utf8() {
let mut decoder = DapCodec::<Value>::new();
let mut buf = BytesMut::new();
let payload = "";
buf.extend_from_slice(&[5, 189, 250, 130, 4, b'\r', b'\n']);
buf.extend_from_slice("\r\n".as_bytes());
buf.extend_from_slice(payload.as_bytes());
let result = decoder.decode(&mut buf);
assert_eq!(result.unwrap_err().kind(), ErrorKind::InvalidData);
assert_eq!(buf.len(), 2);
let result = decoder.decode(&mut buf);
assert_eq!(result.unwrap(), None);
assert_eq!(buf.len(), 0);
}
#[test]
fn bad_frame_no_length() {
let mut decoder = DapCodec::<Value>::new();
let mut buf = BytesMut::new();
let content = json!({"seq": 3, "type": "request", "command": "test"});
let payload = serde_json::to_string_pretty(&content).unwrap();
buf.extend_from_slice("\r\n".as_bytes());
buf.extend_from_slice(payload.as_bytes());
let result = decoder.decode(&mut buf);
assert_eq!(result.unwrap(), None);
assert_eq!(buf.len(), 0)
}
#[test]
fn recover_from_bad_frame() {
let mut decoder = DapCodec::<Value>::new();
let mut buf = BytesMut::new();
let content = json!({"seq": 3, "type": "request", "command": "test"});
let payload = serde_json::to_string_pretty(&content).unwrap();
buf.extend_from_slice("\r\n".as_bytes());
buf.extend_from_slice(payload.as_bytes());
buf.extend_from_slice(format!("Content-Length: {}\r\n", payload.len()).as_bytes());
buf.extend_from_slice("\r\n".as_bytes());
buf.extend_from_slice(payload.as_bytes());
let result = decoder.decode(&mut buf);
assert_eq!(result.unwrap().unwrap(), Frame { content });
assert_eq!(buf.len(), 0);
let result = decoder.decode(&mut buf);
assert_eq!(result.unwrap(), None);
}
#[test]
fn parse_valid_length_header() {
let header = "Content-Length: 234\r\n";
assert_eq!(234, get_content_len(header).unwrap());
}
#[test]
fn parse_valid_length_header_with_prepended_data() {
let header = "asdasdContent-Length: 234\r\n";
assert_eq!(234, get_content_len(header).unwrap());
}
#[test]
fn parse_invalid_length_header_with_prepended_data() {
let header = "asdasd Content-Length: 234\r\n";
assert!(get_content_len(header).is_none());
}
#[test]
fn parse_valid_length_header_with_prepended_data_2() {
let header = "asdasd: Content-Length: 234\r\n";
assert!(get_content_len(header).is_none());
}
#[test]
fn parse_invalid_length_header() {
let header = "Content: 234\r\n";
assert!(get_content_len(header).is_none());
}
}