Frame Protocol¶
Quill uses a simple, efficient binary frame protocol for streaming RPCs.
Frame Layout¶
┌─────────────────┬──────────────┬─────────────────┐
│ Length (varint) │ Flags (1 B) │ Payload (N B) │
└─────────────────┴──────────────┴─────────────────┘
| Field | Size | Description |
|---|---|---|
| Length | 1-5 bytes | Varint-encoded payload length |
| Flags | 1 byte | Frame type and control flags |
| Payload | N bytes | Message data |
Flags¶
| Flag | Value | Description |
|---|---|---|
DATA |
0x01 |
Frame contains data payload |
END_STREAM |
0x02 |
Last frame in stream |
CANCEL |
0x04 |
Cancel the stream |
CREDIT |
0x08 |
Flow control credit grant |
Flags can be combined. For example, DATA | END_STREAM (0x03) indicates a final data frame.
Varint Encoding¶
Lengths use protobuf-style varint encoding: - Values 0-127: 1 byte - Values 128-16383: 2 bytes - Values up to 2^28-1: 4 bytes
// Encoding example
fn encode_varint(mut value: u64, buf: &mut Vec<u8>) {
while value >= 0x80 {
buf.push((value as u8) | 0x80);
value >>= 7;
}
buf.push(value as u8);
}
Frame Types¶
Data Frame¶
Carries message payload.
End Stream Frame¶
Signals stream completion.
Cancel Frame¶
Aborts the stream.
Credit Frame¶
Grants flow control credits to the sender.
Usage Examples¶
Encoding a Frame¶
use quill_core::framing::{Frame, FrameFlags, encode_frame};
// Create a data frame
let frame = Frame::new(
FrameFlags::DATA,
b"Hello, World!".to_vec(),
);
// Encode to bytes
let mut buffer = Vec::new();
encode_frame(&frame, &mut buffer);
Decoding a Frame¶
use quill_core::framing::{Frame, decode_frame};
let bytes = &[13, 0x01, /* payload... */];
let frame = decode_frame(bytes)?;
if frame.flags.contains(FrameFlags::DATA) {
println!("Received data: {:?}", frame.payload);
}
Streaming with Frames¶
// Server streaming - send multiple frames
sender.send(Frame::data(response1)).await?;
sender.send(Frame::data(response2)).await?;
sender.send(Frame::end_stream()).await?;
// Client receiving
while let Some(frame) = receiver.next().await {
if frame.flags.contains(FrameFlags::END_STREAM) {
break;
}
process(frame.payload);
}
Resource Limits¶
| Limit | Value | Description |
|---|---|---|
max_frame_bytes |
4 MB | Maximum frame size |
max_streams_per_connection |
100 | Concurrent streams limit |
Frames exceeding max_frame_bytes are rejected with an error.
Wire Format Example¶
A "Hello" message with END_STREAM:
Comparison with gRPC¶
| Feature | Quill | gRPC |
|---|---|---|
| Frame header | 2-6 bytes | 5 bytes fixed |
| Compression flag | In flags byte | Separate byte |
| Trailers | Not required | Required for errors |
| Flow control | Credit-based | WINDOW_UPDATE |
Next Steps¶
- Flow Control - Credit-based flow control
- Streaming Guide - Streaming patterns
- Error Handling - Problem Details errors