sark_core/http/head/
input.rs1use super::byte::is_ascii_ws;
2use super::error::ERR_INVALID_HEADER_NAME;
3
4pub struct HeaderLineScan {
5 pub end: usize,
6 pub colon: Option<usize>,
7 pub value_start: usize,
8 pub value_end: usize,
9}
10
11pub trait HeadInput {
12 fn len(&self) -> usize;
13 fn is_empty(&self) -> bool {
14 self.len() == 0
15 }
16 fn find_crlf_from(&self, start: usize) -> Option<usize>;
17 fn slice_range(&self, range: std::ops::Range<usize>) -> Option<&[u8]>;
18 fn copy_range_local(
19 &self,
20 range: std::ops::Range<usize>,
21 ) -> Option<crate::http::LocalFrameBytes>;
22 fn copy_range_into(&self, range: std::ops::Range<usize>, out: &mut [u8]);
23 fn for_each_slice<F>(&self, range: std::ops::Range<usize>, f: F)
24 where
25 F: FnMut(&[u8]);
26}
27
28impl HeadInput for [u8] {
29 fn len(&self) -> usize {
30 <[u8]>::len(self)
31 }
32
33 fn find_crlf_from(&self, start: usize) -> Option<usize> {
34 BytesScan::find_crlf_from(self, start)
35 }
36
37 fn slice_range(&self, range: std::ops::Range<usize>) -> Option<&[u8]> {
38 self.get(range)
39 }
40
41 fn copy_range_into(&self, range: std::ops::Range<usize>, out: &mut [u8]) {
42 assert!(
43 range.start <= range.end && range.end <= self.len(),
44 "head input copy range invariant: range out of bounds",
45 );
46 let need = range.end - range.start;
47 assert!(
48 out.len() >= need,
49 "head input copy range invariant: output too small",
50 );
51 out[..need].copy_from_slice(&self[range]);
52 }
53
54 fn copy_range_local(
55 &self,
56 range: std::ops::Range<usize>,
57 ) -> Option<crate::http::LocalFrameBytes> {
58 self.get(range)
59 .map(o3::buffer::Shared::copy_from_slice)
60 .map(crate::http::LocalFrameBytes::from_shared)
61 }
62
63 fn for_each_slice<F>(&self, range: std::ops::Range<usize>, mut f: F)
64 where
65 F: FnMut(&[u8]),
66 {
67 if let Some(slice) = self.get(range) {
68 f(slice);
69 }
70 }
71}
72
73pub struct BytesScan;
74
75impl BytesScan {
76 pub fn find_byte_from(bytes: &[u8], start: usize, needle: u8) -> Option<usize> {
77 if start >= bytes.len() {
78 return None;
79 }
80 memchr::memchr(needle, &bytes[start..]).map(|i| start + i)
81 }
82
83 pub fn find_crlf_from(bytes: &[u8], start: usize) -> Option<usize> {
84 if bytes.len() < 2 || start >= bytes.len().saturating_sub(1) {
85 return None;
86 }
87 let mut seek = start;
88 while let Some(cr_idx) = Self::find_byte_from(bytes, seek, b'\r') {
89 if cr_idx + 1 >= bytes.len() {
90 return None;
91 }
92 if bytes[cr_idx + 1] == b'\n' {
93 return Some(cr_idx);
94 }
95 seek = cr_idx + 1;
96 }
97 None
98 }
99
100 pub fn find_name_end_valid(
101 bytes: &[u8],
102 start: usize,
103 ) -> Result<Option<(usize, u8)>, crate::error::Error> {
104 match crate::simd::scan_header_name(bytes, start) {
105 crate::simd::HeaderNameOutcome::Found { pos, byte } => Ok(Some((pos, byte))),
106 crate::simd::HeaderNameOutcome::Invalid => Err(crate::error::Error::BadRequest(
107 ERR_INVALID_HEADER_NAME.into(),
108 )),
109 crate::simd::HeaderNameOutcome::None => Ok(None),
110 }
111 }
112
113 pub fn find_header_line_from(bytes: &[u8], start: usize) -> Option<HeaderLineScan> {
114 if start >= bytes.len() {
115 return None;
116 }
117
118 let mut search = start;
119 let (colon_pos, value_segment_start) = loop {
120 let rel = memchr::memchr2(b':', b'\r', &bytes[search..])?;
121 let pos = search + rel;
122 if bytes[pos] == b':' {
123 break (pos, pos + 1);
124 }
125 if pos + 1 >= bytes.len() {
126 return None;
127 }
128 if bytes[pos + 1] == b'\n' {
129 return Some(HeaderLineScan {
130 end: pos,
131 colon: None,
132 value_start: 0,
133 value_end: 0,
134 });
135 }
136 search = pos + 1;
137 };
138
139 let mut search = value_segment_start;
140 let cr = loop {
141 let rel = memchr::memchr(b'\r', &bytes[search..])?;
142 let pos = search + rel;
143 if pos + 1 >= bytes.len() {
144 return None;
145 }
146 if bytes[pos + 1] == b'\n' {
147 break pos;
148 }
149 search = pos + 1;
150 };
151
152 let segment = &bytes[value_segment_start..cr];
153 let leading = segment.iter().take_while(|&&b| is_ascii_ws(b)).count();
154 let trailing = segment[leading..]
155 .iter()
156 .rev()
157 .take_while(|&&b| is_ascii_ws(b))
158 .count();
159 Some(HeaderLineScan {
160 end: cr,
161 colon: Some(colon_pos),
162 value_start: value_segment_start + leading,
163 value_end: cr - trailing,
164 })
165 }
166}