flatland_protocol/
frame.rs1use std::marker::Unpin;
4
5use crate::codec::Codec;
6use crate::{ClientMessage, Envelope, PostcardCodec, ServerMessage, PROTOCOL_VERSION};
7use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
8
9const MAX_FRAME_BYTES: usize = 1_048_576;
10
11#[derive(Debug, thiserror::Error)]
12pub enum FrameError {
13 #[error("frame too large: {0} bytes")]
14 TooLarge(usize),
15 #[error("io error: {0}")]
16 Io(#[from] std::io::Error),
17 #[error("codec error: {0}")]
18 Codec(#[from] crate::codec::CodecError),
19 #[error("protocol version mismatch: expected {PROTOCOL_VERSION}, got {0}")]
20 VersionMismatch(u16),
21}
22
23pub async fn write_client_message<W>(writer: &mut W, message: &ClientMessage) -> Result<(), FrameError>
24where
25 W: AsyncWrite + Unpin + Send,
26{
27 write_payload(writer, &Envelope::new(message.clone())).await
28}
29
30pub async fn read_client_message<R>(reader: &mut R) -> Result<ClientMessage, FrameError>
31where
32 R: AsyncRead + Unpin + Send,
33{
34 let envelope: Envelope<ClientMessage> = read_payload(reader).await?;
35 Ok(envelope.payload)
36}
37
38pub async fn write_server_message<W>(writer: &mut W, message: &ServerMessage) -> Result<(), FrameError>
39where
40 W: AsyncWrite + Unpin + Send,
41{
42 write_payload(writer, &Envelope::new(message.clone())).await
43}
44
45pub async fn read_server_message<R>(reader: &mut R) -> Result<ServerMessage, FrameError>
46where
47 R: AsyncRead + Unpin + Send,
48{
49 let envelope: Envelope<ServerMessage> = read_payload(reader).await?;
50 Ok(envelope.payload)
51}
52
53async fn write_payload<W, T>(writer: &mut W, envelope: &Envelope<T>) -> Result<(), FrameError>
54where
55 W: AsyncWrite + Unpin + Send,
56 T: serde::Serialize,
57{
58 let codec = PostcardCodec;
59 let bytes = codec.encode(envelope)?;
60 let len = u32::try_from(bytes.len()).map_err(|_| FrameError::TooLarge(bytes.len()))?;
61 writer.write_u32_le(len).await?;
62 writer.write_all(&bytes).await?;
63 writer.flush().await?;
64 Ok(())
65}
66
67async fn read_payload<R, T>(reader: &mut R) -> Result<Envelope<T>, FrameError>
68where
69 R: AsyncRead + Unpin + Send,
70 T: serde::de::DeserializeOwned,
71{
72 let len = reader.read_u32_le().await? as usize;
73 if len > MAX_FRAME_BYTES {
74 return Err(FrameError::TooLarge(len));
75 }
76 let mut buf = vec![0_u8; len];
77 reader.read_exact(&mut buf).await?;
78 let codec = PostcardCodec;
79 let envelope: Envelope<T> = codec.decode(&buf)?;
80 if envelope.protocol_version != PROTOCOL_VERSION {
81 return Err(FrameError::VersionMismatch(envelope.protocol_version));
82 }
83 Ok(envelope)
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89 use super::*;
90 use crate::{AuthCredential, ClientMessage, Hello, PROTOCOL_VERSION};
91 use uuid::Uuid;
92
93 #[tokio::test]
94 async fn roundtrip_client_message() {
95 let msg = ClientMessage::Hello(Hello {
96 client_name: "test".into(),
97 protocol_version: PROTOCOL_VERSION,
98 auth: Default::default(),
99 character_id: None,
100 });
101 let mut buf = Vec::new();
102 write_client_message(&mut buf, &msg).await.unwrap();
103 let parsed = read_client_message(&mut buf.as_slice()).await.unwrap();
104 assert_eq!(msg, parsed);
105 }
106
107 #[tokio::test]
108 async fn roundtrip_session_auth_hello() {
109 let msg = ClientMessage::Hello(Hello {
110 client_name: "traveler".into(),
111 protocol_version: PROTOCOL_VERSION,
112 auth: AuthCredential::Session {
113 token: "flat_sess_test".into(),
114 },
115 character_id: Some(Uuid::parse_str("019f399e-2b4e-7001-87a2-2e99c005e79d").unwrap()),
116 });
117 let mut buf = Vec::new();
118 write_client_message(&mut buf, &msg).await.unwrap();
119 let parsed = read_client_message(&mut buf.as_slice()).await.unwrap();
120 assert_eq!(msg, parsed);
121 }
122}