1use std::{
6 collections::{BTreeMap, VecDeque},
7 mem::size_of,
8};
9
10pub type Headers = Vec<(String, Vec<u8>)>;
12
13#[derive(Clone, Debug, Eq, PartialEq)]
15pub struct StreamMessage {
16 pub compressed: bool,
18 pub payload: Vec<u8>,
20}
21
22#[derive(Clone, Copy, Debug, Eq, PartialEq)]
24pub struct StreamLimits {
25 pub max_buffered_bytes: usize,
27 pub max_queued_messages: usize,
29}
30impl Default for StreamLimits {
31 fn default() -> Self {
32 Self {
33 max_buffered_bytes: 8 * 1024 * 1024,
34 max_queued_messages: 1024,
35 }
36 }
37}
38
39#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
41pub enum SlotMapError {
42 #[error("invalid stream identifier {0}")]
44 InvalidStreamId(i32),
45 #[error("stream {0} is already open")]
47 AlreadyOpen(i32),
48 #[error("unknown stream {0}")]
50 UnknownStream(i32),
51 #[error("GOAWAY: stream={stream_id}, last={last_stream_id}, code={error_code}")]
53 GoAway {
54 stream_id: i32,
55 last_stream_id: i32,
56 error_code: u32,
57 },
58 #[error("stream {stream_id} reset: code={error_code}")]
60 Reset { stream_id: i32, error_code: u32 },
61 #[error("stream {stream_id}: cannot {operation}")]
63 InvalidTransition {
64 stream_id: i32,
65 operation: &'static str,
66 },
67 #[error("stream {stream_id}: byte limit {limit}")]
69 MemoryLimit { stream_id: i32, limit: usize },
70 #[error("stream {stream_id}: message limit {limit}")]
72 MessageLimit { stream_id: i32, limit: usize },
73 #[error("stream {stream_id}: invalid compression flag {flag}")]
75 InvalidCompressionFlag { stream_id: i32, flag: u8 },
76 #[error("stream {0}: truncated envelope")]
78 TruncatedFrame(i32),
79 #[error("stream {0} is not closed")]
81 NotClosed(i32),
82}
83pub type SlotResult<T> = Result<T, SlotMapError>;
85fn memory_limit(stream_id: i32, limits: StreamLimits) -> SlotMapError {
86 SlotMapError::MemoryLimit {
87 stream_id,
88 limit: limits.max_buffered_bytes,
89 }
90}
91fn transition(stream_id: i32, operation: &'static str) -> SlotMapError {
92 SlotMapError::InvalidTransition {
93 stream_id,
94 operation,
95 }
96}
97
98#[derive(Debug, Default)]
100pub struct StreamSlot {
101 initial_metadata: Option<Headers>,
102 initial_seen: bool,
103 initial_bytes: usize,
104 messages: VecDeque<StreamMessage>,
105 trailers: Option<Headers>,
106 trailers_seen: bool,
107 trailer_bytes: usize,
108 partial: Vec<u8>,
109 buffered_bytes: usize,
110 remote_end: bool,
111 local_end: bool,
112 reset: Option<u32>,
113}
114impl StreamSlot {
115 #[must_use]
117 pub const fn buffered_bytes(&self) -> usize {
118 self.buffered_bytes
119 }
120 #[must_use]
122 pub fn queued_message_count(&self) -> usize {
123 self.messages.len()
124 }
125 #[must_use]
127 pub const fn remote_end_stream(&self) -> bool {
128 self.remote_end
129 }
130 #[must_use]
132 pub const fn local_end_stream(&self) -> bool {
133 self.local_end
134 }
135 #[must_use]
137 pub const fn reset_error_code(&self) -> Option<u32> {
138 self.reset
139 }
140 fn check_reset(&self, stream_id: i32) -> SlotResult<()> {
141 match self.reset {
142 Some(0) if self.trailers_seen && self.remote_end => Ok(()),
143 Some(error_code) => Err(SlotMapError::Reset {
144 stream_id,
145 error_code,
146 }),
147 None => Ok(()),
148 }
149 }
150 fn receive(&self, stream_id: i32, operation: &'static str) -> SlotResult<()> {
151 self.check_reset(stream_id)?;
152 if self.remote_end || self.trailers_seen {
153 return Err(transition(stream_id, operation));
154 }
155 Ok(())
156 }
157 fn budget(&self, id: i32, bytes: usize, limits: StreamLimits) -> SlotResult<usize> {
158 self.buffered_bytes
159 .checked_add(bytes)
160 .filter(|total| *total <= limits.max_buffered_bytes)
161 .ok_or(memory_limit(id, limits))
162 }
163}
164
165#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
167pub enum EndpointRole {
168 #[default]
170 Client,
171 Server,
173}
174impl EndpointRole {
175 const fn initiates(self, id: i32) -> bool {
176 matches!(self, Self::Client) == (id % 2 == 1)
177 }
178}
179#[derive(Debug, Default)]
181pub struct StreamSlotMap {
182 slots: BTreeMap<i32, StreamSlot>,
183 limits: StreamLimits,
184 role: EndpointRole,
185 goaway: Option<(i32, u32)>,
186}
187impl StreamSlotMap {
188 #[must_use]
190 pub const fn new(limits: StreamLimits) -> Self {
191 Self::with_role(limits, EndpointRole::Client)
192 }
193 #[must_use]
195 pub const fn with_role(limits: StreamLimits, role: EndpointRole) -> Self {
196 Self {
197 slots: BTreeMap::new(),
198 limits,
199 role,
200 goaway: None,
201 }
202 }
203 pub fn open_stream(&mut self, stream_id: i32) -> SlotResult<()> {
207 if stream_id <= 0 {
208 return Err(SlotMapError::InvalidStreamId(stream_id));
209 }
210 if let Some((last_stream_id, error_code)) = self.goaway
211 && self.role.initiates(stream_id)
212 {
213 return Err(SlotMapError::GoAway {
214 stream_id,
215 last_stream_id,
216 error_code,
217 });
218 }
219 if self.slots.contains_key(&stream_id) {
220 return Err(SlotMapError::AlreadyOpen(stream_id));
221 }
222 self.slots.insert(stream_id, StreamSlot::default());
223 Ok(())
224 }
225 #[must_use]
227 pub fn get(&self, stream_id: i32) -> Option<&StreamSlot> {
228 self.slots.get(&stream_id)
229 }
230 #[must_use]
232 pub fn active_stream_count(&self) -> usize {
233 self.slots.len()
234 }
235 #[must_use]
237 pub const fn goaway(&self) -> Option<(i32, u32)> {
238 self.goaway
239 }
240 pub fn push_initial_metadata(&mut self, stream_id: i32, headers: Headers) -> SlotResult<()> {
244 self.push_headers(stream_id, headers, false)
245 }
246 pub fn append_recv_bytes(&mut self, stream_id: i32, bytes: &[u8]) -> SlotResult<()> {
251 let limits = self.limits;
252 let slot = self.slot_mut(stream_id)?;
253 slot.receive(stream_id, "receive DATA")?;
254 if !slot.initial_seen {
255 return Err(transition(
256 stream_id,
257 "receive DATA before initial metadata",
258 ));
259 }
260 let mut total = slot.budget(stream_id, bytes.len(), limits)?;
261 let len = slot.partial.len() + bytes.len();
262 let at = |index: usize| {
263 if index < slot.partial.len() {
264 slot.partial[index]
265 } else {
266 bytes[index - slot.partial.len()]
267 }
268 };
269 let mut consumed = 0;
270 let mut count = 0;
271 while consumed < len {
272 let flag = at(consumed);
273 if flag > 1 {
274 return Err(SlotMapError::InvalidCompressionFlag { stream_id, flag });
275 }
276 if len - consumed < 5 {
277 break;
278 }
279 let payload_len =
280 u32::from_be_bytes(std::array::from_fn(|i| at(consumed + 1 + i))) as usize;
281 let frame_len = payload_len
282 .checked_add(5)
283 .ok_or(memory_limit(stream_id, limits))?;
284 if frame_len
285 .checked_add(size_of::<StreamMessage>())
286 .is_none_or(|required| required > limits.max_buffered_bytes)
287 {
288 return Err(memory_limit(stream_id, limits));
289 }
290 if len - consumed < frame_len {
291 break;
292 }
293 count += 1;
294 if slot
295 .messages
296 .len()
297 .checked_add(count)
298 .is_none_or(|n| n > limits.max_queued_messages)
299 {
300 return Err(SlotMapError::MessageLimit {
301 stream_id,
302 limit: limits.max_queued_messages,
303 });
304 }
305 total = total
306 .checked_add(size_of::<StreamMessage>())
307 .filter(|n| *n <= limits.max_buffered_bytes)
308 .ok_or(memory_limit(stream_id, limits))?;
309 consumed += frame_len;
310 }
311 if count == 0 {
312 slot.partial.extend_from_slice(bytes);
313 slot.buffered_bytes = total;
314 return Ok(());
315 }
316 let mut combined = Vec::with_capacity(len);
317 combined.extend_from_slice(&slot.partial);
318 combined.extend_from_slice(bytes);
319 let mut offset = 0;
320 for _ in 0..count {
321 let payload_len =
322 u32::from_be_bytes(std::array::from_fn(|i| combined[offset + 1 + i])) as usize;
323 let end = offset + 5 + payload_len;
324 slot.messages.push_back(StreamMessage {
325 compressed: combined[offset] == 1,
326 payload: combined[offset + 5..end].to_vec(),
327 });
328 offset = end;
329 }
330 slot.partial = combined[consumed..].to_vec();
331 slot.buffered_bytes = total;
332 Ok(())
333 }
334 pub fn push_message(&mut self, stream_id: i32, message: StreamMessage) -> SlotResult<()> {
338 let limits = self.limits;
339 let slot = self.slot_mut(stream_id)?;
340 slot.receive(stream_id, "receive a message")?;
341 if !slot.initial_seen || !slot.partial.is_empty() {
342 return Err(transition(
343 stream_id,
344 "receive decoded message before headers or within a partial frame",
345 ));
346 }
347 if slot.messages.len() >= limits.max_queued_messages {
348 return Err(SlotMapError::MessageLimit {
349 stream_id,
350 limit: limits.max_queued_messages,
351 });
352 }
353 let additional = message
354 .payload
355 .len()
356 .checked_add(5 + size_of::<StreamMessage>())
357 .ok_or(memory_limit(stream_id, limits))?;
358 let total = slot.budget(stream_id, additional, limits)?;
359 slot.messages.push_back(StreamMessage {
360 compressed: message.compressed,
361 payload: message.payload.into_boxed_slice().into_vec(),
362 });
363 slot.buffered_bytes = total;
364 Ok(())
365 }
366 pub fn push_trailing_metadata(&mut self, stream_id: i32, headers: Headers) -> SlotResult<()> {
370 self.push_headers(stream_id, headers, true)
371 }
372 fn push_headers(&mut self, stream_id: i32, headers: Headers, trailing: bool) -> SlotResult<()> {
373 let limits = self.limits;
374 let slot = self.slot_mut(stream_id)?;
375 slot.receive(stream_id, "receive metadata")?;
376 if !trailing && slot.initial_seen {
377 return Err(transition(stream_id, "receive duplicate initial metadata"));
378 }
379 if trailing && !slot.partial.is_empty() {
380 return Err(SlotMapError::TruncatedFrame(stream_id));
381 }
382 let additional = header_bytes(&headers).ok_or(memory_limit(stream_id, limits))?;
383 let total = slot.budget(stream_id, additional, limits)?;
384 if trailing {
385 slot.trailer_bytes = additional;
386 slot.trailers = Some(normalize_headers(headers));
387 slot.trailers_seen = true;
388 } else {
389 slot.initial_bytes = additional;
390 slot.initial_metadata = Some(normalize_headers(headers));
391 slot.initial_seen = true;
392 }
393 slot.buffered_bytes = total;
394 Ok(())
395 }
396 pub fn mark_remote_end_stream(&mut self, stream_id: i32) -> SlotResult<()> {
400 let slot = self.slot_mut(stream_id)?;
401 slot.check_reset(stream_id)?;
402 if slot.remote_end {
403 return Err(transition(stream_id, "receive duplicate END_STREAM"));
404 }
405 if !slot.partial.is_empty() {
406 return Err(SlotMapError::TruncatedFrame(stream_id));
407 }
408 slot.remote_end = true;
409 Ok(())
410 }
411 pub fn mark_local_end_stream(&mut self, stream_id: i32) -> SlotResult<()> {
415 let slot = self.slot_mut(stream_id)?;
416 slot.check_reset(stream_id)?;
417 if slot.local_end {
418 return Err(transition(stream_id, "send duplicate END_STREAM"));
419 }
420 slot.local_end = true;
421 Ok(())
422 }
423 pub fn record_rst_stream(&mut self, stream_id: i32, error_code: u32) -> SlotResult<()> {
427 let slot = self.slot_mut(stream_id)?;
428 if let Some(error_code) = slot.reset {
429 return Err(SlotMapError::Reset {
430 stream_id,
431 error_code,
432 });
433 }
434 if error_code == 0 && slot.trailers_seen && slot.remote_end {
435 slot.reset = Some(0);
436 slot.local_end = true;
437 return Ok(());
438 }
439 *slot = StreamSlot {
440 reset: Some(error_code),
441 remote_end: true,
442 local_end: true,
443 ..StreamSlot::default()
444 };
445 Ok(())
446 }
447 pub fn apply_goaway(&mut self, last_stream_id: i32, error_code: u32) -> SlotResult<Vec<i32>> {
452 if last_stream_id < 0 {
453 return Err(SlotMapError::InvalidStreamId(last_stream_id));
454 }
455 if self
456 .goaway
457 .is_some_and(|(previous, _)| last_stream_id > previous)
458 {
459 return Err(transition(last_stream_id, "increase GOAWAY boundary"));
460 }
461 let mut removed = Vec::new();
462 self.slots.retain(|id, _| {
463 let excluded = *id > last_stream_id && self.role.initiates(*id);
464 if excluded {
465 removed.push(*id);
466 }
467 !excluded
468 });
469 self.goaway = Some((last_stream_id, error_code));
470 Ok(removed)
471 }
472 pub fn pop_initial_metadata(&mut self, stream_id: i32) -> SlotResult<Option<Headers>> {
476 let slot = self.slot_mut(stream_id)?;
477 slot.check_reset(stream_id)?;
478 let headers = slot.initial_metadata.take();
479 slot.buffered_bytes -= std::mem::take(&mut slot.initial_bytes);
480 Ok(headers)
481 }
482 pub fn pop_message(&mut self, stream_id: i32) -> SlotResult<Option<StreamMessage>> {
486 let slot = self.slot_mut(stream_id)?;
487 slot.check_reset(stream_id)?;
488 let message = slot.messages.pop_front();
489 if slot.messages.len() <= slot.messages.capacity() / 4 {
490 slot.messages.shrink_to_fit();
491 }
492 if let Some(message) = &message {
493 slot.buffered_bytes -= message.payload.len() + 5 + size_of::<StreamMessage>();
494 }
495 Ok(message)
496 }
497 pub fn take_trailers(&mut self, stream_id: i32) -> SlotResult<Option<Headers>> {
501 let slot = self.slot_mut(stream_id)?;
502 slot.check_reset(stream_id)?;
503 let headers = slot.trailers.take();
504 slot.buffered_bytes -= std::mem::take(&mut slot.trailer_bytes);
505 Ok(headers)
506 }
507 pub fn remove_closed_stream(&mut self, stream_id: i32) -> SlotResult<StreamSlot> {
511 let slot = self
512 .slots
513 .get(&stream_id)
514 .ok_or(SlotMapError::UnknownStream(stream_id))?;
515 if slot.reset.is_none() && !(slot.remote_end && slot.local_end) {
516 return Err(SlotMapError::NotClosed(stream_id));
517 }
518 self.slots
519 .remove(&stream_id)
520 .ok_or(SlotMapError::UnknownStream(stream_id))
521 }
522 fn slot_mut(&mut self, id: i32) -> SlotResult<&mut StreamSlot> {
523 self.slots
524 .get_mut(&id)
525 .ok_or(SlotMapError::UnknownStream(id))
526 }
527}
528fn header_bytes(headers: &Headers) -> Option<usize> {
529 headers.iter().try_fold(0_usize, |total, (key, value)| {
530 total
531 .checked_add(size_of::<(String, Vec<u8>)>())?
532 .checked_add(key.len())?
533 .checked_add(value.len())
534 })
535}
536fn normalize_headers(headers: Headers) -> Headers {
537 headers
538 .into_iter()
539 .map(|(key, value)| (key.into_boxed_str().into(), value.into_boxed_slice().into()))
540 .collect::<Box<[_]>>()
541 .into_vec()
542}
543#[cfg(test)]
544mod tests {
545 use super::*;
546 fn headers(name: &str, value: &[u8]) -> Headers {
547 vec![(name.to_owned(), value.to_vec())]
548 }
549 fn message(payload: &[u8]) -> StreamMessage {
550 StreamMessage {
551 compressed: false,
552 payload: payload.to_vec(),
553 }
554 }
555 fn frame(payload: &[u8]) -> Vec<u8> {
556 let mut wire = vec![0];
557 let len = u32::try_from(payload.len()).expect("test length");
558 wire.extend(len.to_be_bytes());
559 wire.extend(payload);
560 wire
561 }
562 fn bounded(bytes: usize, messages: usize) -> StreamSlotMap {
563 StreamSlotMap::new(StreamLimits {
564 max_buffered_bytes: bytes,
565 max_queued_messages: messages,
566 })
567 }
568 fn open(map: &mut StreamSlotMap, id: i32) -> SlotResult<()> {
569 map.open_stream(id)?;
570 map.push_initial_metadata(id, vec![])
571 }
572 #[test]
573 fn interleaved_streams_preserve_all_events() -> SlotResult<()> {
574 let mut map = StreamSlotMap::default();
575 let initial = headers(":status", b"200");
576 for id in [1, 3] {
577 map.open_stream(id)?;
578 map.push_initial_metadata(id, initial.clone())?;
579 }
580 map.push_message(3, message(b"three-a"))?;
581 map.push_message(1, message(b"one"))?;
582 map.push_message(3, message(b"three-b"))?;
583 assert_eq!(map.pop_message(1)?, Some(message(b"one")));
584 for payload in [b"three-a", b"three-b"] {
585 assert_eq!(map.pop_message(3)?, Some(message(payload)));
586 }
587 for id in [1, 3] {
588 map.push_trailing_metadata(id, headers("grpc-status", b"0"))?;
589 map.mark_remote_end_stream(id)?;
590 assert_eq!(map.pop_initial_metadata(id)?, Some(initial.clone()));
591 assert_eq!(map.take_trailers(id)?, Some(headers("grpc-status", b"0")));
592 }
593 Ok(())
594 }
595 #[test]
596 fn trailers_only_and_duplicate_metadata() -> SlotResult<()> {
597 let mut map = StreamSlotMap::default();
598 map.open_stream(1)?;
599 map.push_trailing_metadata(1, headers("grpc-status", b"12"))?;
600 map.mark_remote_end_stream(1)?;
601 assert_eq!(map.pop_initial_metadata(1)?, None);
602 assert_eq!(map.pop_message(1)?, None);
603 assert_eq!(map.take_trailers(1)?, Some(headers("grpc-status", b"12")));
604 open(&mut map, 3)?;
605 map.pop_initial_metadata(3)?;
606 assert!(map.push_initial_metadata(3, vec![]).is_err());
607 map.push_trailing_metadata(3, vec![])?;
608 map.take_trailers(3)?;
609 assert!(map.push_trailing_metadata(3, vec![]).is_err());
610 Ok(())
611 }
612 #[test]
613 fn goaway_prunes_and_enforces_decreasing_boundary() -> SlotResult<()> {
614 let mut map = StreamSlotMap::default();
615 for id in [1, 3, 5, 7, i32::MAX] {
616 open(&mut map, id)?;
617 }
618 assert!(map.open_stream(1).is_err());
619 map.push_message(1, message(b"retained"))?;
620 assert!(map.apply_goaway(i32::MAX, 0)?.is_empty());
621 assert!(matches!(
622 map.open_stream(9),
623 Err(SlotMapError::GoAway { .. })
624 ));
625 open(&mut map, 8)?;
626 assert_eq!(map.apply_goaway(3, 0)?, vec![5, 7, i32::MAX]);
627 assert!(map.open_stream(5).is_err());
628 assert_eq!(map.pop_message(1)?, Some(message(b"retained")));
629 assert_eq!(map.apply_goaway(1, 0)?, vec![3]);
630 assert!(map.apply_goaway(7, 0).is_err());
631 assert!(map.apply_goaway(-1, 0).is_err());
632 assert_eq!(map.apply_goaway(0, 7)?, vec![1]);
633 assert!(map.get(8).is_some());
634 let mut server = StreamSlotMap::with_role(StreamLimits::default(), EndpointRole::Server);
635 for id in [1, 2, 3, 4] {
636 open(&mut server, id)?;
637 }
638 server.push_message(3, message(b"request"))?;
639 assert_eq!(server.apply_goaway(2, 0)?, vec![4]);
640 assert!(server.open_stream(6).is_err());
641 assert_eq!(server.apply_goaway(0, 0)?, vec![2]);
642 assert_eq!(server.pop_message(3)?, Some(message(b"request")));
643 open(&mut server, 5)?;
644 assert!(matches!(
645 map.open_stream(1),
646 Err(SlotMapError::GoAway { error_code: 7, .. })
647 ));
648 Ok(())
649 }
650 #[test]
651 fn completed_response_survives_no_error_reset() -> SlotResult<()> {
652 for (code, complete) in [(0, true), (0, false), (8, true)] {
653 let mut map = StreamSlotMap::default();
654 open(&mut map, 1)?;
655 map.push_message(1, message(b"early response"))?;
656 if complete {
657 map.push_trailing_metadata(1, headers("grpc-status", b"0"))?;
658 map.mark_remote_end_stream(1)?;
659 } else {
660 map.append_recv_bytes(1, &[0, 0])?;
661 }
662 let bytes = map.get(1).expect("slot").buffered_bytes();
663 map.record_rst_stream(1, code)?;
664 assert!(map.record_rst_stream(1, code).is_err());
665 let slot = map.get(1).expect("slot");
666 assert!(slot.remote_end_stream() && slot.local_end_stream());
667 if code == 0 && complete {
668 assert_eq!(slot.buffered_bytes(), bytes);
669 assert_eq!(map.pop_initial_metadata(1)?, Some(vec![]));
670 assert_eq!(map.pop_message(1)?, Some(message(b"early response")));
671 assert_eq!(map.take_trailers(1)?, Some(headers("grpc-status", b"0")));
672 assert_eq!(map.get(1).expect("slot").buffered_bytes(), 0);
673 } else {
674 assert_eq!(slot.buffered_bytes(), 0);
675 assert!(map.pop_initial_metadata(1).is_err());
676 assert!(map.take_trailers(1).is_err());
677 assert!(matches!(
678 map.pop_message(1),
679 Err(SlotMapError::Reset { .. })
680 ));
681 assert!(map.push_message(1, message(b"late")).is_err());
682 }
683 map.remove_closed_stream(1)?;
684 }
685 Ok(())
686 }
687 #[test]
688 fn fragmented_frames_compression_and_invalid_prefixes() -> SlotResult<()> {
689 let mut map = StreamSlotMap::default();
690 open(&mut map, 1)?;
691 let mut wire = frame(b"abc");
692 wire.extend(frame(b""));
693 for byte in wire {
694 map.append_recv_bytes(1, &[byte])?;
695 }
696 for payload in [b"abc".as_slice(), b"".as_slice()] {
697 assert_eq!(map.pop_message(1)?, Some(message(payload)));
698 }
699 let mut compressed = frame(b"xyz");
700 compressed[0] = 1;
701 map.append_recv_bytes(1, &compressed)?;
702 assert!(map.pop_message(1)?.expect("message").compressed);
703 assert!(map.append_recv_bytes(1, &[2, 0, 0, 0, 0]).is_err());
704 assert!(map.remove_closed_stream(1).is_err());
705 map.mark_local_end_stream(1)?;
706 map.push_message(1, message(b"half-open"))?;
707 assert!(map.mark_local_end_stream(1).is_err());
708 map.mark_remote_end_stream(1)?;
709 assert!(map.push_message(1, message(b"late")).is_err());
710 map.remove_closed_stream(1)?;
711 Ok(())
712 }
713 #[test]
714 fn rejected_batches_preserve_partial_frames() -> SlotResult<()> {
715 let mut map = bounded(128, 1);
716 open(&mut map, 1)?;
717 let wire = frame(b"ok");
718 map.append_recv_bytes(1, &wire[..2])?;
719 let mut queue_excess = wire[2..].to_vec();
720 queue_excess.extend(frame(b""));
721 let mut malformed = wire[2..].to_vec();
722 malformed.push(2);
723 for rejected in [queue_excess, malformed, vec![0; 128]] {
724 assert!(map.append_recv_bytes(1, &rejected).is_err());
725 let slot = map.get(1).expect("slot");
726 assert_eq!(slot.buffered_bytes(), 2);
727 assert_eq!(slot.queued_message_count(), 0);
728 }
729 map.append_recv_bytes(1, &wire[2..])?;
730 assert_eq!(map.pop_message(1)?, Some(message(b"ok")));
731 assert_eq!(map.get(1).expect("slot").buffered_bytes(), 0);
732 Ok(())
733 }
734 #[test]
735 fn truncated_frames_and_declared_lengths_are_bounded() -> SlotResult<()> {
736 for wire in [&[0, 0][..], &[0, 0, 0, 0, 3, b'a'][..]] {
737 let mut map = bounded(128, 4);
738 open(&mut map, 1)?;
739 map.append_recv_bytes(1, wire)?;
740 assert!(map.mark_remote_end_stream(1).is_err());
741 }
742 let mut map = bounded(8, 4);
743 open(&mut map, 1)?;
744 let oversized = [0, 0xff, 0xff, 0xff, 0xff];
745 assert!(map.append_recv_bytes(1, &oversized).is_err());
746 map.append_recv_bytes(1, &[0, 0, 0])?;
747 assert_eq!(map.get(1).expect("slot").buffered_bytes(), 3);
748 assert!(map.push_message(1, message(b"123456")).is_err());
749 Ok(())
750 }
751 #[test]
752 fn byte_and_queue_budgets_release_on_consumption() -> SlotResult<()> {
753 let mut map = bounded(128, 1);
754 map.open_stream(1)?;
755 map.push_initial_metadata(1, headers("key", b"value"))?;
756 assert!(map.push_message(1, message(&[0; 128])).is_err());
757 map.pop_initial_metadata(1)?;
758 assert_eq!(map.get(1).expect("slot").buffered_bytes(), 0);
759 map.push_message(1, message(&[0; 64]))?;
760 assert!(map.push_trailing_metadata(1, headers("x", b"y")).is_err());
761 map.pop_message(1)?;
762 map.push_message(1, message(b""))?;
763 assert!(map.push_message(1, message(b"")).is_err());
764 assert_eq!(map.get(1).expect("slot").queued_message_count(), 1);
765 assert!(map.append_recv_bytes(1, &frame(b"blocked")).is_err());
766 assert_eq!(map.pop_message(1)?, Some(message(b"")));
767 map.append_recv_bytes(1, &frame(b"next"))?;
768 assert_eq!(map.pop_message(1)?, Some(message(b"next")));
769 map.push_trailing_metadata(1, headers("x", b"y"))?;
770 map.take_trailers(1)?;
771 assert_eq!(map.get(1).expect("slot").buffered_bytes(), 0);
772 Ok(())
773 }
774}