Framing
We will now apply what we just learned about I/O and implement the Mini-Redis framing layer. Framing is the process of taking a byte stream and converting it to a stream of frames. A frame is a unit of data transmitted between two peers. The Redis protocol frame is as follows:
1. use bytes::Bytes;
3. enum Frame {
4. Simple(String),
5. Error(String),
6. Integer(u64),
7. Bulk(Bytes),
8. Null,
9. Array(Vec<Frame>),
10. }
Note how the frame only consists of data without any semantics. The command parsing and implementation happen at a higher level.
For HTTP, a frame might look like:
1. enum HttpFrame {
2. RequestHead {
3. method: Method,
4. uri: Uri,
5. version: Version,
6. headers: HeaderMap,
7. },
8. ResponseHead {
9. status: StatusCode,
10. version: Version,
11. headers: HeaderMap,
12. },
13. BodyChunk {
14. chunk: Bytes,
15. },
16. }
To implement framing for Mini-Redis, we will implement a Connection struct that wraps a TcpStream and reads/writes mini_redis::Frame values.
1. use tokio::net::TcpStream;
2. use mini_redis::{Frame, Result};
4. struct Connection {
5. stream: TcpStream,
6. // ... other fields here
7. }
9. impl Connection {
10. /// Read a frame from the connection.
11. ///
12. /// Returns `None` if EOF is reached
13. pub async fn read_frame(&mut self)
14. -> Result<Option<Frame>>
15. {
16. // implementation here
17. }
19. /// Write a frame to the connection.
20. pub async fn write_frame(&mut self, frame: &Frame)
21. -> Result<()>
22. {
23. // implementation here
24. }
25. }
You can find the details of the Redis wire protocol here. The full Connection code is found here.
