1use crate::status::Status;
4
5pub const FRAME_HEADER_SIZE: usize = 5;
7pub const DEFAULT_MAX_MESSAGE_SIZE: usize = 16 * 1024 * 1024;
9const LEADING_PADDING: usize = 3;
10const PAYLOAD_OFFSET: usize = LEADING_PADDING + FRAME_HEADER_SIZE;
11
12#[repr(C, align(8))]
13#[derive(Clone, Debug)]
14struct AlignedWord([u8; 8]);
15
16#[derive(Clone, Debug)]
22pub struct AlignedMessageBuf {
23 words: Vec<AlignedWord>,
24 payload_len: usize,
25}
26
27impl AlignedMessageBuf {
28 pub fn new(payload: &[u8], compressed: bool) -> Result<Self, Status> {
34 let mut message = Self::with_payload_len(payload.len(), compressed)?;
35 message.payload_mut().copy_from_slice(payload);
36 Ok(message)
37 }
38
39 fn with_payload_len(payload_len: usize, compressed: bool) -> Result<Self, Status> {
40 let wire_len = u32::try_from(payload_len)
41 .map_err(|_| Status::resource_exhausted("gRPC message length exceeds u32"))?;
42 let storage_len = payload_len
43 .checked_add(PAYLOAD_OFFSET)
44 .ok_or_else(|| Status::resource_exhausted("gRPC aligned storage size overflow"))?;
45 let word_count = storage_len.div_ceil(size_of::<AlignedWord>());
46 let mut words = Vec::new();
47 words
48 .try_reserve_exact(word_count)
49 .map_err(|_| Status::resource_exhausted("cannot allocate gRPC message storage"))?;
50 words.resize(word_count, AlignedWord([0; 8]));
51 let mut message = Self { words, payload_len };
52 let bytes = message.storage_mut();
53 bytes[LEADING_PADDING] = u8::from(compressed);
54 bytes[LEADING_PADDING + 1..PAYLOAD_OFFSET].copy_from_slice(&wire_len.to_be_bytes());
55 Ok(message)
56 }
57
58 fn storage(&self) -> &[u8] {
59 unsafe {
63 std::slice::from_raw_parts(
64 self.words.as_ptr().cast::<u8>(),
65 self.words.len() * size_of::<AlignedWord>(),
66 )
67 }
68 }
69
70 fn storage_mut(&mut self) -> &mut [u8] {
71 unsafe {
74 std::slice::from_raw_parts_mut(
75 self.words.as_mut_ptr().cast::<u8>(),
76 self.words.len() * size_of::<AlignedWord>(),
77 )
78 }
79 }
80
81 pub fn payload(&self) -> &[u8] {
83 &self.storage()[PAYLOAD_OFFSET..PAYLOAD_OFFSET + self.payload_len]
84 }
85
86 pub fn payload_mut(&mut self) -> &mut [u8] {
88 let end = PAYLOAD_OFFSET + self.payload_len;
89 &mut self.storage_mut()[PAYLOAD_OFFSET..end]
90 }
91
92 pub fn wire_bytes(&self) -> &[u8] {
94 &self.storage()[LEADING_PADDING..PAYLOAD_OFFSET + self.payload_len]
95 }
96
97 pub fn is_compressed(&self) -> bool {
99 self.storage()[LEADING_PADDING] == 1
100 }
101
102 pub fn len(&self) -> usize {
104 self.payload_len
105 }
106
107 pub fn is_empty(&self) -> bool {
109 self.payload_len == 0
110 }
111}
112
113#[derive(Debug)]
119pub struct LengthPrefixedFrame {
120 max_message_size: usize,
121 header: [u8; FRAME_HEADER_SIZE],
122 header_len: usize,
123 message: Option<AlignedMessageBuf>,
124 received: usize,
125 failure: Option<Status>,
126}
127
128impl Default for LengthPrefixedFrame {
129 fn default() -> Self {
130 Self::new(DEFAULT_MAX_MESSAGE_SIZE)
131 }
132}
133
134impl LengthPrefixedFrame {
135 pub fn new(max_message_size: usize) -> Self {
137 Self {
138 max_message_size,
139 header: [0; FRAME_HEADER_SIZE],
140 header_len: 0,
141 message: None,
142 received: 0,
143 failure: None,
144 }
145 }
146
147 pub fn push(&mut self, mut chunk: &[u8]) -> Result<Vec<AlignedMessageBuf>, Status> {
156 if let Some(error) = &self.failure {
157 return Err(error.clone());
158 }
159 let mut messages = Vec::new();
160 while !chunk.is_empty() {
161 if self.message.is_none() {
162 let count = (FRAME_HEADER_SIZE - self.header_len).min(chunk.len());
163 self.header[self.header_len..self.header_len + count]
164 .copy_from_slice(&chunk[..count]);
165 self.header_len += count;
166 chunk = &chunk[count..];
167 if self.header_len < FRAME_HEADER_SIZE {
168 continue;
169 }
170 let result = self.start_message();
171 if let Err(error) = result {
172 return Err(self.fail(error));
173 }
174 }
175 if let Some(message) = &mut self.message {
176 let count = (message.len() - self.received).min(chunk.len());
177 message.payload_mut()[self.received..self.received + count]
178 .copy_from_slice(&chunk[..count]);
179 self.received += count;
180 chunk = &chunk[count..];
181 if self.received == message.len() {
182 if messages.try_reserve(1).is_err() {
183 return Err(self.fail(Status::resource_exhausted(
184 "cannot allocate gRPC decoded message list",
185 )));
186 }
187 if let Some(complete) = self.message.take() {
188 messages.push(complete);
189 }
190 self.header_len = 0;
191 self.received = 0;
192 }
193 }
194 }
195 Ok(messages)
196 }
197
198 fn start_message(&mut self) -> Result<(), Status> {
199 if self.header[0] > 1 {
200 return Err(Status::internal("invalid gRPC compressed flag"));
201 }
202 let length = u32::from_be_bytes([
203 self.header[1],
204 self.header[2],
205 self.header[3],
206 self.header[4],
207 ]);
208 let length = usize::try_from(length)
209 .map_err(|_| Status::resource_exhausted("gRPC message length exceeds platform size"))?;
210 if length > self.max_message_size {
211 return Err(Status::resource_exhausted(
212 "gRPC message exceeds maximum decoding size",
213 ));
214 }
215 self.message = Some(AlignedMessageBuf::with_payload_len(
216 length,
217 self.header[0] == 1,
218 )?);
219 Ok(())
220 }
221
222 fn fail(&mut self, error: Status) -> Status {
223 self.message = None;
224 self.failure = Some(error.clone());
225 error
226 }
227
228 pub fn finish(&mut self) -> Result<(), Status> {
233 if let Some(error) = &self.failure {
234 return Err(error.clone());
235 }
236 if self.header_len != 0 || self.message.is_some() {
237 return Err(self.fail(Status::internal("truncated gRPC message frame")));
238 }
239 Ok(())
240 }
241
242 pub fn reset(&mut self) {
244 self.header_len = 0;
245 self.message = None;
246 self.received = 0;
247 self.failure = None;
248 }
249}
250
251#[cfg(test)]
252mod unit {
253 use super::*;
254 use crate::status::Code;
255
256 fn assert_aligned(message: &AlignedMessageBuf, payload: &[u8], compressed: bool) {
257 assert_eq!(message.payload().as_ptr() as usize % 8, 0);
258 assert_eq!(message.payload(), payload);
259 assert_eq!(message.is_compressed(), compressed);
260 assert_eq!(
261 message.wire_bytes().len(),
262 FRAME_HEADER_SIZE + payload.len()
263 );
264 assert_eq!(message.wire_bytes()[0], u8::from(compressed));
265 assert_eq!(
266 &message.wire_bytes()[1..FRAME_HEADER_SIZE],
267 &(payload.len() as u32).to_be_bytes()
268 );
269 }
270
271 #[test]
272 fn scenario_alignment_survives_all_chunk_sizes_and_coalesced_messages() {
273 let payloads: [&[u8]; 4] = [b"", b"a", b"12345678", b"arbitrary payload length"];
274 let mut wire = Vec::new();
275 for (index, payload) in payloads.iter().enumerate() {
276 let message = AlignedMessageBuf::new(payload, index % 2 == 1).unwrap();
277 assert_aligned(&message, payload, index % 2 == 1);
278 assert_aligned(&message.clone(), payload, index % 2 == 1);
279 wire.extend_from_slice(message.wire_bytes());
280 }
281 for chunk_size in 1..=wire.len() {
282 let mut decoder = LengthPrefixedFrame::new(128);
283 let mut decoded = Vec::new();
284 for chunk in wire.chunks(chunk_size) {
285 decoded.extend(decoder.push(chunk).unwrap());
286 assert!(decoder.push(&[]).unwrap().is_empty());
287 }
288 decoder.finish().unwrap();
289 assert_eq!(decoded.len(), payloads.len());
290 for (index, (message, payload)) in decoded.iter().zip(payloads).enumerate() {
291 assert_aligned(message, payload, index % 2 == 1);
292 }
293 }
294 }
295
296 #[test]
297 fn scenario_every_single_split_preserves_alignment_and_wire_bytes() {
298 let original = AlignedMessageBuf::new(b"capnp aligned payload", false).unwrap();
299 for split in 0..=original.wire_bytes().len() {
300 let mut decoder = LengthPrefixedFrame::default();
301 let mut decoded = decoder.push(&original.wire_bytes()[..split]).unwrap();
302 decoded.extend(decoder.push(&original.wire_bytes()[split..]).unwrap());
303 decoder.finish().unwrap();
304 assert_eq!(decoded.len(), 1);
305 assert_aligned(&decoded[0], original.payload(), false);
306 assert_eq!(decoded[0].wire_bytes(), original.wire_bytes());
307 }
308 }
309
310 #[test]
311 fn scenario_corrupt_flags_fail_closed_until_reset() {
312 for flag in [2, 127, 255] {
313 let mut decoder = LengthPrefixedFrame::default();
314 let error = decoder.push(&[flag, 0, 0, 0, 0]).unwrap_err();
315 assert_eq!(error.code(), Code::Internal);
316 assert_eq!(decoder.push(&[]).unwrap_err().code(), Code::Internal);
317 assert_eq!(decoder.finish().unwrap_err().code(), Code::Internal);
318 decoder.reset();
319 assert_eq!(decoder.push(&[0; 5]).unwrap().len(), 1);
320 decoder.finish().unwrap();
321 }
322 }
323
324 #[test]
325 fn scenario_size_limit_rejects_header_without_receiving_body() {
326 let mut decoder = LengthPrefixedFrame::new(3);
327 assert!(decoder.push(&[0, 0, 0, 0]).unwrap().is_empty());
328 assert_eq!(
329 decoder.push(&[4]).unwrap_err().code(),
330 Code::ResourceExhausted
331 );
332 decoder.reset();
333 let message = AlignedMessageBuf::new(b"abc", false).unwrap();
334 assert_eq!(decoder.push(message.wire_bytes()).unwrap().len(), 1);
335 decoder.finish().unwrap();
336 decoder.reset();
337 assert_eq!(
338 decoder.push(&[0, 255, 255, 255, 255]).unwrap_err().code(),
339 Code::ResourceExhausted
340 );
341 }
342
343 #[test]
344 fn scenario_truncation_detects_every_partial_header_and_body() {
345 let message = AlignedMessageBuf::new(b"body", false).unwrap();
346 for end in 1..message.wire_bytes().len() {
347 let mut decoder = LengthPrefixedFrame::default();
348 assert!(
349 decoder
350 .push(&message.wire_bytes()[..end])
351 .unwrap()
352 .is_empty()
353 );
354 assert_eq!(decoder.finish().unwrap_err().code(), Code::Internal);
355 }
356 LengthPrefixedFrame::default().finish().unwrap();
357 }
358
359 #[test]
360 fn scenario_zero_limit_accepts_empty_payload_only() {
361 let mut decoder = LengthPrefixedFrame::new(0);
362 let decoded = decoder.push(&[0; 10]).unwrap();
363 assert_eq!(decoded.len(), 2);
364 for message in decoded {
365 assert_aligned(&message, b"", false);
366 }
367 decoder.finish().unwrap();
368 assert_eq!(
369 decoder.push(&[0, 0, 0, 0, 1]).unwrap_err().code(),
370 Code::ResourceExhausted
371 );
372 }
373
374 #[test]
375 fn scenario_lifecycle_reset_discards_every_partial_frame() {
376 let abandoned = AlignedMessageBuf::new(b"abandoned", true).unwrap();
377 let replacement = AlignedMessageBuf::new(b"replacement", false).unwrap();
378 for end in 1..abandoned.wire_bytes().len() {
379 let mut decoder = LengthPrefixedFrame::default();
380 assert!(
381 decoder
382 .push(&abandoned.wire_bytes()[..end])
383 .unwrap()
384 .is_empty()
385 );
386 decoder.reset();
387 let decoded = decoder.push(replacement.wire_bytes()).unwrap();
388 assert_eq!(decoded.len(), 1);
389 assert_aligned(&decoded[0], replacement.payload(), false);
390 decoder.finish().unwrap();
391 }
392 }
393
394 #[test]
395 fn scenario_failure_after_complete_message_rejects_entire_chunk() {
396 let valid = AlignedMessageBuf::new(b"valid", false).unwrap();
397 let mut chunk = valid.wire_bytes().to_vec();
398 chunk.extend_from_slice(&[2, 0, 0, 0, 0]);
399 let mut decoder = LengthPrefixedFrame::default();
400 let error = decoder.push(&chunk).unwrap_err();
401 assert_eq!(error.code(), Code::Internal);
402 assert_eq!(decoder.push(valid.wire_bytes()).unwrap_err(), error);
403 assert_eq!(decoder.finish().unwrap_err(), error);
404 decoder.reset();
405 let decoded = decoder.push(valid.wire_bytes()).unwrap();
406 assert_eq!(decoded.len(), 1);
407 assert_aligned(&decoded[0], valid.payload(), false);
408 decoder.finish().unwrap();
409 }
410}