use std::io::{self, BufRead, Write};
pub fn read_message<R: BufRead>(reader: &mut R) -> io::Result<Option<(Vec<u8>, WireFormat)>> {
let mut first_line = String::new();
let bytes_read = reader.read_line(&mut first_line)?;
if bytes_read == 0 {
return Ok(None); }
let trimmed = first_line.trim();
if trimmed.starts_with("Content-Length:") {
let content_length: usize = trimmed
.strip_prefix("Content-Length:")
.and_then(|s| s.trim().parse().ok())
.ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidData, "Invalid Content-Length header")
})?;
loop {
let mut header_line = String::new();
let header_bytes = reader.read_line(&mut header_line)?;
if header_bytes == 0 {
return Ok(None); }
if header_line.trim().is_empty() {
break; }
}
let mut body = vec![0u8; content_length];
reader.read_exact(&mut body)?;
Ok(Some((body, WireFormat::ContentLength)))
} else if trimmed.starts_with('{') {
Ok(Some((trimmed.as_bytes().to_vec(), WireFormat::Ndjson)))
} else if trimmed.is_empty() {
read_message(reader)
} else {
Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("Unknown message format, expected Content-Length or JSON, got: {}",
if trimmed.len() > 50 { &trimmed[..50] } else { trimmed })
))
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum WireFormat {
ContentLength,
Ndjson,
}
pub fn write_message_format<W: Write, T: serde::Serialize>(
writer: &mut W,
message: &T,
format: WireFormat,
) -> io::Result<()> {
let body = serde_json::to_vec(message)?;
match format {
WireFormat::ContentLength => {
write!(writer, "Content-Length: {}\r\n\r\n", body.len())?;
writer.write_all(&body)?;
}
WireFormat::Ndjson => {
writer.write_all(&body)?;
writer.write_all(b"\n")?;
}
}
Ok(())
}
#[allow(dead_code)]
pub fn write_message<W: Write, T: serde::Serialize>(writer: &mut W, message: &T) -> io::Result<()> {
let body = serde_json::to_vec(message)?;
write!(writer, "Content-Length: {}\r\n\r\n", body.len())?;
writer.write_all(&body)?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Cursor;
#[test]
fn test_read_message() {
let input = b"Content-Length: 13\r\n\r\n{\"test\": 123}";
let mut reader = Cursor::new(input);
let (msg, format) = read_message(&mut reader).unwrap().unwrap();
assert_eq!(msg, b"{\"test\": 123}");
assert_eq!(format, WireFormat::ContentLength);
}
#[test]
fn test_write_message() {
let mut output = Vec::new();
let msg = serde_json::json!({"test": 123});
write_message(&mut output, &msg).unwrap();
let expected = b"Content-Length: 12\r\n\r\n{\"test\":123}";
assert_eq!(output, expected);
}
}