1use core::fmt;
5
6use crate::error::{FormError, Limit, Location};
7use crate::limits::Limits;
8use crate::urlencoded::raw::{Budget, RawPair};
9
10pub struct UrlencodedParser<B: AsMut<[u8]> + AsRef<[u8]>> {
44 scratch: B,
45 held: usize,
47 held_at: u64,
49 release: bool,
51 seen: u64,
53 dirty: usize,
55 budget: Budget,
56 poisoned: bool,
57 ended: bool,
58}
59
60impl<B: AsMut<[u8]> + AsRef<[u8]>> fmt::Debug for UrlencodedParser<B> {
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 f.debug_struct("UrlencodedParser")
63 .field("bytes_seen", &self.seen)
64 .field("held", &self.held)
65 .field("ended", &self.ended)
66 .finish_non_exhaustive()
67 }
68}
69
70#[non_exhaustive]
73#[derive(Debug)]
74pub struct UrlencodedStep<'p> {
75 pub consumed: usize,
77 pub event: UrlencodedEvent<'p>,
79}
80
81#[non_exhaustive]
83#[derive(Clone, Copy, Debug)]
84pub enum UrlencodedEvent<'p> {
85 Pair(RawPair<'p>),
87 NeedInput,
89 End,
91}
92
93enum Found {
95 Input { start: usize, end: usize, at: u64 },
96 Scratch { len: usize, at: u64 },
97 NeedInput,
98 End,
99}
100
101impl<B: AsMut<[u8]> + AsRef<[u8]>> UrlencodedParser<B> {
102 pub fn with_buffer(limits: &Limits, scratch: B) -> Result<Self, FormError> {
109 limits.validate()?;
110 let needed = limits.urlencoded_buffer_len();
111 if scratch.as_ref().len() < needed {
112 return Err(FormError::BufferTooSmall { needed });
113 }
114 Ok(UrlencodedParser {
115 scratch,
116 held: 0,
117 held_at: 0,
118 release: false,
119 seen: 0,
120 dirty: 0,
121 budget: Budget::new(limits),
122 poisoned: false,
123 ended: false,
124 })
125 }
126
127 pub fn parse<'p>(
135 &'p mut self,
136 input: &'p [u8],
137 eof: bool,
138 ) -> Result<UrlencodedStep<'p>, FormError> {
139 if self.poisoned {
140 return Err(FormError::Poisoned);
141 }
142 if self.release {
143 self.release = false;
144 self.clear_held();
145 }
146 let (consumed, found) = match self.step(input, eof) {
147 Ok(r) => r,
148 Err(e) => {
149 self.poisoned = true;
150 self.clear_held();
151 return Err(e);
152 }
153 };
154 let event = match found {
155 Found::Input { start, end, at } => {
156 UrlencodedEvent::Pair(RawPair::split(input.get(start..end).unwrap_or(&[]), at))
157 }
158 Found::Scratch { len, at } => {
159 self.release = true;
160 let seq = self.scratch.as_ref().get(..len).unwrap_or(&[]);
161 UrlencodedEvent::Pair(RawPair::split(seq, at))
162 }
163 Found::NeedInput => UrlencodedEvent::NeedInput,
164 Found::End => UrlencodedEvent::End,
165 };
166 Ok(UrlencodedStep { consumed, event })
167 }
168
169 pub fn reset(&mut self) {
172 self.wipe_used();
173 self.held = 0;
174 self.held_at = 0;
175 self.release = false;
176 self.seen = 0;
177 self.budget.reset();
178 self.poisoned = false;
179 self.ended = false;
180 }
181
182 #[must_use]
184 pub const fn bytes_seen(&self) -> u64 {
185 self.seen
186 }
187
188 fn wipe_used(&mut self) {
189 let used = self.dirty;
190 if let Some(region) = self.scratch.as_mut().get_mut(..used) {
191 crate::wipe::wipe(region);
192 }
193 self.dirty = 0;
194 }
195
196 fn clear_held(&mut self) {
197 if let Some(held) = self.scratch.as_mut().get_mut(..self.held) {
198 crate::wipe::wipe(held);
199 }
200 self.held = 0;
201 }
202
203 fn step(&mut self, input: &[u8], eof: bool) -> Result<(usize, Found), FormError> {
204 if self.ended {
205 return Ok((0, Found::End));
206 }
207 let body = self.budget.body;
208 let mut pos = 0usize;
209 loop {
210 let rest = input.get(pos..).unwrap_or(&[]);
211 let allowed = usize::try_from(body.saturating_sub(self.seen)).unwrap_or(usize::MAX);
212 let window = rest.get(..allowed.min(rest.len())).unwrap_or(&[]);
213 let over = rest.len() > window.len();
214 let amp = crate::scan::find_any(window, 0, *b"&&&&").min(window.len());
215 let terminated = amp < window.len();
216 let piece = window.get(..amp).unwrap_or(&[]);
217 if !terminated && (!eof || over) {
218 self.check_partial(piece)?;
221 if over {
222 return Err(FormError::limit(Limit::Body, Location::at(body)));
223 }
224 self.hold(piece);
225 pos = pos.saturating_add(piece.len());
226 self.seen = self.seen.saturating_add(piece.len() as u64);
227 return Ok((pos, Found::NeedInput));
228 }
229 let start = pos;
232 let at = self.seen;
233 let seq_len = self.held.saturating_add(piece.len());
234 pos = pos.saturating_add(piece.len());
235 self.seen = self.seen.saturating_add(piece.len() as u64);
236 if terminated {
237 pos = pos.saturating_add(1);
238 self.seen = self.seen.saturating_add(1);
239 }
240 if seq_len == 0 {
241 if terminated {
242 continue;
243 }
244 self.ended = true;
245 return Ok((pos, Found::End));
246 }
247 if self.held == 0 {
248 self.budget.admit(&RawPair::split(piece, at))?;
249 return Ok((
250 pos,
251 Found::Input {
252 start,
253 end: start.saturating_add(piece.len()),
254 at,
255 },
256 ));
257 }
258 self.check_partial(piece)?;
260 self.hold(piece);
261 let at = self.held_at;
262 let len = self.held;
263 let seq = self.scratch.as_ref().get(..len).unwrap_or(&[]);
264 self.budget.admit(&RawPair::split(seq, at))?;
265 return Ok((pos, Found::Scratch { len, at }));
266 }
267 }
268
269 fn check_partial(&self, more: &[u8]) -> Result<(), FormError> {
272 let held = self.scratch.as_ref().get(..self.held).unwrap_or(&[]);
273 let at = if self.held == 0 {
274 self.seen
275 } else {
276 self.held_at
277 };
278 let (name_len, has_equals, value_len) = match held.iter().position(|&b| b == b'=') {
279 Some(e) => (
280 e,
281 true,
282 held.len()
283 .saturating_sub(e)
284 .saturating_sub(1)
285 .saturating_add(more.len()),
286 ),
287 None => match more.iter().position(|&b| b == b'=') {
288 Some(e) => (
289 held.len().saturating_add(e),
290 true,
291 more.len().saturating_sub(e).saturating_sub(1),
292 ),
293 None => (held.len().saturating_add(more.len()), false, 0),
294 },
295 };
296 self.budget
297 .admit_partial(at, name_len, has_equals, value_len)
298 }
299
300 fn hold(&mut self, bytes: &[u8]) {
303 if bytes.is_empty() {
304 return;
305 }
306 if self.held == 0 {
307 self.held_at = self.seen;
308 }
309 let start = self.held;
310 let end = start.saturating_add(bytes.len());
311 if let Some(dst) = self.scratch.as_mut().get_mut(start..end) {
312 dst.copy_from_slice(bytes);
313 self.held = end;
314 self.dirty = self.dirty.max(end);
315 }
316 }
317}
318
319impl<B: AsMut<[u8]> + AsRef<[u8]>> Drop for UrlencodedParser<B> {
320 fn drop(&mut self) {
321 self.wipe_used();
322 }
323}
324
325#[cfg(feature = "alloc")]
326impl UrlencodedParser<alloc::vec::Vec<u8>> {
327 pub fn new(limits: &Limits) -> Result<Self, FormError> {
344 limits.validate()?;
345 Self::with_buffer(limits, alloc::vec![0u8; limits.urlencoded_buffer_len()])
346 }
347}