1use byteorder::{BigEndian, ByteOrder};
6use std::io::{self, Write};
7
8pub const MAGIC_REQ: u8 = 0x80;
11pub const MAGIC_RES: u8 = 0x81;
13
14pub const ST_OK: u16 = 0x0000;
17pub const ST_NF: u16 = 0x0001;
19pub const ST_IX: u16 = 0x0002;
21pub const ST_ARGS: u16 = 0x0004;
23pub const ST_NOT_STORED: u16 = 0x0005;
25pub const ST_AUTH_ERROR: u16 = 0x0020;
27pub const ST_AUTH_CONTINUE: u16 = 0x0021;
29pub const ST_UNK: u16 = 0x0081;
31
32pub const MAX_BODY_LEN: u32 = 20 * 1024 * 1024; pub const MAX_KEY_LEN: u16 = 250;
43
44pub const CAS_ZERO: u64 = 0;
51
52#[repr(u8)]
58#[derive(Copy, Clone, Debug, PartialEq, Eq)]
59pub enum Opcode {
60 Get = 0x00,
61 Set = 0x01,
62 Add = 0x02,
63 Replace = 0x03,
64 Delete = 0x04,
65 Increment = 0x05,
66 Decrement = 0x06,
67 Quit = 0x07,
68 Flush = 0x08,
69 GetQ = 0x09,
70 Noop = 0x0a,
71 Version = 0x0b,
72 GetK = 0x0c,
73 GetKQ = 0x0d,
74 Append = 0x0e,
75 Prepend = 0x0f,
76 SetQ = 0x11,
77 AddQ = 0x12,
78 ReplaceQ = 0x13,
79 DeleteQ = 0x14,
80 IncrementQ = 0x15,
81 DecrementQ = 0x16,
82 QuitQ = 0x17,
83 FlushQ = 0x18,
84 AppendQ = 0x19,
85 PrependQ = 0x1a,
86 Stat = 0x10,
87 Verbosity = 0x1b,
88 Touch = 0x1c,
89 GAT = 0x1d,
90 GATQ = 0x1e,
91 SaslListMechs = 0x20,
92 SaslAuth = 0x21,
93 SaslStep = 0x22,
94}
95
96impl Opcode {
97 pub fn parse(b: u8) -> Option<Self> {
100 use Opcode::*;
101 Some(match b {
102 0x00 => Get,
103 0x01 => Set,
104 0x02 => Add,
105 0x03 => Replace,
106 0x04 => Delete,
107 0x05 => Increment,
108 0x06 => Decrement,
109 0x07 => Quit,
110 0x08 => Flush,
111 0x09 => GetQ,
112 0x0a => Noop,
113 0x0b => Version,
114 0x0c => GetK,
115 0x0d => GetKQ,
116 0x0e => Append,
117 0x0f => Prepend,
118 0x11 => SetQ,
119 0x12 => AddQ,
120 0x13 => ReplaceQ,
121 0x14 => DeleteQ,
122 0x15 => IncrementQ,
123 0x16 => DecrementQ,
124 0x17 => QuitQ,
125 0x18 => FlushQ,
126 0x19 => AppendQ,
127 0x1a => PrependQ,
128 0x10 => Stat,
129 0x1b => Verbosity,
130 0x1c => Touch,
131 0x1d => GAT,
132 0x1e => GATQ,
133 0x20 => SaslListMechs,
134 0x21 => SaslAuth,
135 0x22 => SaslStep,
136 _ => return None,
137 })
138 }
139
140 pub fn is_quiet(self) -> bool {
144 use Opcode::*;
145 matches!(
146 self,
147 GetQ | GetKQ
148 | SetQ
149 | AddQ
150 | ReplaceQ
151 | DeleteQ
152 | IncrementQ
153 | DecrementQ
154 | QuitQ
155 | FlushQ
156 | AppendQ
157 | PrependQ
158 | GATQ
159 )
160 }
161
162 pub fn includes_key(self) -> bool {
164 matches!(
165 self,
166 Opcode::GetK | Opcode::GetKQ | Opcode::GAT | Opcode::GATQ
167 )
168 }
169
170 pub fn base(self) -> Self {
174 use Opcode::*;
175 match self {
176 SetQ => Set,
177 AddQ => Add,
178 ReplaceQ => Replace,
179 DeleteQ => Delete,
180 IncrementQ => Increment,
181 DecrementQ => Decrement,
182 QuitQ => Quit,
183 FlushQ => Flush,
184 GetQ => Get,
185 GetKQ => GetK,
186 AppendQ => Append,
187 PrependQ => Prepend,
188 GATQ => GAT,
189 other => other,
190 }
191 }
192}
193
194pub const HEADER_LEN: usize = 24;
197
198#[derive(Debug)]
203pub struct Header {
204 pub opcode: Option<Opcode>,
206 pub opcode_byte: u8,
209 pub key_len: u16,
210 pub extras_len: u8,
211 pub body_len: u32,
212 pub opaque: u32,
213 pub cas: u64,
214}
215
216impl Header {
217 pub fn parse(buf: &[u8]) -> Option<Self> {
223 if buf.len() < HEADER_LEN {
224 return None;
225 }
226 Some(Self {
227 opcode: Opcode::parse(buf[1]),
228 opcode_byte: buf[1],
229 key_len: BigEndian::read_u16(&buf[2..4]),
230 extras_len: buf[4],
231 body_len: BigEndian::read_u32(&buf[8..12]),
232 opaque: BigEndian::read_u32(&buf[12..16]),
233 cas: BigEndian::read_u64(&buf[16..24]),
234 })
235 }
236}
237
238#[derive(Debug)]
243pub struct Request<'a> {
244 pub hdr: Header,
246 pub extras: &'a [u8],
248 pub key: &'a [u8],
250 pub value: &'a [u8],
252}
253
254#[derive(Debug, PartialEq, Eq)]
256pub enum ParseResult<T> {
257 Ok(T),
259 Incomplete,
261 BadMagic,
264 MalformedFrame {
268 opaque: u32,
269 opcode_byte: u8,
270 bytes_to_skip: usize,
271 },
272 OversizedFrame { opaque: u32, opcode_byte: u8 },
277}
278
279pub fn try_parse_request(buf: &[u8]) -> ParseResult<(Request<'_>, usize)> {
286 if buf.is_empty() {
287 return ParseResult::Incomplete;
288 }
289 if buf[0] != MAGIC_REQ {
291 return ParseResult::BadMagic;
292 }
293 let hdr = match Header::parse(buf) {
294 Some(h) => h,
295 None => return ParseResult::Incomplete,
296 };
297 if hdr.body_len > MAX_BODY_LEN || hdr.key_len > MAX_KEY_LEN {
299 return ParseResult::OversizedFrame {
300 opaque: hdr.opaque,
301 opcode_byte: hdr.opcode_byte,
302 };
303 }
304 let total = HEADER_LEN + hdr.body_len as usize;
305 if buf.len() < total {
306 return ParseResult::Incomplete;
307 }
308 let extras_end = HEADER_LEN + hdr.extras_len as usize;
309 let key_end = extras_end + hdr.key_len as usize;
310 if key_end > total || extras_end > total {
311 return ParseResult::MalformedFrame {
312 opaque: hdr.opaque,
313 opcode_byte: hdr.opcode_byte,
314 bytes_to_skip: total,
315 };
316 }
317 ParseResult::Ok((
318 Request {
319 hdr,
320 extras: &buf[HEADER_LEN..extras_end],
321 key: &buf[extras_end..key_end],
322 value: &buf[key_end..total],
323 },
324 total,
325 ))
326}
327
328pub fn parse_request(buf: &[u8]) -> Option<(Request<'_>, usize)> {
331 match try_parse_request(buf) {
332 ParseResult::Ok(pair) => Some(pair),
333 _ => None,
334 }
335}
336
337pub struct ResponseMeta {
343 pub status: u16,
345 pub opaque: u32,
347 pub cas: u64,
349}
350
351pub fn write_raw_response(
355 w: &mut impl Write,
356 opcode_byte: u8,
357 meta: &ResponseMeta,
358 extras: &[u8],
359 key: &[u8],
360 value: &[u8],
361) -> io::Result<()> {
362 let total_body = extras.len() as u32 + key.len() as u32 + value.len() as u32;
363 let mut hdr = [0u8; HEADER_LEN];
364 hdr[0] = MAGIC_RES;
365 hdr[1] = opcode_byte;
366 BigEndian::write_u16(&mut hdr[2..4], key.len() as u16);
367 hdr[4] = extras.len() as u8;
368 BigEndian::write_u16(&mut hdr[6..8], meta.status);
369 BigEndian::write_u32(&mut hdr[8..12], total_body);
370 BigEndian::write_u32(&mut hdr[12..16], meta.opaque);
371 BigEndian::write_u64(&mut hdr[16..24], meta.cas);
372 w.write_all(&hdr)?;
373 w.write_all(extras)?;
374 w.write_all(key)?;
375 w.write_all(value)?;
376 Ok(())
377}
378
379pub fn write_response(
381 w: &mut impl Write,
382 opcode: Opcode,
383 meta: &ResponseMeta,
384 extras: &[u8],
385 key: &[u8],
386 value: &[u8],
387) -> io::Result<()> {
388 write_raw_response(w, opcode as u8, meta, extras, key, value)
389}
390
391pub fn write_simple_response(
393 w: &mut impl Write,
394 opcode: Opcode,
395 status: u16,
396 opaque: u32,
397 cas: u64,
398 body: &[u8],
399) -> io::Result<()> {
400 write_response(
401 w,
402 opcode,
403 &ResponseMeta {
404 status,
405 opaque,
406 cas,
407 },
408 &[],
409 &[],
410 body,
411 )
412}
413
414pub fn write_error_for_raw_opcode(
417 w: &mut impl Write,
418 opcode_byte: u8,
419 status: u16,
420 opaque: u32,
421 body: &[u8],
422) -> io::Result<()> {
423 write_raw_response(
424 w,
425 opcode_byte,
426 &ResponseMeta {
427 status,
428 opaque,
429 cas: CAS_ZERO,
430 },
431 &[],
432 &[],
433 body,
434 )
435}
436
437pub fn build_request_frame(
441 opcode: Opcode,
442 opaque: u32,
443 cas: u64,
444 extras: &[u8],
445 key: &[u8],
446 value: &[u8],
447) -> Vec<u8> {
448 build_raw_request_frame(opcode as u8, opaque, cas, extras, key, value)
449}
450
451pub fn build_raw_request_frame(
454 opcode_byte: u8,
455 opaque: u32,
456 cas: u64,
457 extras: &[u8],
458 key: &[u8],
459 value: &[u8],
460) -> Vec<u8> {
461 let body_len = extras.len() + key.len() + value.len();
462 let mut frame = vec![0u8; HEADER_LEN + body_len];
463 frame[0] = MAGIC_REQ;
464 frame[1] = opcode_byte;
465 BigEndian::write_u16(&mut frame[2..4], key.len() as u16);
466 frame[4] = extras.len() as u8;
467 BigEndian::write_u32(&mut frame[8..12], body_len as u32);
468 BigEndian::write_u32(&mut frame[12..16], opaque);
469 BigEndian::write_u64(&mut frame[16..24], cas);
470 frame[HEADER_LEN..HEADER_LEN + extras.len()].copy_from_slice(extras);
471 let key_start = HEADER_LEN + extras.len();
472 frame[key_start..key_start + key.len()].copy_from_slice(key);
473 let val_start = key_start + key.len();
474 frame[val_start..val_start + value.len()].copy_from_slice(value);
475 frame
476}
477
478#[cfg(test)]
479mod tests {
480 use super::*;
481
482 #[test]
483 fn opcode_round_trip() {
484 for &(byte, expected) in &[
485 (0x00, Opcode::Get),
486 (0x01, Opcode::Set),
487 (0x02, Opcode::Add),
488 (0x03, Opcode::Replace),
489 (0x04, Opcode::Delete),
490 (0x05, Opcode::Increment),
491 (0x06, Opcode::Decrement),
492 (0x07, Opcode::Quit),
493 (0x08, Opcode::Flush),
494 (0x09, Opcode::GetQ),
495 (0x0a, Opcode::Noop),
496 (0x0b, Opcode::Version),
497 (0x0c, Opcode::GetK),
498 (0x0d, Opcode::GetKQ),
499 (0x0e, Opcode::Append),
500 (0x0f, Opcode::Prepend),
501 (0x10, Opcode::Stat),
502 (0x11, Opcode::SetQ),
503 (0x12, Opcode::AddQ),
504 (0x13, Opcode::ReplaceQ),
505 (0x14, Opcode::DeleteQ),
506 (0x15, Opcode::IncrementQ),
507 (0x16, Opcode::DecrementQ),
508 (0x17, Opcode::QuitQ),
509 (0x18, Opcode::FlushQ),
510 (0x19, Opcode::AppendQ),
511 (0x1a, Opcode::PrependQ),
512 (0x1b, Opcode::Verbosity),
513 (0x1c, Opcode::Touch),
514 (0x1d, Opcode::GAT),
515 (0x1e, Opcode::GATQ),
516 (0x20, Opcode::SaslListMechs),
517 (0x21, Opcode::SaslAuth),
518 (0x22, Opcode::SaslStep),
519 ] {
520 assert_eq!(Opcode::parse(byte), Some(expected));
521 assert_eq!(expected as u8, byte);
522 }
523 }
524
525 #[test]
526 fn opcode_unknown_returns_none() {
527 assert!(Opcode::parse(0xFF).is_none());
528 assert!(Opcode::parse(0x80).is_none());
529 }
530
531 #[test]
532 fn quiet_and_key_flags() {
533 assert!(Opcode::GetQ.is_quiet());
534 assert!(Opcode::GetKQ.is_quiet());
535 assert!(Opcode::SetQ.is_quiet());
536 assert!(Opcode::AddQ.is_quiet());
537 assert!(Opcode::ReplaceQ.is_quiet());
538 assert!(Opcode::DeleteQ.is_quiet());
539 assert!(Opcode::IncrementQ.is_quiet());
540 assert!(Opcode::DecrementQ.is_quiet());
541 assert!(Opcode::QuitQ.is_quiet());
542 assert!(Opcode::FlushQ.is_quiet());
543 assert!(Opcode::AppendQ.is_quiet());
544 assert!(Opcode::PrependQ.is_quiet());
545 assert!(Opcode::GATQ.is_quiet());
546 assert!(!Opcode::Get.is_quiet());
547 assert!(!Opcode::Set.is_quiet());
548 assert!(!Opcode::Touch.is_quiet());
549 assert!(!Opcode::Append.is_quiet());
550 assert!(!Opcode::Stat.is_quiet());
551 assert!(!Opcode::Verbosity.is_quiet());
552 assert!(!Opcode::SaslListMechs.is_quiet());
553 assert!(!Opcode::SaslAuth.is_quiet());
554 assert!(!Opcode::SaslStep.is_quiet());
555 assert!(Opcode::GetK.includes_key());
556 assert!(Opcode::GetKQ.includes_key());
557 assert!(Opcode::GAT.includes_key());
558 assert!(Opcode::GATQ.includes_key());
559 assert!(!Opcode::Get.includes_key());
560 assert!(!Opcode::Touch.includes_key());
561 }
562
563 #[test]
564 fn opcode_base() {
565 assert_eq!(Opcode::SetQ.base(), Opcode::Set);
566 assert_eq!(Opcode::AddQ.base(), Opcode::Add);
567 assert_eq!(Opcode::ReplaceQ.base(), Opcode::Replace);
568 assert_eq!(Opcode::DeleteQ.base(), Opcode::Delete);
569 assert_eq!(Opcode::IncrementQ.base(), Opcode::Increment);
570 assert_eq!(Opcode::DecrementQ.base(), Opcode::Decrement);
571 assert_eq!(Opcode::QuitQ.base(), Opcode::Quit);
572 assert_eq!(Opcode::FlushQ.base(), Opcode::Flush);
573 assert_eq!(Opcode::AppendQ.base(), Opcode::Append);
574 assert_eq!(Opcode::PrependQ.base(), Opcode::Prepend);
575 assert_eq!(Opcode::GATQ.base(), Opcode::GAT);
576 assert_eq!(Opcode::Get.base(), Opcode::Get);
577 assert_eq!(Opcode::Noop.base(), Opcode::Noop);
578 assert_eq!(Opcode::Touch.base(), Opcode::Touch);
579 assert_eq!(Opcode::GAT.base(), Opcode::GAT);
580 assert_eq!(Opcode::Stat.base(), Opcode::Stat);
581 assert_eq!(Opcode::Verbosity.base(), Opcode::Verbosity);
582 assert_eq!(Opcode::SaslListMechs.base(), Opcode::SaslListMechs);
583 assert_eq!(Opcode::SaslAuth.base(), Opcode::SaslAuth);
584 assert_eq!(Opcode::SaslStep.base(), Opcode::SaslStep);
585 }
586
587 #[test]
588 fn header_parse_valid() {
589 let frame = build_request_frame(Opcode::Get, 42, 0, &[], b"mykey", &[]);
590 let hdr = Header::parse(&frame).expect("should parse");
591 assert_eq!(hdr.opcode, Some(Opcode::Get));
592 assert_eq!(hdr.opcode_byte, 0x00);
593 assert_eq!(hdr.key_len, 5);
594 assert_eq!(hdr.extras_len, 0);
595 assert_eq!(hdr.body_len, 5);
596 assert_eq!(hdr.opaque, 42);
597 assert_eq!(hdr.cas, 0);
598 }
599
600 #[test]
601 fn header_parse_unknown_opcode() {
602 let frame = build_raw_request_frame(0xFE, 99, 0, &[], b"key", &[]);
603 let hdr = Header::parse(&frame).expect("should parse even unknown opcode");
604 assert_eq!(hdr.opcode, None);
605 assert_eq!(hdr.opcode_byte, 0xFE);
606 assert_eq!(hdr.opaque, 99);
607 }
608
609 #[test]
610 fn header_rejects_short_buffer() {
611 assert!(Header::parse(&[0x80; 10]).is_none());
612 }
613
614 #[test]
615 fn header_parses_any_magic() {
616 let mut frame = build_request_frame(Opcode::Noop, 0, 0, &[], &[], &[]);
619 frame[0] = 0x00;
620 let hdr = Header::parse(&frame);
621 assert!(hdr.is_some());
622 }
623
624 #[test]
627 fn try_parse_empty_is_incomplete() {
628 assert!(matches!(try_parse_request(&[]), ParseResult::Incomplete));
629 }
630
631 #[test]
632 fn try_parse_bad_magic() {
633 let mut frame = build_request_frame(Opcode::Get, 0, 0, &[], b"k", &[]);
634 frame[0] = 0x42;
635 assert!(matches!(try_parse_request(&frame), ParseResult::BadMagic));
636 }
637
638 #[test]
639 fn try_parse_incomplete_header() {
640 assert!(matches!(
641 try_parse_request(&[MAGIC_REQ, 0x00, 0x00]),
642 ParseResult::Incomplete,
643 ));
644 }
645
646 #[test]
647 fn try_parse_incomplete_body() {
648 let frame = build_request_frame(Opcode::Get, 0, 0, &[], b"key", &[]);
649 assert!(matches!(
650 try_parse_request(&frame[..frame.len() - 1]),
651 ParseResult::Incomplete,
652 ));
653 }
654
655 #[test]
656 fn try_parse_malformed_frame() {
657 let mut bad = vec![0u8; HEADER_LEN + 2];
660 bad[0] = MAGIC_REQ;
661 bad[1] = 0x00; BigEndian::write_u16(&mut bad[2..4], 2); bad[4] = 1; BigEndian::write_u32(&mut bad[8..12], 2); BigEndian::write_u32(&mut bad[12..16], 77); match try_parse_request(&bad) {
667 ParseResult::MalformedFrame {
668 opaque,
669 opcode_byte,
670 bytes_to_skip,
671 } => {
672 assert_eq!(opaque, 77);
673 assert_eq!(opcode_byte, 0x00);
674 assert_eq!(bytes_to_skip, HEADER_LEN + 2);
675 }
676 other => panic!("expected MalformedFrame, got {other:?}"),
677 }
678 }
679
680 #[test]
681 fn try_parse_oversized_body() {
682 let mut frame = vec![0u8; HEADER_LEN];
684 frame[0] = MAGIC_REQ;
685 frame[1] = 0x01; BigEndian::write_u32(&mut frame[8..12], MAX_BODY_LEN + 1);
687 BigEndian::write_u32(&mut frame[12..16], 55); match try_parse_request(&frame) {
689 ParseResult::OversizedFrame {
690 opaque,
691 opcode_byte,
692 } => {
693 assert_eq!(opaque, 55);
694 assert_eq!(opcode_byte, 0x01);
695 }
696 other => panic!("expected OversizedFrame, got {other:?}"),
697 }
698 }
699
700 #[test]
701 fn try_parse_oversized_key() {
702 let key_len = MAX_KEY_LEN + 1;
704 let body_len = key_len as u32;
705 let mut frame = vec![0u8; HEADER_LEN + body_len as usize];
706 frame[0] = MAGIC_REQ;
707 frame[1] = 0x00; BigEndian::write_u16(&mut frame[2..4], key_len);
709 BigEndian::write_u32(&mut frame[8..12], body_len);
710 BigEndian::write_u32(&mut frame[12..16], 66);
711 match try_parse_request(&frame) {
712 ParseResult::OversizedFrame {
713 opaque,
714 opcode_byte,
715 } => {
716 assert_eq!(opaque, 66);
717 assert_eq!(opcode_byte, 0x00);
718 }
719 other => panic!("expected OversizedFrame, got {other:?}"),
720 }
721 }
722
723 #[test]
724 fn try_parse_unknown_opcode_still_parses() {
725 let frame = build_raw_request_frame(0xFE, 42, 0, &[], b"k", &[]);
726 match try_parse_request(&frame) {
727 ParseResult::Ok((req, consumed)) => {
728 assert_eq!(consumed, HEADER_LEN + 1);
729 assert!(req.hdr.opcode.is_none());
730 assert_eq!(req.hdr.opcode_byte, 0xFE);
731 assert_eq!(req.hdr.opaque, 42);
732 assert_eq!(req.key, b"k");
733 }
734 other => panic!("expected Ok, got {other:?}"),
735 }
736 }
737
738 #[test]
741 fn parse_request_get() {
742 let frame = build_request_frame(Opcode::Get, 7, 0, &[], b"hello", &[]);
743 let (req, consumed) = parse_request(&frame).expect("should parse");
744 assert_eq!(consumed, HEADER_LEN + 5);
745 assert_eq!(req.hdr.opcode, Some(Opcode::Get));
746 assert_eq!(req.key, b"hello");
747 assert!(req.extras.is_empty());
748 assert!(req.value.is_empty());
749 }
750
751 #[test]
752 fn parse_request_set_with_extras_and_value() {
753 let extras = [0u8; 8];
754 let frame = build_request_frame(Opcode::Set, 1, 0, &extras, b"k", b"val");
755 let (req, consumed) = parse_request(&frame).expect("should parse");
756 assert_eq!(consumed, HEADER_LEN + 8 + 1 + 3);
757 assert_eq!(req.extras.len(), 8);
758 assert_eq!(req.key, b"k");
759 assert_eq!(req.value, b"val");
760 }
761
762 #[test]
763 fn parse_request_incomplete_returns_none() {
764 let frame = build_request_frame(Opcode::Get, 0, 0, &[], b"key", &[]);
765 assert!(parse_request(&frame[..frame.len() - 1]).is_none());
766 }
767
768 #[test]
769 fn parse_request_two_in_buffer() {
770 let f1 = build_request_frame(Opcode::Noop, 1, 0, &[], &[], &[]);
771 let f2 = build_request_frame(Opcode::Quit, 2, 0, &[], &[], &[]);
772 let mut buf = f1.clone();
773 buf.extend_from_slice(&f2);
774
775 let (req1, c1) = parse_request(&buf).expect("first");
776 assert_eq!(req1.hdr.opcode, Some(Opcode::Noop));
777 assert_eq!(req1.hdr.opaque, 1);
778
779 let (req2, c2) = parse_request(&buf[c1..]).expect("second");
780 assert_eq!(req2.hdr.opcode, Some(Opcode::Quit));
781 assert_eq!(req2.hdr.opaque, 2);
782 assert_eq!(c1 + c2, buf.len());
783 }
784
785 #[test]
788 fn write_response_simple() {
789 let mut out = Vec::new();
790 write_simple_response(&mut out, Opcode::Noop, ST_OK, 99, 0, &[]).expect("write");
791 assert_eq!(out.len(), HEADER_LEN);
792 assert_eq!(out[0], MAGIC_RES);
793 assert_eq!(out[1], Opcode::Noop as u8);
794 assert_eq!(BigEndian::read_u16(&out[6..8]), ST_OK);
795 assert_eq!(BigEndian::read_u32(&out[12..16]), 99);
796 }
797
798 #[test]
799 fn write_response_with_body() {
800 let mut out = Vec::new();
801 let extras = 0u32.to_be_bytes();
802 write_response(
803 &mut out,
804 Opcode::Get,
805 &ResponseMeta {
806 status: ST_OK,
807 opaque: 5,
808 cas: 100,
809 },
810 &extras,
811 &[],
812 b"val",
813 )
814 .expect("write");
815 assert_eq!(out.len(), HEADER_LEN + 4 + 3);
816 assert_eq!(out[4], 4);
817 assert_eq!(BigEndian::read_u32(&out[8..12]), 7);
818 assert_eq!(BigEndian::read_u64(&out[16..24]), 100);
819 assert_eq!(&out[HEADER_LEN + 4..], b"val");
820 }
821
822 #[test]
823 fn write_response_getk_with_key() {
824 let mut out = Vec::new();
825 let extras = 0u32.to_be_bytes();
826 write_response(
827 &mut out,
828 Opcode::GetK,
829 &ResponseMeta {
830 status: ST_OK,
831 opaque: 0,
832 cas: 1,
833 },
834 &extras,
835 b"mykey",
836 b"myval",
837 )
838 .expect("write");
839 let key_len = BigEndian::read_u16(&out[2..4]);
840 assert_eq!(key_len, 5);
841 let key_start = HEADER_LEN + 4;
842 assert_eq!(&out[key_start..key_start + 5], b"mykey");
843 assert_eq!(&out[key_start + 5..], b"myval");
844 }
845
846 #[test]
847 fn write_error_for_unknown_opcode() {
848 let mut out = Vec::new();
849 let msg = b"Unknown command";
850 write_error_for_raw_opcode(&mut out, 0xFE, ST_UNK, 42, msg).expect("write");
851 assert_eq!(out[0], MAGIC_RES);
852 assert_eq!(out[1], 0xFE);
853 assert_eq!(BigEndian::read_u16(&out[6..8]), ST_UNK);
854 assert_eq!(BigEndian::read_u32(&out[12..16]), 42);
855 assert_eq!(BigEndian::read_u64(&out[16..24]), CAS_ZERO);
856 assert_eq!(&out[HEADER_LEN..], msg);
857 }
858
859 #[test]
866 fn regression_bad_magic_with_valid_body() {
867 let mut frame = build_request_frame(Opcode::Get, 1, 0, &[], b"key", &[]);
868 frame[0] = 0x81; assert!(matches!(try_parse_request(&frame), ParseResult::BadMagic));
871 frame[0] = 0x00;
872 assert!(matches!(try_parse_request(&frame), ParseResult::BadMagic));
873 }
874
875 #[test]
879 fn regression_malformed_extras_key_overflow() {
880 let mut frame = vec![0u8; HEADER_LEN + 6];
882 frame[0] = MAGIC_REQ;
883 frame[1] = Opcode::Set as u8;
884 BigEndian::write_u16(&mut frame[2..4], 4); frame[4] = 4; BigEndian::write_u32(&mut frame[8..12], 6); BigEndian::write_u32(&mut frame[12..16], 123);
888 match try_parse_request(&frame) {
889 ParseResult::MalformedFrame {
890 opaque,
891 bytes_to_skip,
892 ..
893 } => {
894 assert_eq!(opaque, 123);
895 assert_eq!(bytes_to_skip, HEADER_LEN + 6);
896 }
897 other => panic!("expected MalformedFrame, got {other:?}"),
898 }
899 }
900
901 #[test]
904 fn regression_oversized_at_exact_boundary() {
905 let mut ok_frame = vec![0u8; HEADER_LEN];
907 ok_frame[0] = MAGIC_REQ;
908 ok_frame[1] = Opcode::Set as u8;
909 BigEndian::write_u32(&mut ok_frame[8..12], MAX_BODY_LEN);
910 assert!(matches!(
912 try_parse_request(&ok_frame),
913 ParseResult::Incomplete
914 ));
915
916 let mut bad_frame = vec![0u8; HEADER_LEN];
918 bad_frame[0] = MAGIC_REQ;
919 bad_frame[1] = Opcode::Set as u8;
920 BigEndian::write_u32(&mut bad_frame[8..12], MAX_BODY_LEN + 1);
921 assert!(matches!(
922 try_parse_request(&bad_frame),
923 ParseResult::OversizedFrame { .. }
924 ));
925 }
926
927 #[test]
930 fn regression_key_len_at_exact_boundary() {
931 let mut ok_hdr = vec![0u8; HEADER_LEN];
933 ok_hdr[0] = MAGIC_REQ;
934 ok_hdr[1] = Opcode::Get as u8;
935 BigEndian::write_u16(&mut ok_hdr[2..4], MAX_KEY_LEN); BigEndian::write_u32(&mut ok_hdr[8..12], MAX_KEY_LEN as u32);
937 assert!(matches!(
938 try_parse_request(&ok_hdr),
939 ParseResult::Incomplete
940 ));
941
942 let mut bad_hdr = vec![0u8; HEADER_LEN];
944 bad_hdr[0] = MAGIC_REQ;
945 bad_hdr[1] = Opcode::Get as u8;
946 BigEndian::write_u16(&mut bad_hdr[2..4], MAX_KEY_LEN + 1);
947 BigEndian::write_u32(&mut bad_hdr[8..12], (MAX_KEY_LEN + 1) as u32);
948 assert!(matches!(
949 try_parse_request(&bad_hdr),
950 ParseResult::OversizedFrame { .. }
951 ));
952 }
953
954 #[test]
958 fn regression_delete_success_cas_nonzero_in_response() {
959 let mut out = Vec::new();
960 let delete_cas: u64 = 42;
961 write_simple_response(&mut out, Opcode::Delete, ST_OK, 1, delete_cas, &[]).expect("write");
962 let response_cas = BigEndian::read_u64(&out[16..24]);
963 assert_ne!(response_cas, 0, "DELETE success must carry a non-zero CAS");
964 assert_eq!(response_cas, 42);
965 }
966
967 #[test]
970 fn regression_unknown_opcode_preserves_raw_byte() {
971 for raw_byte in [0x30, 0x7F, 0xAA, 0xFF] {
972 let frame = build_raw_request_frame(raw_byte, 99, 0, &[], b"k", &[]);
973 match try_parse_request(&frame) {
974 ParseResult::Ok((req, _)) => {
975 assert!(req.hdr.opcode.is_none());
976 assert_eq!(req.hdr.opcode_byte, raw_byte);
977 }
978 other => panic!("byte 0x{raw_byte:02x}: expected Ok, got {other:?}"),
979 }
980 }
981 }
982
983 #[test]
989 fn binary_safe_null_bytes_in_key_and_value() {
990 let key = b"key\x00with\x00nulls";
991 let value = b"\x00\x00\x00";
992 let frame = build_request_frame(Opcode::Set, 1, 0, &[0u8; 8], key, value);
993 let (req, consumed) = parse_request(&frame).expect("should parse");
994 assert_eq!(consumed, HEADER_LEN + 8 + key.len() + value.len());
995 assert_eq!(req.key, key);
996 assert_eq!(req.value, value);
997 }
998
999 #[test]
1001 fn binary_safe_high_bytes() {
1002 let key = b"\xff\xfe\xfd";
1003 let value = b"\xff\xff\xff\xff";
1004 let frame = build_request_frame(Opcode::Set, 1, 0, &[0u8; 8], key, value);
1005 let (req, _) = parse_request(&frame).expect("should parse");
1006 assert_eq!(req.key, key);
1007 assert_eq!(req.value, value);
1008 }
1009
1010 #[test]
1012 fn binary_safe_empty_key_and_value() {
1013 let frame = build_request_frame(Opcode::Noop, 1, 0, &[], &[], &[]);
1014 let (req, consumed) = parse_request(&frame).expect("should parse");
1015 assert_eq!(consumed, HEADER_LEN);
1016 assert!(req.key.is_empty());
1017 assert!(req.value.is_empty());
1018 assert!(req.extras.is_empty());
1019 }
1020
1021 #[test]
1023 fn binary_safe_max_key_length() {
1024 let key = vec![b'A'; MAX_KEY_LEN as usize];
1025 let frame = build_request_frame(Opcode::Get, 1, 0, &[], &key, &[]);
1026 let (req, _) = parse_request(&frame).expect("should parse");
1027 assert_eq!(req.key.len(), 250);
1028 }
1029
1030 #[test]
1032 fn binary_safe_all_byte_values() {
1033 let value: Vec<u8> = (0..=255u8).collect();
1034 let frame = build_request_frame(Opcode::Set, 1, 0, &[0u8; 8], b"k", &value);
1035 let (req, _) = parse_request(&frame).expect("should parse");
1036 assert_eq!(req.value.len(), 256);
1037 assert_eq!(req.value, value.as_slice());
1038 }
1039
1040 #[test]
1046 fn response_magic_byte() {
1047 for opcode in [Opcode::Get, Opcode::Set, Opcode::Noop, Opcode::Quit] {
1048 let mut out = Vec::new();
1049 write_simple_response(&mut out, opcode, ST_OK, 0, 0, &[]).unwrap();
1050 assert_eq!(
1051 out[0], MAGIC_RES,
1052 "opcode {opcode:?} response must start with 0x81"
1053 );
1054 }
1055 }
1056
1057 #[test]
1059 fn response_error_carries_cas_zero() {
1060 for status in [ST_NF, ST_IX, ST_ARGS, ST_NOT_STORED, ST_UNK] {
1061 let mut out = Vec::new();
1062 write_simple_response(&mut out, Opcode::Get, status, 0, CAS_ZERO, &[]).unwrap();
1063 let cas = BigEndian::read_u64(&out[16..24]);
1064 assert_eq!(cas, CAS_ZERO, "status 0x{status:04x} must carry CAS_ZERO");
1065 }
1066 }
1067
1068 #[test]
1070 fn response_body_len_field_correct() {
1071 let extras = [1u8, 2, 3, 4];
1072 let key = b"mykey";
1073 let value = b"myvalue";
1074 let mut out = Vec::new();
1075 write_response(
1076 &mut out,
1077 Opcode::GetK,
1078 &ResponseMeta {
1079 status: ST_OK,
1080 opaque: 0,
1081 cas: 1,
1082 },
1083 &extras,
1084 key,
1085 value,
1086 )
1087 .unwrap();
1088 let body_len = BigEndian::read_u32(&out[8..12]);
1089 assert_eq!(body_len as usize, extras.len() + key.len() + value.len());
1090 }
1091
1092 #[test]
1094 fn response_opaque_echo() {
1095 for opaque in [0u32, 1, 0x12345678, u32::MAX] {
1096 let mut out = Vec::new();
1097 write_simple_response(&mut out, Opcode::Noop, ST_OK, opaque, 0, &[]).unwrap();
1098 assert_eq!(BigEndian::read_u32(&out[12..16]), opaque);
1099 }
1100 }
1101
1102 #[test]
1104 fn response_key_len_field_correct() {
1105 let key = b"testkey";
1106 let mut out = Vec::new();
1107 write_response(
1108 &mut out,
1109 Opcode::GetK,
1110 &ResponseMeta {
1111 status: ST_OK,
1112 opaque: 0,
1113 cas: 1,
1114 },
1115 &[0u8; 4],
1116 key,
1117 b"val",
1118 )
1119 .unwrap();
1120 let key_len = BigEndian::read_u16(&out[2..4]);
1121 assert_eq!(key_len as usize, key.len());
1122 }
1123
1124 #[test]
1126 fn response_extras_len_field_correct() {
1127 let extras = [0u8; 4];
1128 let mut out = Vec::new();
1129 write_response(
1130 &mut out,
1131 Opcode::Get,
1132 &ResponseMeta {
1133 status: ST_OK,
1134 opaque: 0,
1135 cas: 1,
1136 },
1137 &extras,
1138 &[],
1139 b"val",
1140 )
1141 .unwrap();
1142 assert_eq!(out[4], 4);
1143 }
1144
1145 #[test]
1147 fn response_raw_opcode_echo() {
1148 for raw in [0x00u8, 0xFE, 0xFF, 0x42] {
1149 let mut out = Vec::new();
1150 write_raw_response(
1151 &mut out,
1152 raw,
1153 &ResponseMeta {
1154 status: ST_OK,
1155 opaque: 0,
1156 cas: 0,
1157 },
1158 &[],
1159 &[],
1160 &[],
1161 )
1162 .unwrap();
1163 assert_eq!(out[1], raw);
1164 }
1165 }
1166
1167 #[test]
1174 fn property_consumed_equals_header_plus_body() {
1175 let opcodes = [
1176 Opcode::Get,
1177 Opcode::Set,
1178 Opcode::Delete,
1179 Opcode::Noop,
1180 Opcode::Increment,
1181 Opcode::Append,
1182 Opcode::Touch,
1183 Opcode::GAT,
1184 Opcode::Flush,
1185 Opcode::Version,
1186 Opcode::Stat,
1187 ];
1188 let extras_sizes = [0, 4, 8, 20];
1189 let key_sizes = [0, 1, 5, 50];
1190 let value_sizes = [0, 1, 10, 100];
1191
1192 for &op in &opcodes {
1193 for &elen in &extras_sizes {
1194 for &klen in &key_sizes {
1195 for &vlen in &value_sizes {
1196 let extras = vec![0u8; elen];
1197 let key = vec![b'k'; klen];
1198 let value = vec![b'v'; vlen];
1199 let frame = build_request_frame(op, 0, 0, &extras, &key, &value);
1200
1201 let expected_body = elen + klen + vlen;
1202 let expected_total = HEADER_LEN + expected_body;
1203 assert_eq!(
1204 frame.len(),
1205 expected_total,
1206 "opcode={op:?} e={elen} k={klen} v={vlen}"
1207 );
1208
1209 match try_parse_request(&frame) {
1210 ParseResult::Ok((req, consumed)) => {
1211 assert_eq!(
1212 consumed, expected_total,
1213 "opcode={op:?} consumed mismatch"
1214 );
1215 assert_eq!(req.extras.len(), elen);
1216 assert_eq!(req.key.len(), klen);
1217 assert_eq!(req.value.len(), vlen);
1218 }
1219 other => panic!("opcode={op:?} e={elen} k={klen} v={vlen}: {other:?}"),
1220 }
1221 }
1222 }
1223 }
1224 }
1225 }
1226
1227 #[test]
1231 fn property_build_always_parseable() {
1232 for op_byte in 0..=0x22u8 {
1233 let op = match Opcode::parse(op_byte) {
1234 Some(o) => o,
1235 None => continue,
1236 };
1237 let frame = build_request_frame(op, 0xDEAD, 0x1234, &[1, 2], b"abc", b"xyz");
1238 match try_parse_request(&frame) {
1239 ParseResult::Ok((req, consumed)) => {
1240 assert_eq!(consumed, HEADER_LEN + 2 + 3 + 3);
1241 assert_eq!(req.hdr.opaque, 0xDEAD);
1242 assert_eq!(req.hdr.cas, 0x1234);
1243 assert_eq!(req.extras, &[1, 2]);
1244 assert_eq!(req.key, b"abc");
1245 assert_eq!(req.value, b"xyz");
1246 }
1247 other => panic!("opcode {op:?}: expected Ok, got {other:?}"),
1248 }
1249 }
1250 }
1251
1252 #[test]
1255 fn property_multi_frame_extraction() {
1256 let f1 = build_request_frame(Opcode::Set, 1, 100, &[0u8; 8], b"k1", b"v1");
1257 let f2 = build_request_frame(Opcode::Get, 2, 0, &[], b"k2", &[]);
1258 let mut buf = f1.clone();
1259 buf.extend_from_slice(&f2);
1260
1261 let (r1, c1) = parse_request(&buf).expect("first frame");
1262 assert_eq!(r1.hdr.opaque, 1);
1263 assert_eq!(r1.hdr.cas, 100);
1264 assert_eq!(r1.key, b"k1");
1265 assert_eq!(r1.value, b"v1");
1266
1267 let (r2, c2) = parse_request(&buf[c1..]).expect("second frame");
1268 assert_eq!(r2.hdr.opaque, 2);
1269 assert_eq!(r2.key, b"k2");
1270
1271 assert_eq!(
1272 c1 + c2,
1273 buf.len(),
1274 "total consumed must equal buffer length"
1275 );
1276 }
1277
1278 #[test]
1281 fn property_trailing_bytes_ignored() {
1282 let frame = build_request_frame(Opcode::Noop, 7, 0, &[], &[], &[]);
1283 let mut buf = frame.clone();
1284 buf.extend_from_slice(&[0xDE, 0xAD]); let (req, consumed) = parse_request(&buf).expect("should parse first frame");
1287 assert_eq!(consumed, HEADER_LEN);
1288 assert_eq!(req.hdr.opaque, 7);
1289 assert_eq!(buf.len() - consumed, 2);
1291 }
1292
1293 #[test]
1306 fn counter_u64_parse_exact_below_2_53() {
1307 let exact_values: &[u64] = &[
1310 0,
1311 1,
1312 u32::MAX as u64,
1313 (1u64 << 53) - 1, ];
1315 for &val in exact_values {
1316 let s = format!("{val}");
1317 let parsed: u64 = s.parse().expect("should parse");
1318 assert_eq!(parsed, val, "value {val} must round-trip exactly");
1319 }
1320 }
1321
1322 #[test]
1326 fn counter_precision_loss_above_2_53() {
1327 let boundary = 1u64 << 53; let f = boundary as f64;
1330 assert_eq!(f as u64, boundary, "2^53 itself is exact in f64");
1331
1332 let above = boundary + 1;
1334 let f_above = above as f64;
1335 assert_ne!(
1337 f_above as u64, above,
1338 "2^53+1 should NOT round-trip exactly through f64 — \
1339 this is precision loss, not wraparound"
1340 );
1341 assert_eq!(
1342 f_above as u64, boundary,
1343 "2^53+1 rounds to 2^53 in f64 — precision loss"
1344 );
1345 }
1346
1347 #[test]
1351 fn counter_lua_format_is_rounding_not_wraparound() {
1352 let val: u64 = (1u64 << 53) + 1;
1355 let as_f64 = val as f64;
1356 let formatted = format!("{:.0}", as_f64);
1357 let back: u64 = formatted.parse().unwrap();
1358 assert_eq!(back, 1u64 << 53);
1360 assert!(
1363 back > 0,
1364 "large counter values round, they don't wrap to zero"
1365 );
1366 }
1367
1368 #[test]
1371 fn counter_response_encoding() {
1372 let test_values: &[u64] = &[0, 1, 255, 256, u32::MAX as u64, u64::MAX];
1373 for &val in test_values {
1374 let encoded = val.to_be_bytes();
1375 assert_eq!(encoded.len(), 8);
1376 let decoded = u64::from_be_bytes(encoded);
1377 assert_eq!(decoded, val);
1378 }
1379 }
1380
1381 #[test]
1388 fn edge_single_magic_byte_is_incomplete() {
1389 assert!(matches!(
1390 try_parse_request(&[MAGIC_REQ]),
1391 ParseResult::Incomplete
1392 ));
1393 }
1394
1395 #[test]
1397 fn edge_one_byte_short_of_header() {
1398 let mut buf = vec![MAGIC_REQ; 23];
1399 buf[0] = MAGIC_REQ;
1400 assert!(matches!(try_parse_request(&buf), ParseResult::Incomplete));
1401 }
1402
1403 #[test]
1406 fn edge_exact_header_zero_body() {
1407 let frame = build_request_frame(Opcode::Noop, 0, 0, &[], &[], &[]);
1408 assert_eq!(frame.len(), HEADER_LEN);
1409 match try_parse_request(&frame) {
1410 ParseResult::Ok((req, consumed)) => {
1411 assert_eq!(consumed, HEADER_LEN);
1412 assert_eq!(req.hdr.body_len, 0);
1413 }
1414 other => panic!("expected Ok, got {other:?}"),
1415 }
1416 }
1417
1418 #[test]
1420 fn edge_cas_preserved_in_header() {
1421 let cas_values: &[u64] = &[0, 1, u64::MAX, 0xDEADBEEFCAFEBABE];
1422 for &cas in cas_values {
1423 let frame = build_request_frame(Opcode::Set, 0, cas, &[0u8; 8], b"k", b"v");
1424 let (req, _) = parse_request(&frame).expect("should parse");
1425 assert_eq!(req.hdr.cas, cas, "CAS 0x{cas:016x} must be preserved");
1426 }
1427 }
1428
1429 #[test]
1431 fn edge_opaque_preserved() {
1432 for opaque in [0u32, 1, u32::MAX, 0x12345678] {
1433 let frame = build_request_frame(Opcode::Get, opaque, 0, &[], b"k", &[]);
1434 let (req, _) = parse_request(&frame).expect("should parse");
1435 assert_eq!(req.hdr.opaque, opaque);
1436 }
1437 }
1438
1439 #[test]
1441 fn response_status_field_position() {
1442 let statuses = [
1443 ST_OK,
1444 ST_NF,
1445 ST_IX,
1446 ST_ARGS,
1447 ST_NOT_STORED,
1448 ST_AUTH_ERROR,
1449 ST_AUTH_CONTINUE,
1450 ST_UNK,
1451 ];
1452 for &status in &statuses {
1453 let mut out = Vec::new();
1454 write_simple_response(&mut out, Opcode::Get, status, 0, 0, &[]).unwrap();
1455 assert_eq!(
1456 BigEndian::read_u16(&out[6..8]),
1457 status,
1458 "status 0x{status:04x} at wrong position"
1459 );
1460 }
1461 }
1462
1463 #[test]
1467 fn quiet_variants_have_loud_base() {
1468 let quiet_loud_pairs = [
1469 (Opcode::GetQ, Opcode::Get),
1470 (Opcode::GetKQ, Opcode::GetK),
1471 (Opcode::SetQ, Opcode::Set),
1472 (Opcode::AddQ, Opcode::Add),
1473 (Opcode::ReplaceQ, Opcode::Replace),
1474 (Opcode::DeleteQ, Opcode::Delete),
1475 (Opcode::IncrementQ, Opcode::Increment),
1476 (Opcode::DecrementQ, Opcode::Decrement),
1477 (Opcode::QuitQ, Opcode::Quit),
1478 (Opcode::FlushQ, Opcode::Flush),
1479 (Opcode::AppendQ, Opcode::Append),
1480 (Opcode::PrependQ, Opcode::Prepend),
1481 (Opcode::GATQ, Opcode::GAT),
1482 ];
1483 for (quiet, loud) in quiet_loud_pairs {
1484 assert!(quiet.is_quiet(), "{quiet:?} must be quiet");
1485 assert!(!loud.is_quiet(), "{loud:?} must not be quiet");
1486 assert_eq!(quiet.base(), loud, "{quiet:?}.base() must be {loud:?}");
1487 assert_eq!(loud.base(), loud, "{loud:?}.base() must be self");
1488 }
1489 }
1490
1491 #[test]
1494 fn opcode_coverage_no_gaps_in_valid_range() {
1495 let valid_bytes: Vec<u8> = vec![
1496 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
1497 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
1498 0x1c, 0x1d, 0x1e, 0x20, 0x21, 0x22,
1499 ];
1500 for b in valid_bytes {
1501 assert!(
1502 Opcode::parse(b).is_some(),
1503 "byte 0x{b:02x} must map to a known opcode"
1504 );
1505 }
1506 assert!(Opcode::parse(0x1f).is_none(), "0x1f must be unknown");
1508 }
1509
1510 #[test]
1513 fn response_cas_field_full_range() {
1514 for cas in [0u64, 1, u64::MAX, 0xCAFEBABEDEADBEEF] {
1515 let mut out = Vec::new();
1516 write_simple_response(&mut out, Opcode::Get, ST_OK, 0, cas, &[]).unwrap();
1517 assert_eq!(BigEndian::read_u64(&out[16..24]), cas);
1518 }
1519 }
1520
1521 #[test]
1524 fn build_raw_matches_build_typed() {
1525 let typed = build_request_frame(Opcode::Get, 42, 100, &[1, 2], b"key", b"val");
1526 let raw = build_raw_request_frame(0x00, 42, 100, &[1, 2], b"key", b"val");
1527 assert_eq!(typed, raw);
1528 }
1529
1530 #[test]
1533 fn parse_request_returns_some_for_valid() {
1534 let frame = build_request_frame(Opcode::Get, 0, 0, &[], b"key", &[]);
1535 let result = parse_request(&frame);
1536 assert!(result.is_some());
1537 let (req, consumed) = result.unwrap();
1538 assert_eq!(req.key, b"key");
1539 assert_eq!(consumed, frame.len());
1540 }
1541
1542 #[test]
1543 fn parse_request_returns_none_for_bad_magic() {
1544 let mut frame = build_request_frame(Opcode::Get, 0, 0, &[], b"key", &[]);
1545 frame[0] = 0xFF; assert!(parse_request(&frame).is_none());
1547 }
1548
1549 #[test]
1550 fn parse_request_returns_none_for_incomplete() {
1551 assert!(parse_request(&[0x80, 0x00]).is_none());
1552 }
1553
1554 #[test]
1555 fn parse_request_returns_none_for_empty() {
1556 assert!(parse_request(&[]).is_none());
1557 }
1558
1559 #[test]
1562 fn write_simple_response_no_body() {
1563 let mut out = Vec::new();
1564 write_simple_response(&mut out, Opcode::Noop, ST_OK, 0, 0, &[]).unwrap();
1565 assert_eq!(out.len(), HEADER_LEN);
1566 assert_eq!(out[0], MAGIC_RES);
1567 assert_eq!(out[1], Opcode::Noop as u8);
1568 assert_eq!(BigEndian::read_u32(&out[8..12]), 0); }
1570
1571 #[test]
1572 fn write_simple_response_with_body() {
1573 let mut out = Vec::new();
1574 write_simple_response(&mut out, Opcode::Version, ST_OK, 0, 0, b"1.0").unwrap();
1575 assert_eq!(out.len(), HEADER_LEN + 3);
1576 assert_eq!(BigEndian::read_u32(&out[8..12]), 3); assert_eq!(&out[HEADER_LEN..], b"1.0");
1578 }
1579
1580 #[test]
1583 fn write_error_for_raw_opcode_sets_cas_zero() {
1584 let mut out = Vec::new();
1585 write_error_for_raw_opcode(&mut out, 0xFF, ST_UNK, 42, b"err").unwrap();
1586 assert_eq!(BigEndian::read_u64(&out[16..24]), CAS_ZERO);
1587 assert_eq!(out[1], 0xFF); assert_eq!(BigEndian::read_u32(&out[12..16]), 42); assert_eq!(BigEndian::read_u16(&out[6..8]), ST_UNK); }
1591
1592 #[test]
1595 fn includes_key_for_all_variants() {
1596 assert!(Opcode::GetK.includes_key());
1598 assert!(Opcode::GetKQ.includes_key());
1599 assert!(Opcode::GAT.includes_key());
1600 assert!(Opcode::GATQ.includes_key());
1601 assert!(!Opcode::Get.includes_key());
1603 assert!(!Opcode::Set.includes_key());
1604 assert!(!Opcode::Delete.includes_key());
1605 assert!(!Opcode::Noop.includes_key());
1606 assert!(!Opcode::GetQ.includes_key());
1607 }
1608
1609 #[test]
1612 fn sasl_opcodes_not_quiet() {
1613 assert!(!Opcode::SaslListMechs.is_quiet());
1614 assert!(!Opcode::SaslAuth.is_quiet());
1615 assert!(!Opcode::SaslStep.is_quiet());
1616 }
1617
1618 #[test]
1619 fn sasl_opcodes_base_is_self() {
1620 assert_eq!(Opcode::SaslListMechs.base(), Opcode::SaslListMechs);
1621 assert_eq!(Opcode::SaslAuth.base(), Opcode::SaslAuth);
1622 assert_eq!(Opcode::SaslStep.base(), Opcode::SaslStep);
1623 }
1624
1625 #[test]
1628 fn stat_verbosity_not_quiet() {
1629 assert!(!Opcode::Stat.is_quiet());
1630 assert!(!Opcode::Verbosity.is_quiet());
1631 assert!(!Opcode::Version.is_quiet());
1632 }
1633
1634 #[test]
1635 fn touch_not_quiet() {
1636 assert!(!Opcode::Touch.is_quiet());
1637 }
1638
1639 #[test]
1642 fn build_request_frame_empty_all() {
1643 let frame = build_request_frame(Opcode::Noop, 0, 0, &[], &[], &[]);
1644 assert_eq!(frame.len(), HEADER_LEN);
1645 assert_eq!(frame[0], MAGIC_REQ);
1646 assert_eq!(frame[1], Opcode::Noop as u8);
1647 assert_eq!(BigEndian::read_u16(&frame[2..4]), 0); assert_eq!(frame[4], 0); assert_eq!(BigEndian::read_u32(&frame[8..12]), 0); }
1651
1652 #[test]
1653 fn build_request_frame_with_extras_key_value() {
1654 let extras = vec![1, 2, 3, 4];
1655 let key = b"testkey";
1656 let value = b"testvalue";
1657 let frame = build_request_frame(Opcode::Set, 99, 55, &extras, key, value);
1658 assert_eq!(frame.len(), HEADER_LEN + 4 + 7 + 9);
1659 assert_eq!(BigEndian::read_u16(&frame[2..4]), 7); assert_eq!(frame[4], 4); assert_eq!(BigEndian::read_u32(&frame[8..12]), 20); assert_eq!(BigEndian::read_u32(&frame[12..16]), 99); assert_eq!(BigEndian::read_u64(&frame[16..24]), 55); assert_eq!(&frame[HEADER_LEN..HEADER_LEN + 4], &extras);
1666 assert_eq!(&frame[HEADER_LEN + 4..HEADER_LEN + 11], key);
1667 assert_eq!(&frame[HEADER_LEN + 11..], value);
1668 }
1669
1670 #[test]
1673 fn malformed_frame_preserves_opaque_and_opcode() {
1674 let mut frame = [0u8; HEADER_LEN + 4];
1676 frame[0] = MAGIC_REQ;
1677 frame[1] = Opcode::Set as u8;
1678 BigEndian::write_u16(&mut frame[2..4], 10); frame[4] = 10; BigEndian::write_u32(&mut frame[8..12], 4); BigEndian::write_u32(&mut frame[12..16], 0xDEAD); match try_parse_request(&frame) {
1683 ParseResult::MalformedFrame {
1684 opaque,
1685 opcode_byte,
1686 bytes_to_skip,
1687 } => {
1688 assert_eq!(opaque, 0xDEAD);
1689 assert_eq!(opcode_byte, Opcode::Set as u8);
1690 assert_eq!(bytes_to_skip, HEADER_LEN + 4);
1691 }
1692 other => panic!("expected MalformedFrame, got {other:?}"),
1693 }
1694 }
1695
1696 #[test]
1699 fn oversized_key_at_boundary_plus_one() {
1700 let mut frame = [0u8; HEADER_LEN];
1701 frame[0] = MAGIC_REQ;
1702 frame[1] = Opcode::Get as u8;
1703 BigEndian::write_u16(&mut frame[2..4], MAX_KEY_LEN + 1);
1704 BigEndian::write_u32(&mut frame[8..12], (MAX_KEY_LEN + 1) as u32);
1705 BigEndian::write_u32(&mut frame[12..16], 0xBEEF);
1706 match try_parse_request(&frame) {
1707 ParseResult::OversizedFrame {
1708 opaque,
1709 opcode_byte,
1710 } => {
1711 assert_eq!(opaque, 0xBEEF);
1712 assert_eq!(opcode_byte, Opcode::Get as u8);
1713 }
1714 other => panic!("expected OversizedFrame, got {other:?}"),
1715 }
1716 }
1717}