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;
  2. enum Frame {
  3. Simple(String),
  4. Error(String),
  5. Integer(u64),
  6. Bulk(Bytes),
  7. Null,
  8. Array(Vec<Frame>),
  9. }

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};
  3. struct Connection {
  4. stream: TcpStream,
  5. // ... other fields here
  6. }
  7. impl Connection {
  8. /// Read a frame from the connection.
  9. ///
  10. /// Returns `None` if EOF is reached
  11. pub async fn read_frame(&mut self)
  12. -> Result<Option<Frame>>
  13. {
  14. // implementation here
  15. }
  16. /// Write a frame to the connection.
  17. pub async fn write_frame(&mut self, frame: &Frame)
  18. -> Result<()>
  19. {
  20. // implementation here
  21. }
  22. }

You can find the details of the Redis wire protocol here. The full Connection code is found here.