atmos/kernel/
http_body.rs1pub fn body_start(raw: &[u8]) -> Option<usize> {
16 raw.windows(4).position(|w| w == b"\r\n\r\n").map(|i| i + 4)
17}
18
19pub fn content_length(raw: &[u8]) -> Option<usize> {
21 let end = body_start(raw)?;
22 const KEY: &[u8] = b"content-length:";
25 let head = &raw[..end];
26 let mut i = 0usize;
27 while i + KEY.len() <= head.len() {
28 let hit = head[i..i + KEY.len()]
29 .iter()
30 .zip(KEY.iter())
31 .all(|(a, b)| a.to_ascii_lowercase() == *b);
32 if hit {
33 let mut j = i + KEY.len();
35 let mut v: usize = 0;
36 let mut seen = false;
37 while j < head.len() && head[j] != 13 {
38 match head[j] {
39 d @ 48..=57 => {
40 v = v.checked_mul(10)?.checked_add((d - 48) as usize)?;
41 seen = true;
42 }
43 32 | 9 if !seen => {}
44 _ => break,
45 }
46 j += 1;
47 }
48 return if seen { Some(v) } else { None };
49 }
50 i += 1;
51 }
52 None
53}
54
55pub fn body_range(raw: &[u8]) -> Option<(usize, usize)> {
60 let start = body_start(raw)?;
61 let len = content_length(raw)?;
62 let end = start.checked_add(len)?;
63 if raw.len() >= end {
64 Some((start, end))
65 } else {
66 None
67 }
68}
69
70pub fn looks_truncated(raw: &[u8]) -> bool {
79 let Some(start) = body_start(raw) else {
80 return false;
81 };
82 let Some(len) = content_length(raw) else {
83 return false;
84 };
85 raw.len().saturating_sub(start) < len
86}