use super::Stepped;
use crate::content_error::from_form_error;
use crate::declaration::ContentDeclaration;
use crate::field_value::Charset;
use crate::form::{FieldSource, Form};
use crate::{ContentError, ContentErrorKind, ContentEvent};
use base_web_media_type::MediaType;
use base_web_url::{FormError, FormEvent, FormLimits, FormParser};
pub(super) struct UrlEncodedBody {
parser: FormParser,
charset: Charset,
}
impl UrlEncodedBody {
pub(super) fn new(
media_type: &MediaType,
declaration: &ContentDeclaration,
) -> Result<Self, ContentErrorKind> {
let charset = Charset::from_parameter(media_type.parameter("charset"));
if charset == Charset::Unsupported {
return Err(ContentErrorKind::UnsupportedCharset);
}
let limits = &declaration.limits;
let parser = FormParser::with_limits(FormLimits {
size_limit: limits.url_encoded_size_limit,
pair_count_limit: limits.pair_count_limit,
name_length_limit: declaration.longest_name().saturating_mul(3),
value_length_limit: limits.url_encoded_size_limit,
depth_limit: limits.depth_limit,
});
Ok(Self { parser, charset })
}
pub(super) fn minimum_buffer_size(&self) -> usize {
self.parser.minimum_buffer_size()
}
pub(super) fn parse<'a, H>(
&mut self,
form: &mut Form<H>,
input: &'a [u8],
offset: usize,
end: bool,
) -> Result<Stepped<'a>, ContentError> {
let mut consumed = 0;
loop {
let rest = &input[consumed..];
let result = if end {
self.parser.parse_end(rest)
} else {
self.parser.parse(rest)
};
let parsed = match result {
Ok(parsed) => parsed,
Err(error) => return Err(refuse(form, rest, offset + consumed, error)),
};
let start = consumed;
consumed += parsed.consumed;
match parsed.event {
FormEvent::Name(name) => {
let source = if name.has_value() {
FieldSource::Value(self.charset)
} else {
FieldSource::Absent
};
form.begin(name.as_str(), source)
.map_err(|kind| ContentError::new(kind, name.offset()))?;
}
FormEvent::Value(value) => {
let (accepted, error) = form.content(value.as_bytes());
if let Some(kind) = error {
let position = start + encoded_length(&input[start..consumed], accepted);
return Err(ContentError::new(kind, (offset + position) as u64));
}
}
FormEvent::PairEnd => {
form.end();
}
FormEvent::End => return Ok(Stepped::new(consumed, ContentEvent::End)),
FormEvent::Incomplete => {
return Ok(Stepped::new(consumed, ContentEvent::Incomplete));
}
}
}
}
pub(super) fn finish(&self, offset: usize) -> Result<(), ContentError> {
if self.parser.is_finished() {
return Ok(());
}
Err(ContentError::new(
ContentErrorKind::UnexpectedEnd,
offset as u64,
))
}
}
fn refuse<H>(form: &Form<H>, rest: &[u8], start: usize, error: FormError) -> ContentError {
let refused = from_form_error(error);
if refused.kind() != ContentErrorKind::MalformedUrlEncoded || !form.is_receiving() {
return refused;
}
let preceding = &rest[..error.offset() as usize - start];
let (allowance, limit) = form.allowance();
if decoded_length(preceding) > allowance {
let position = start + encoded_length(preceding, allowance);
return ContentError::new(limit, position as u64);
}
refused
}
fn decoded_length(encoded: &[u8]) -> usize {
let escapes = encoded.iter().filter(|&&byte| byte == b'%').count();
encoded.len() - 2 * escapes
}
fn encoded_length(encoded: &[u8], decoded: usize) -> usize {
let mut length = 0;
for _ in 0..decoded {
length += if encoded[length] == b'%' { 3 } else { 1 };
}
length
}