Skip to main content

picoserve/
url_encoded.rs

1//! [`UrlEncodedString`] and related types.
2
3use core::fmt;
4
5use serde::de::Error;
6
7/// The error returned when attempting to decode a character in a [`UrlEncodedString`].
8#[derive(Debug)]
9#[cfg_attr(feature = "defmt", derive(defmt::Format))]
10pub enum UrlEncodedCharacterDecodeError {
11    /// Percent symbol is not followed by two hex digits.
12    BadlyFormattedPercentEncoding,
13    /// Percent-encoded sequence does not decode into UTF-8 byte sequence.
14    Utf8Error,
15}
16
17impl fmt::Display for UrlEncodedCharacterDecodeError {
18    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19        match self {
20            Self::BadlyFormattedPercentEncoding => {
21                write!(f, "Percent symbol is not followed by two hex digits")
22            }
23            Self::Utf8Error => write!(
24                f,
25                "Percent-encoded sequence does not decode into UTF-8 byte sequence"
26            ),
27        }
28    }
29}
30
31#[cfg(any(test, feature = "std"))]
32impl std::error::Error for UrlEncodedCharacterDecodeError {}
33
34/// A decoded character.
35pub enum UrlDecodedCharacter {
36    /// This character was present in the encoded string.
37    Literal(char),
38    /// This character was percent-encoded.
39    Encoded(char),
40}
41
42impl UrlDecodedCharacter {
43    /// Convert into a [`char`], ignoring whether the character was present in the encoded string or percent-encoded.
44    pub const fn into_char(self) -> char {
45        match self {
46            UrlDecodedCharacter::Literal(c) | UrlDecodedCharacter::Encoded(c) => c,
47        }
48    }
49}
50
51/// An iterator over the decoded [`UrlDecodedCharacter`]s of a [`UrlEncodedString`].
52pub struct UrlDecodedCharacters<'a>(core::str::Chars<'a>);
53
54impl<'a> UrlDecodedCharacters<'a> {
55    /// Views the underlying data as a substring of the original string.
56    pub fn as_str(&self) -> UrlEncodedString<'a> {
57        UrlEncodedString(self.0.as_str())
58    }
59}
60
61impl Iterator for UrlDecodedCharacters<'_> {
62    type Item = Result<UrlDecodedCharacter, UrlEncodedCharacterDecodeError>;
63
64    fn next(&mut self) -> Option<Self::Item> {
65        Some(Ok(match self.0.next()? {
66            '+' => UrlDecodedCharacter::Encoded(' '),
67            '%' => {
68                fn to_hex(c: char) -> Option<u8> {
69                    c.to_digit(16).map(|b| b as u8)
70                }
71
72                struct Ones(u8);
73
74                impl Iterator for Ones {
75                    type Item = ();
76
77                    fn next(&mut self) -> Option<Self::Item> {
78                        let b = (0b10000000 & self.0) > 0;
79
80                        self.0 <<= 1;
81
82                        b.then_some(())
83                    }
84                }
85
86                let mut first_byte = {
87                    let Some(first) = self.0.next().and_then(to_hex) else {
88                        return Some(Err(
89                            UrlEncodedCharacterDecodeError::BadlyFormattedPercentEncoding,
90                        ));
91                    };
92                    let Some(second) = self.0.next().and_then(to_hex) else {
93                        return Some(Err(
94                            UrlEncodedCharacterDecodeError::BadlyFormattedPercentEncoding,
95                        ));
96                    };
97
98                    first * 0x10 + second
99                };
100
101                let mut bits = Ones(first_byte);
102
103                let code_point = if bits.next().is_some() {
104                    let byte_count = 1 + bits.count();
105
106                    if byte_count == 1 {
107                        return Some(Err(UrlEncodedCharacterDecodeError::Utf8Error));
108                    }
109
110                    // Zero our the prefix bytes
111                    first_byte <<= byte_count;
112                    first_byte >>= byte_count;
113
114                    let mut code_point = u32::from(first_byte);
115
116                    for _ in 1..byte_count {
117                        let Some('%') = self.0.next() else {
118                            return Some(Err(UrlEncodedCharacterDecodeError::Utf8Error));
119                        };
120
121                        let next_byte = {
122                            let Some(first) = self.0.next().and_then(to_hex) else {
123                                return Some(Err(
124                                    UrlEncodedCharacterDecodeError::BadlyFormattedPercentEncoding,
125                                ));
126                            };
127                            let Some(second) = self.0.next().and_then(to_hex) else {
128                                return Some(Err(
129                                    UrlEncodedCharacterDecodeError::BadlyFormattedPercentEncoding,
130                                ));
131                            };
132
133                            first * 0x10 + second
134                        };
135
136                        if (0b11000000 & next_byte) != 0b10000000 {
137                            return Some(Err(UrlEncodedCharacterDecodeError::Utf8Error));
138                        }
139
140                        code_point <<= 6;
141                        code_point += u32::from(0b00111111 & next_byte);
142                    }
143
144                    code_point
145                } else {
146                    first_byte.into()
147                };
148
149                let Some(c) = char::from_u32(code_point) else {
150                    return Some(Err(UrlEncodedCharacterDecodeError::Utf8Error));
151                };
152                UrlDecodedCharacter::Encoded(c)
153            }
154            c => UrlDecodedCharacter::Literal(c),
155        }))
156    }
157}
158
159const URL_ENCODED_KEY: &str = "____URL_ENCODED____";
160
161fn deserializer<'a>(
162    key: &'a str,
163    value: UrlEncodedString<'a>,
164) -> serde::de::value::MapDeserializer<
165    'a,
166    core::option::IntoIter<(&'a str, DeserializeUrlEncoded<'a>)>,
167    DeserializationError,
168> {
169    serde::de::value::MapDeserializer::new(
170        Some((URL_ENCODED_KEY, DeserializeUrlEncoded { key, value })).into_iter(),
171    )
172}
173
174#[derive(serde::Deserialize)]
175struct UrlEncodedRepresentation<'a> {
176    #[serde(rename = "____URL_ENCODED____")]
177    value: &'a str,
178}
179
180/// The error returned when attempting to decode a [`UrlEncodedString`] into a string.
181#[derive(Debug)]
182#[cfg_attr(feature = "defmt", derive(defmt::Format))]
183pub enum DecodeError {
184    /// Error during decoding a character.
185    BadUrlEncodedCharacter(UrlEncodedCharacterDecodeError),
186    /// The provided buffer does not have enough space to store the string.
187    NoSpace,
188}
189
190impl fmt::Display for DecodeError {
191    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192        match self {
193            Self::BadUrlEncodedCharacter(bad_url_encoded_character) => {
194                bad_url_encoded_character.fmt(f)
195            }
196            Self::NoSpace => write!(f, "No space to decode url-encoded string"),
197        }
198    }
199}
200
201#[cfg(any(test, feature = "std"))]
202impl std::error::Error for DecodeError {}
203
204struct NamedDecodeError<'a> {
205    key: &'a str,
206    error: DecodeError,
207}
208
209/// A url-encoded string.
210#[derive(Clone, Copy, Default, serde::Deserialize)]
211#[serde(from = "UrlEncodedRepresentation")]
212pub struct UrlEncodedString<'a>(pub &'a str);
213
214impl fmt::Debug for UrlEncodedString<'_> {
215    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216        self.0.fmt(f)
217    }
218}
219
220impl<'r> PartialEq<&'r str> for UrlEncodedString<'r> {
221    fn eq(&self, other: &&'r str) -> bool {
222        matches!(self.strip_prefix(other), Some(UrlEncodedString("")))
223    }
224}
225
226impl<'de> From<UrlEncodedRepresentation<'de>> for UrlEncodedString<'de> {
227    fn from(UrlEncodedRepresentation { value }: UrlEncodedRepresentation<'de>) -> Self {
228        Self(value)
229    }
230}
231
232impl<'a> UrlEncodedString<'a> {
233    /// Returns an iterator over the decoded [`UrlDecodedCharacter`]s of the string.
234    pub fn chars(self) -> UrlDecodedCharacters<'a> {
235        UrlDecodedCharacters(self.0.chars())
236    }
237
238    /// Try decoding the chars into a string.
239    pub fn try_into_string<const N: usize>(self) -> Result<heapless::String<N>, DecodeError> {
240        let mut str = heapless::String::new();
241
242        for c in self.chars() {
243            str.push(c.map_err(DecodeError::BadUrlEncodedCharacter)?.into_char())
244                .map_err(|()| DecodeError::NoSpace)?;
245        }
246
247        Ok(str)
248    }
249
250    #[cfg(any(test, feature = "std"))]
251    /// Try decoding the chars into a `std::string::String`.
252    pub fn try_into_std_string(
253        self,
254    ) -> Result<std::string::String, UrlEncodedCharacterDecodeError> {
255        self.chars()
256            .map(|c| c.map(UrlDecodedCharacter::into_char))
257            .collect()
258    }
259
260    /// Returns a substring with the prefix removed. A '/' in the prefix must match a literal '/' in the encoded string,
261    /// all other characters ignore whether the character is literal or percent-encoded.
262    pub fn strip_prefix(self, prefix: &str) -> Option<Self> {
263        let mut chars = self.chars();
264
265        for c in prefix.chars() {
266            if c == '/' {
267                let UrlDecodedCharacter::Literal('/') = chars.next()?.ok()? else {
268                    return None;
269                };
270            } else if c != chars.next()?.ok()?.into_char() {
271                return None;
272            }
273        }
274
275        Some(chars.as_str())
276    }
277
278    /// Returns true if the string has a length of 0.
279    pub const fn is_empty(self) -> bool {
280        self.0.is_empty()
281    }
282
283    fn with_decoded<'d, T, E: From<NamedDecodeError<'d>>, F: FnOnce(&str) -> Result<T, E>>(
284        self,
285        key: &'d str,
286        f: F,
287    ) -> Result<T, E> {
288        f(&self
289            .try_into_string::<1024>()
290            .map_err(|error| NamedDecodeError { key, error })?)
291    }
292}
293
294#[derive(Debug)]
295pub(crate) struct DeserializationError;
296
297impl fmt::Display for DeserializationError {
298    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
299        write!(f, "Deserialization Error")
300    }
301}
302
303impl serde::de::Error for DeserializationError {
304    fn custom<T: fmt::Display>(msg: T) -> Self {
305        log_warn!(
306            "DeserializationError: {}",
307            crate::logging::Display2Format(&msg)
308        );
309
310        Self
311    }
312}
313
314#[cfg(any(test, feature = "std"))]
315impl std::error::Error for DeserializationError {}
316
317impl From<NamedDecodeError<'_>> for DeserializationError {
318    fn from(NamedDecodeError { key, error }: NamedDecodeError) -> Self {
319        Self::custom(format_args!("No space to decode {key}: {error}"))
320    }
321}
322
323struct DeserializeUrlEncoded<'de> {
324    pub key: &'de str,
325    pub value: UrlEncodedString<'de>,
326}
327
328impl<'de> serde::de::IntoDeserializer<'de, DeserializationError> for DeserializeUrlEncoded<'de> {
329    type Deserializer = Self;
330
331    fn into_deserializer(self) -> Self::Deserializer {
332        self
333    }
334}
335
336macro_rules! deserialize_parse_value {
337    ($this:ident, $key_value:expr; $($deserialize:ident $visit:ident)*) => {
338        $(
339            fn $deserialize<V: serde::de::Visitor<'de>>(
340                $this,
341                visitor: V,
342            ) -> Result<V::Value, Self::Error> {
343                let (key, value) = $key_value;
344
345                value.with_decoded(key, |value| {
346                    visitor.$visit(value.parse().map_err(|err| {
347                        DeserializationError::custom(format_args!("Failed to parse {}: {}", key, err))
348                    })?)
349                })
350            }
351        )*
352    };
353}
354
355impl<'de> serde::Deserializer<'de> for DeserializeUrlEncoded<'de> {
356    type Error = DeserializationError;
357
358    fn deserialize_any<V: serde::de::Visitor<'de>>(
359        self,
360        visitor: V,
361    ) -> Result<V::Value, Self::Error> {
362        self.value.with_decoded(self.key, |v| visitor.visit_str(v))
363    }
364
365    fn deserialize_struct<V: serde::de::Visitor<'de>>(
366        self,
367        name: &'static str,
368        fields: &'static [&'static str],
369        visitor: V,
370    ) -> Result<V::Value, Self::Error> {
371        if name == "UrlEncodedString" && fields == [URL_ENCODED_KEY] {
372            deserializer(self.key, self.value).deserialize_struct(name, fields, visitor)
373        } else {
374            Err(DeserializationError::custom("paths items must be atomic"))
375        }
376    }
377
378    fn deserialize_enum<V: serde::de::Visitor<'de>>(
379        self,
380        _name: &'static str,
381        _variants: &'static [&'static str],
382        visitor: V,
383    ) -> Result<V::Value, Self::Error> {
384        self.value.with_decoded(self.key, |value| {
385            visitor.visit_enum(serde::de::value::StrDeserializer::new(value))
386        })
387    }
388
389    fn deserialize_option<V: serde::de::Visitor<'de>>(
390        self,
391        visitor: V,
392    ) -> Result<V::Value, Self::Error> {
393        visitor.visit_some(self)
394    }
395
396    deserialize_parse_value!(
397        self, (self.key, self.value);
398        deserialize_bool visit_bool
399        deserialize_f32 visit_f32 deserialize_f64 visit_f64
400        deserialize_i8 visit_i8 deserialize_i16 visit_i16 deserialize_i32 visit_i32 deserialize_i64 visit_i64
401        deserialize_u8 visit_u8 deserialize_u16 visit_u16 deserialize_u32 visit_u32 deserialize_u64 visit_u64
402    );
403
404    serde::forward_to_deserialize_any! {
405        char str string
406        bytes byte_buf unit unit_struct newtype_struct seq tuple
407        tuple_struct map identifier ignored_any
408    }
409}
410
411/// Failed to deserialize a URL-Encoded form
412#[derive(Debug)]
413#[cfg_attr(feature = "defmt", derive(defmt::Format))]
414pub struct FormDeserializationError;
415
416impl fmt::Display for FormDeserializationError {
417    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
418        write!(f, "Failed to deserialize Url Encoded Form")
419    }
420}
421
422impl serde::de::Error for FormDeserializationError {
423    fn custom<T: fmt::Display>(_msg: T) -> Self {
424        Self
425    }
426}
427
428#[cfg(any(test, feature = "std"))]
429impl std::error::Error for FormDeserializationError {}
430
431impl From<super::url_encoded::DeserializationError> for FormDeserializationError {
432    fn from(
433        super::url_encoded::DeserializationError: super::url_encoded::DeserializationError,
434    ) -> Self {
435        Self
436    }
437}
438
439struct DeserializeUrlEncodedForm<'r, T> {
440    pairs: T,
441    value: (&'r str, UrlEncodedString<'r>),
442}
443
444/// Deserialize the given URL-Encoded Form.
445pub fn deserialize_form<T: serde::de::DeserializeOwned>(
446    UrlEncodedString(form): UrlEncodedString,
447) -> Result<T, FormDeserializationError> {
448    T::deserialize(DeserializeUrlEncodedForm {
449        pairs: form.split('&').filter(|s| !s.is_empty()),
450        value: ("", UrlEncodedString("")),
451    })
452}
453
454impl<'de, T: Iterator<Item = &'de str>> serde::de::Deserializer<'de>
455    for DeserializeUrlEncodedForm<'de, T>
456{
457    type Error = FormDeserializationError;
458
459    fn deserialize_any<V: serde::de::Visitor<'de>>(
460        self,
461        visitor: V,
462    ) -> Result<V::Value, Self::Error> {
463        visitor.visit_map(self)
464    }
465
466    serde::forward_to_deserialize_any! {
467        bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
468        bytes byte_buf option unit unit_struct newtype_struct seq tuple
469        tuple_struct map struct enum identifier ignored_any
470    }
471}
472
473impl<'de, T: Iterator<Item = &'de str>> serde::de::MapAccess<'de>
474    for DeserializeUrlEncodedForm<'de, T>
475{
476    type Error = FormDeserializationError;
477
478    fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
479    where
480        K: serde::de::DeserializeSeed<'de>,
481    {
482        self.pairs
483            .next()
484            .map(|value| {
485                let (key, value) = value.split_once('=').ok_or(FormDeserializationError)?;
486
487                self.value = (key, UrlEncodedString(value));
488
489                Ok(seed.deserialize(DeserializeUrlEncoded {
490                    key,
491                    value: UrlEncodedString(key),
492                })?)
493            })
494            .transpose()
495    }
496
497    fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>
498    where
499        V: serde::de::DeserializeSeed<'de>,
500    {
501        let (name, value) = self.value;
502
503        Ok(seed.deserialize(DeserializeUrlEncoded { key: name, value })?)
504    }
505}