Using printf + nc, send these by hand:
printf '*1\r\n$4\r\nPING\r\n' | nc localhost 6380
# -> +PONG\r\n
printf '*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n' | nc localhost 6380
# -> $20\r\n['SET', 'foo', 'bar']\r\nCheckpoint: The second one echoes the parsed array as a string. We'll replace this with real SET in step 3.
Real redis-cli also accepts "inline" commands separated by spaces and ended with \r\n:
PING\r\n
SET foo bar\r\n
Extend parse_resp to detect when the first byte is NOT one of +-:$* and parse the line as a space-separated command. (Hint: split on spaces, treat each word as a bulk string.)
Checkpoint: printf 'PING\r\n' | nc localhost 6380 returns +PONG\r\n.
Send a command one byte at a time:
import socket, time
s = socket.socket()
s.connect(("127.0.0.1", 6380))
frame = b'*1\r\n$4\r\nPING\r\n'
for byte in frame:
s.sendall(bytes([byte]))
time.sleep(0.05)
print(s.recv(100))Checkpoint: The reply is b'+PONG\r\n'. The server reassembled the frame from individual-byte chunks because the parser returned None until the buffer was complete.
Send something that isn't valid RESP and see what happens:
printf 'garbage\r\n' | nc localhost 6380The server should log a ProtocolError. In production you'd want to drop the connection cleanly instead of letting the exception bubble. Add a try/except in handle_client that closes the socket on ProtocolError and logs the offending bytes.
Checkpoint: The server keeps running and accepts new clients after a bad input. It does not crash the whole process.