1use crate::error::ProtocolError;
31use crate::proto::Limits;
32use yo_common::num::parse_i64;
33
34#[derive(Debug, Clone, Copy)]
36struct Span {
37 start: usize,
38 len: u32,
39 scratch: bool,
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub enum Step {
47 Incomplete,
49 Command {
57 consumed: usize,
59 },
60}
61
62#[derive(Debug, Default)]
67pub struct Argv {
68 spans: Vec<Span>,
69 scratch: Vec<u8>,
72 next: usize,
74 want: Option<u32>,
76}
77
78impl Argv {
79 pub fn new() -> Argv {
81 Argv::default()
82 }
83
84 pub fn with_capacity(n: usize) -> Argv {
89 Argv {
90 spans: Vec::with_capacity(n),
91 ..Argv::default()
92 }
93 }
94
95 pub fn reset(&mut self) {
101 self.spans.clear();
102 self.scratch.clear();
103 self.next = 0;
104 self.want = None;
105 }
106
107 pub fn len(&self) -> usize {
109 self.spans.len()
110 }
111
112 pub fn is_empty(&self) -> bool {
114 self.spans.is_empty()
115 }
116
117 pub fn arg<'a>(&'a self, buf: &'a [u8], i: usize) -> Option<&'a [u8]> {
122 let s = self.spans.get(i)?;
123 let src = if s.scratch { &self.scratch[..] } else { buf };
124 src.get(s.start..s.start + s.len as usize)
125 }
126
127 pub fn args<'a>(&'a self, buf: &'a [u8]) -> impl Iterator<Item = &'a [u8]> {
129 (0..self.spans.len()).filter_map(move |i| self.arg(buf, i))
130 }
131
132 pub fn decode(&mut self, buf: &[u8], limits: &Limits) -> Result<Step, ProtocolError> {
141 if self.want.is_none() {
142 self.spans.clear();
146 self.scratch.clear();
147 self.next = 0;
148
149 if buf.is_empty() {
150 return Ok(Step::Incomplete);
151 }
152 if buf[0] != b'*' {
153 return self.inline(buf, limits);
154 }
155 let Some((line, after)) = line_at(buf, 1, ProtocolError::InvalidMultibulkLength)?
156 else {
157 return if buf.len() > limits.max_inline {
161 Err(ProtocolError::TooBigMbulkCount)
162 } else {
163 Ok(Step::Incomplete)
164 };
165 };
166 let count = parse_i64(line).ok_or(ProtocolError::InvalidMultibulkLength)?;
167 if count > limits.max_multibulk as i64 {
168 return Err(ProtocolError::InvalidMultibulkLength);
169 }
170 if count <= 0 {
171 return Ok(Step::Command { consumed: after });
174 }
175 self.spans.reserve(count as usize);
178 self.next = after;
179 self.want = Some(count as u32);
180 }
181
182 while self.want.is_some_and(|w| w > 0) {
183 let Some(&kind) = buf.get(self.next) else {
184 return Ok(Step::Incomplete);
185 };
186 if kind != b'$' {
187 return Err(ProtocolError::ExpectedDollar(kind));
188 }
189 let Some((line, after)) =
190 line_at(buf, self.next + 1, ProtocolError::InvalidBulkLength)?
191 else {
192 return if buf.len() - self.next > limits.max_inline {
193 Err(ProtocolError::TooBigBulkCount)
194 } else {
195 Ok(Step::Incomplete)
196 };
197 };
198 let len = parse_i64(line).ok_or(ProtocolError::InvalidBulkLength)?;
199 if len < 0 || len > limits.max_bulk as i64 {
200 return Err(ProtocolError::InvalidBulkLength);
201 }
202 let len = len as usize;
203 if buf.len() < after + len + 2 {
207 return Ok(Step::Incomplete);
208 }
209 self.spans.push(Span {
210 start: after,
211 len: len as u32,
212 scratch: false,
213 });
214 self.next = after + len + 2;
215 self.want = self.want.map(|w| w - 1);
216 }
217
218 let consumed = self.next;
219 self.want = None;
220 self.next = 0;
221 Ok(Step::Command { consumed })
222 }
223
224 fn inline(&mut self, buf: &[u8], limits: &Limits) -> Result<Step, ProtocolError> {
230 let Some(nl) = buf.iter().position(|&b| b == b'\n') else {
231 return if buf.len() > limits.max_inline {
232 Err(ProtocolError::TooBigInline)
233 } else {
234 Ok(Step::Incomplete)
235 };
236 };
237 let mut line = &buf[..nl];
238 if line.last() == Some(&b'\r') {
239 line = &line[..line.len() - 1];
240 }
241 self.split_inline(line)?;
242 Ok(Step::Command { consumed: nl + 1 })
243 }
244
245 fn split_inline(&mut self, line: &[u8]) -> Result<(), ProtocolError> {
253 let mut i = 0;
254 loop {
255 while i < line.len() && is_space(line[i]) {
256 i += 1;
257 }
258 if i >= line.len() {
259 return Ok(());
260 }
261 let start = self.scratch.len();
262 let mut in_double = false;
263 let mut in_single = false;
264 let mut done = false;
265 while !done {
266 let c = line.get(i).copied();
267 if in_double {
268 match c {
269 Some(b'\\')
270 if i + 3 < line.len()
271 && line[i + 1] == b'x'
272 && hex(line[i + 2]).is_some()
273 && hex(line[i + 3]).is_some() =>
274 {
275 let hi = hex(line[i + 2]).unwrap_or(0);
276 let lo = hex(line[i + 3]).unwrap_or(0);
277 self.scratch.push(hi * 16 + lo);
278 i += 3;
279 }
280 Some(b'\\') if i + 1 < line.len() => {
281 i += 1;
282 self.scratch.push(match line[i] {
283 b'n' => b'\n',
284 b'r' => b'\r',
285 b't' => b'\t',
286 b'b' => 0x08,
287 b'a' => 0x07,
288 other => other,
289 });
290 }
291 Some(b'"') => {
292 if line.get(i + 1).is_some_and(|&n| !is_space(n)) {
293 return Err(ProtocolError::UnbalancedQuotes);
294 }
295 done = true;
296 }
297 None => return Err(ProtocolError::UnbalancedQuotes),
298 Some(ch) => self.scratch.push(ch),
299 }
300 } else if in_single {
301 match c {
302 Some(b'\\') if line.get(i + 1) == Some(&b'\'') => {
303 i += 1;
304 self.scratch.push(b'\'');
305 }
306 Some(b'\'') => {
307 if line.get(i + 1).is_some_and(|&n| !is_space(n)) {
308 return Err(ProtocolError::UnbalancedQuotes);
309 }
310 done = true;
311 }
312 None => return Err(ProtocolError::UnbalancedQuotes),
313 Some(ch) => self.scratch.push(ch),
314 }
315 } else {
316 match c {
317 None | Some(b' ') | Some(b'\n') | Some(b'\r') | Some(b'\t') => done = true,
318 Some(b'"') => in_double = true,
319 Some(b'\'') => in_single = true,
320 Some(ch) => self.scratch.push(ch),
321 }
322 }
323 if i < line.len() {
324 i += 1;
325 }
326 }
327 let len = self.scratch.len() - start;
328 self.spans.push(Span {
329 start,
330 len: len as u32,
331 scratch: true,
332 });
333 }
334 }
335}
336
337#[inline]
339const fn is_space(b: u8) -> bool {
340 matches!(b, b' ' | b'\t' | b'\n' | 0x0b | 0x0c | b'\r')
341}
342
343#[inline]
345const fn hex(b: u8) -> Option<u8> {
346 match b {
347 b'0'..=b'9' => Some(b - b'0'),
348 b'a'..=b'f' => Some(b - b'a' + 10),
349 b'A'..=b'F' => Some(b - b'A' + 10),
350 _ => None,
351 }
352}
353
354fn line_at(
362 buf: &[u8],
363 from: usize,
364 bad: ProtocolError,
365) -> Result<Option<(&[u8], usize)>, ProtocolError> {
366 let Some(off) = buf[from..].iter().position(|&b| b == b'\r') else {
367 return Ok(None);
368 };
369 let cr = from + off;
370 match buf.get(cr + 1) {
371 None => Ok(None),
372 Some(&b'\n') => Ok(Some((&buf[from..cr], cr + 2))),
373 Some(_) => Err(bad),
374 }
375}
376
377#[cfg(test)]
378mod tests {
379 use super::*;
380
381 fn one(buf: &[u8]) -> Result<(Vec<Vec<u8>>, usize), ProtocolError> {
384 let mut argv = Argv::new();
385 match argv.decode(buf, &Limits::default())? {
386 Step::Incomplete => panic!("expected a whole command in {buf:?}"),
387 Step::Command { consumed } => Ok((
388 argv.args(buf).map(<[u8]>::to_vec).collect::<Vec<_>>(),
389 consumed,
390 )),
391 }
392 }
393
394 fn words(buf: &[u8]) -> Vec<Vec<u8>> {
395 one(buf).expect("should decode").0
396 }
397
398 #[test]
399 fn a_multibulk_command_comes_out_as_its_arguments() {
400 let (args, consumed) = one(b"*3\r\n$3\r\nSET\r\n$1\r\nk\r\n$1\r\nv\r\n").unwrap();
401 assert_eq!(args, vec![b"SET".to_vec(), b"k".to_vec(), b"v".to_vec()]);
402 assert_eq!(consumed, 27);
403 }
404
405 #[test]
406 fn an_empty_argument_is_an_argument() {
407 assert_eq!(
408 words(b"*2\r\n$3\r\nGET\r\n$0\r\n\r\n"),
409 vec![b"GET".to_vec(), Vec::new()]
410 );
411 }
412
413 #[test]
414 fn a_value_can_hold_anything_including_crlf() {
415 let args = words(b"*3\r\n$3\r\nSET\r\n$1\r\nk\r\n$4\r\na\r\nb\r\n");
416 assert_eq!(args[2], b"a\r\nb".to_vec());
417 }
418
419 #[test]
422 fn commands_come_out_one_at_a_time_from_one_buffer() {
423 let buf = b"*1\r\n$4\r\nPING\r\n*2\r\n$3\r\nGET\r\n$1\r\nk\r\n";
424 let mut argv = Argv::new();
425 let mut at = 0;
426 let mut seen: Vec<Vec<Vec<u8>>> = Vec::new();
427 loop {
428 match argv.decode(&buf[at..], &Limits::default()).unwrap() {
429 Step::Incomplete => break,
430 Step::Command { consumed } => {
431 seen.push(argv.args(&buf[at..]).map(<[u8]>::to_vec).collect());
432 at += consumed;
433 }
434 }
435 }
436 assert_eq!(at, buf.len());
437 assert_eq!(seen.len(), 2);
438 assert_eq!(seen[0], vec![b"PING".to_vec()]);
439 assert_eq!(seen[1], vec![b"GET".to_vec(), b"k".to_vec()]);
440 }
441
442 #[test]
447 fn a_command_arriving_one_byte_at_a_time_decodes_once_at_the_end() {
448 let whole = b"*3\r\n$3\r\nSET\r\n$5\r\nhello\r\n$5\r\nworld\r\n";
449 let mut argv = Argv::new();
450 for n in 0..whole.len() {
451 assert_eq!(
452 argv.decode(&whole[..n], &Limits::default()).unwrap(),
453 Step::Incomplete,
454 "the first {n} bytes should not be a command"
455 );
456 }
457 let step = argv.decode(whole, &Limits::default()).unwrap();
458 assert_eq!(
459 step,
460 Step::Command {
461 consumed: whole.len()
462 }
463 );
464 assert_eq!(
465 argv.args(whole).map(<[u8]>::to_vec).collect::<Vec<_>>(),
466 vec![b"SET".to_vec(), b"hello".to_vec(), b"world".to_vec()]
467 );
468 }
469
470 #[test]
475 fn a_partly_arrived_command_remembers_where_it_got_to() {
476 let mut argv = Argv::new();
477 let head = b"*3\r\n$3\r\nSET\r\n$1\r\nk\r\n$10\r\nabc";
478 assert_eq!(
479 argv.decode(head, &Limits::default()).unwrap(),
480 Step::Incomplete
481 );
482 assert_eq!(argv.want, Some(1), "two of three arguments are in");
483 assert_eq!(argv.next, 20, "the third argument's body starts here");
484 }
485
486 #[test]
487 fn an_empty_command_is_consumed_and_has_no_arguments() {
488 for buf in [&b"*0\r\n"[..], b"*-1\r\n"] {
489 let (args, consumed) = one(buf).unwrap();
490 assert!(args.is_empty(), "{buf:?}");
491 assert_eq!(consumed, buf.len());
492 }
493 }
494
495 #[test]
496 fn an_inline_command_is_split_on_whitespace() {
497 assert_eq!(words(b"PING\r\n"), vec![b"PING".to_vec()]);
498 assert_eq!(
499 words(b"SET key value\n"),
500 vec![b"SET".to_vec(), b"key".to_vec(), b"value".to_vec()]
501 );
502 assert!(words(b"\r\n").is_empty());
503 assert!(words(b" \n").is_empty());
504 }
505
506 #[test]
507 fn inline_quotes_and_escapes_follow_redis() {
508 assert_eq!(
509 words(b"SET k \"a b\"\r\n"),
510 vec![b"SET".to_vec(), b"k".to_vec(), b"a b".to_vec()]
511 );
512 assert_eq!(words(b"ECHO \"\\x41\\x42\"\r\n")[1], b"AB".to_vec());
513 assert_eq!(words(b"ECHO \"a\\nb\"\r\n")[1], b"a\nb".to_vec());
514 assert_eq!(words(b"ECHO 'it\\'s'\r\n")[1], b"it's".to_vec());
515 assert_eq!(words(b"ECHO \"\"\r\n")[1], Vec::<u8>::new());
516 }
517
518 #[test]
519 fn an_unclosed_or_misplaced_quote_is_an_error() {
520 for bad in [
521 &b"ECHO \"abc\r\n"[..],
522 b"ECHO 'abc\r\n",
523 b"ECHO \"abc\"d\r\n",
524 b"ECHO 'abc'd\r\n",
525 ] {
526 let mut argv = Argv::new();
527 assert_eq!(
528 argv.decode(bad, &Limits::default()),
529 Err(ProtocolError::UnbalancedQuotes),
530 "{bad:?}"
531 );
532 }
533 }
534
535 #[test]
536 fn the_wrong_type_byte_where_an_argument_belongs_names_the_byte() {
537 let mut argv = Argv::new();
538 assert_eq!(
539 argv.decode(b"*1\r\n+OK\r\n", &Limits::default()),
540 Err(ProtocolError::ExpectedDollar(b'+'))
541 );
542 }
543
544 #[test]
545 fn lengths_that_are_not_lengths_are_refused() {
546 let cases: &[(&[u8], ProtocolError)] = &[
547 (b"*x\r\n", ProtocolError::InvalidMultibulkLength),
548 (b"*\r\n", ProtocolError::InvalidMultibulkLength),
549 (b"*01\r\n", ProtocolError::InvalidMultibulkLength),
550 (
551 b"*99999999999999999999\r\n",
552 ProtocolError::InvalidMultibulkLength,
553 ),
554 (b"*2\r\n$x\r\n", ProtocolError::InvalidBulkLength),
555 (b"*2\r\n$-1\r\n", ProtocolError::InvalidBulkLength),
556 ];
557 for &(buf, want) in cases {
558 let mut argv = Argv::new();
559 assert_eq!(argv.decode(buf, &Limits::default()), Err(want), "{buf:?}");
560 }
561 }
562
563 #[test]
567 fn an_enormous_count_is_refused_rather_than_reserved() {
568 let mut argv = Argv::new();
569 assert_eq!(
570 argv.decode(b"*2000000000\r\n", &Limits::default()),
571 Err(ProtocolError::InvalidMultibulkLength)
572 );
573 assert_eq!(
574 argv.spans.capacity(),
575 0,
576 "nothing should have been reserved"
577 );
578 }
579
580 #[test]
581 fn a_bulk_past_the_limit_is_refused() {
582 let limits = Limits {
583 max_bulk: 16,
584 ..Limits::default()
585 };
586 let mut argv = Argv::new();
587 assert_eq!(
588 argv.decode(b"*1\r\n$17\r\n", &limits),
589 Err(ProtocolError::InvalidBulkLength)
590 );
591 let mut argv = Argv::new();
593 assert_eq!(argv.decode(b"*1\r\n$16\r\n", &limits), Ok(Step::Incomplete));
594 }
595
596 #[test]
597 fn a_line_that_never_ends_is_refused_rather_than_buffered_forever() {
598 let limits = Limits {
599 max_inline: 8,
600 ..Limits::default()
601 };
602 let mut argv = Argv::new();
603 assert_eq!(
604 argv.decode(b"*123456789", &limits),
605 Err(ProtocolError::TooBigMbulkCount)
606 );
607 let mut argv = Argv::new();
608 assert_eq!(
609 argv.decode(b"*1\r\n$123456789", &limits),
610 Err(ProtocolError::TooBigBulkCount)
611 );
612 let mut argv = Argv::new();
613 assert_eq!(
614 argv.decode(b"PING PING PING", &limits),
615 Err(ProtocolError::TooBigInline)
616 );
617 }
618
619 #[test]
620 fn a_carriage_return_with_no_newline_after_it_is_a_protocol_error() {
621 let mut argv = Argv::new();
622 assert_eq!(
623 argv.decode(b"*1\rx", &Limits::default()),
624 Err(ProtocolError::InvalidMultibulkLength)
625 );
626 }
627
628 #[test]
629 fn a_reset_forgets_a_half_read_command() {
630 let mut argv = Argv::new();
631 assert_eq!(
632 argv.decode(b"*2\r\n$3\r\nGET\r\n", &Limits::default())
633 .unwrap(),
634 Step::Incomplete
635 );
636 assert_eq!(argv.want, Some(1));
637 argv.reset();
638 assert_eq!(argv.want, None);
639 assert_eq!(argv.next, 0);
640 assert_eq!(
642 argv.decode(b"*1\r\n$4\r\nPING\r\n", &Limits::default())
643 .unwrap(),
644 Step::Command { consumed: 14 }
645 );
646 }
647
648 #[test]
652 fn arguments_do_not_survive_into_the_next_command() {
653 let mut argv = Argv::new();
654 argv.decode(b"*2\r\n$3\r\nGET\r\n$1\r\nk\r\n", &Limits::default())
655 .unwrap();
656 assert_eq!(argv.len(), 2);
657 argv.decode(b"*1\r\n$4\r\nPING\r\n", &Limits::default())
658 .unwrap();
659 assert_eq!(argv.len(), 1);
660 assert_eq!(argv.arg(b"*1\r\n$4\r\nPING\r\n", 1), None);
661 }
662}