/src/rust-url/form_urlencoded/src/lib.rs
Line | Count | Source |
1 | | // Copyright 2013-2016 The rust-url developers. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
4 | | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
5 | | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
6 | | // option. This file may not be copied, modified, or distributed |
7 | | // except according to those terms. |
8 | | |
9 | | //! Parser and serializer for the [`application/x-www-form-urlencoded` syntax]( |
10 | | //! http://url.spec.whatwg.org/#application/x-www-form-urlencoded), |
11 | | //! as used by HTML forms. |
12 | | //! |
13 | | //! Converts between a string (such as an URL’s query string) |
14 | | //! and a sequence of (name, value) pairs. |
15 | | #![no_std] |
16 | | |
17 | | // For forwards compatibility |
18 | | #[cfg(feature = "std")] |
19 | | extern crate std as _; |
20 | | |
21 | | extern crate alloc; |
22 | | |
23 | | #[cfg(not(feature = "alloc"))] |
24 | | compile_error!("the `alloc` feature must currently be enabled"); |
25 | | |
26 | | use alloc::borrow::{Borrow, Cow, ToOwned}; |
27 | | use alloc::string::String; |
28 | | use core::str; |
29 | | use percent_encoding::{percent_decode, percent_encode_byte}; |
30 | | |
31 | | /// Convert a byte string in the `application/x-www-form-urlencoded` syntax |
32 | | /// into a iterator of (name, value) pairs. |
33 | | /// |
34 | | /// Use `parse(input.as_bytes())` to parse a `&str` string. |
35 | | /// |
36 | | /// The names and values are percent-decoded. For instance, `%23first=%25try%25` will be |
37 | | /// converted to `[("#first", "%try%")]`. |
38 | | #[inline] |
39 | 0 | pub fn parse(input: &[u8]) -> Parse<'_> { |
40 | 0 | Parse { input } |
41 | 0 | } |
42 | | /// The return type of `parse()`. |
43 | | #[derive(Copy, Clone)] |
44 | | pub struct Parse<'a> { |
45 | | input: &'a [u8], |
46 | | } |
47 | | |
48 | | impl<'a> Iterator for Parse<'a> { |
49 | | type Item = (Cow<'a, str>, Cow<'a, str>); |
50 | | |
51 | 0 | fn next(&mut self) -> Option<Self::Item> { |
52 | | loop { |
53 | 0 | if self.input.is_empty() { |
54 | 0 | return None; |
55 | 0 | } |
56 | 0 | let mut split2 = self.input.splitn(2, |&b| b == b'&'); |
57 | 0 | let sequence = split2.next().unwrap(); |
58 | 0 | self.input = split2.next().unwrap_or(&[][..]); |
59 | 0 | if sequence.is_empty() { |
60 | 0 | continue; |
61 | 0 | } |
62 | 0 | let mut split2 = sequence.splitn(2, |&b| b == b'='); |
63 | 0 | let name = split2.next().unwrap(); |
64 | 0 | let value = split2.next().unwrap_or(&[][..]); |
65 | 0 | return Some((decode(name), decode(value))); |
66 | | } |
67 | 0 | } |
68 | | } |
69 | | |
70 | 0 | fn decode(input: &[u8]) -> Cow<'_, str> { |
71 | 0 | let replaced = replace_plus(input); |
72 | 0 | decode_utf8_lossy(match percent_decode(&replaced).into() { |
73 | 0 | Cow::Owned(vec) => Cow::Owned(vec), |
74 | 0 | Cow::Borrowed(_) => replaced, |
75 | | }) |
76 | 0 | } |
77 | | |
78 | | /// Replace b'+' with b' ' |
79 | 0 | fn replace_plus(input: &[u8]) -> Cow<'_, [u8]> { |
80 | 0 | match input.iter().position(|&b| b == b'+') { |
81 | 0 | None => Cow::Borrowed(input), |
82 | 0 | Some(first_position) => { |
83 | 0 | let mut replaced = input.to_owned(); |
84 | 0 | replaced[first_position] = b' '; |
85 | 0 | for byte in &mut replaced[first_position + 1..] { |
86 | 0 | if *byte == b'+' { |
87 | 0 | *byte = b' '; |
88 | 0 | } |
89 | | } |
90 | 0 | Cow::Owned(replaced) |
91 | | } |
92 | | } |
93 | 0 | } |
94 | | |
95 | | impl<'a> Parse<'a> { |
96 | | /// Return a new iterator that yields pairs of `String` instead of pairs of `Cow<str>`. |
97 | 0 | pub fn into_owned(self) -> ParseIntoOwned<'a> { |
98 | 0 | ParseIntoOwned { inner: self } |
99 | 0 | } |
100 | | } |
101 | | |
102 | | /// Like `Parse`, but yields pairs of `String` instead of pairs of `Cow<str>`. |
103 | | pub struct ParseIntoOwned<'a> { |
104 | | inner: Parse<'a>, |
105 | | } |
106 | | |
107 | | impl Iterator for ParseIntoOwned<'_> { |
108 | | type Item = (String, String); |
109 | | |
110 | 0 | fn next(&mut self) -> Option<Self::Item> { |
111 | 0 | self.inner |
112 | 0 | .next() |
113 | 0 | .map(|(k, v)| (k.into_owned(), v.into_owned())) |
114 | 0 | } |
115 | | } |
116 | | |
117 | | /// The [`application/x-www-form-urlencoded` byte serializer]( |
118 | | /// https://url.spec.whatwg.org/#concept-urlencoded-byte-serializer). |
119 | | /// |
120 | | /// Return an iterator of `&str` slices. |
121 | 0 | pub fn byte_serialize(input: &[u8]) -> ByteSerialize<'_> { |
122 | 0 | ByteSerialize { bytes: input } |
123 | 0 | } |
124 | | |
125 | | /// Return value of `byte_serialize()`. |
126 | | #[derive(Debug)] |
127 | | pub struct ByteSerialize<'a> { |
128 | | bytes: &'a [u8], |
129 | | } |
130 | | |
131 | 0 | fn byte_serialized_unchanged(byte: u8) -> bool { |
132 | 0 | matches!(byte, b'*' | b'-' | b'.' | b'0' ..= b'9' | b'A' ..= b'Z' | b'_' | b'a' ..= b'z') |
133 | 0 | } |
134 | | |
135 | | impl<'a> Iterator for ByteSerialize<'a> { |
136 | | type Item = &'a str; |
137 | | |
138 | 0 | fn next(&mut self) -> Option<&'a str> { |
139 | 0 | if let Some((&first, tail)) = self.bytes.split_first() { |
140 | 0 | if !byte_serialized_unchanged(first) { |
141 | 0 | self.bytes = tail; |
142 | 0 | return Some(if first == b' ' { |
143 | 0 | "+" |
144 | | } else { |
145 | 0 | percent_encode_byte(first) |
146 | | }); |
147 | 0 | } |
148 | 0 | let position = tail.iter().position(|&b| !byte_serialized_unchanged(b)); |
149 | 0 | let (unchanged_slice, remaining) = match position { |
150 | | // 1 for first_byte + i unchanged in tail |
151 | 0 | Some(i) => self.bytes.split_at(1 + i), |
152 | 0 | None => (self.bytes, &[][..]), |
153 | | }; |
154 | 0 | self.bytes = remaining; |
155 | | // This unsafe is appropriate because we have already checked these |
156 | | // bytes in byte_serialized_unchanged, which checks for a subset |
157 | | // of UTF-8. So we know these bytes are valid UTF-8, and doing |
158 | | // another UTF-8 check would be wasteful. |
159 | 0 | Some(unsafe { str::from_utf8_unchecked(unchanged_slice) }) |
160 | | } else { |
161 | 0 | None |
162 | | } |
163 | 0 | } |
164 | | |
165 | 0 | fn size_hint(&self) -> (usize, Option<usize>) { |
166 | 0 | if self.bytes.is_empty() { |
167 | 0 | (0, Some(0)) |
168 | | } else { |
169 | 0 | (1, Some(self.bytes.len())) |
170 | | } |
171 | 0 | } |
172 | | } |
173 | | |
174 | | /// The [`application/x-www-form-urlencoded` serializer]( |
175 | | /// https://url.spec.whatwg.org/#concept-urlencoded-serializer). |
176 | | pub struct Serializer<'a, T: Target> { |
177 | | target: Option<T>, |
178 | | start_position: usize, |
179 | | encoding: EncodingOverride<'a>, |
180 | | } |
181 | | |
182 | | pub trait Target { |
183 | | fn as_mut_string(&mut self) -> &mut String; |
184 | | fn finish(self) -> Self::Finished; |
185 | | type Finished; |
186 | | } |
187 | | |
188 | | impl Target for String { |
189 | 0 | fn as_mut_string(&mut self) -> &mut String { |
190 | 0 | self |
191 | 0 | } |
192 | 0 | fn finish(self) -> Self { |
193 | 0 | self |
194 | 0 | } |
195 | | type Finished = Self; |
196 | | } |
197 | | |
198 | | impl Target for &mut String { |
199 | 0 | fn as_mut_string(&mut self) -> &mut String { |
200 | 0 | self |
201 | 0 | } |
202 | 0 | fn finish(self) -> Self { |
203 | 0 | self |
204 | 0 | } |
205 | | type Finished = Self; |
206 | | } |
207 | | |
208 | | impl<'a, T: Target> Serializer<'a, T> { |
209 | | /// Create a new `application/x-www-form-urlencoded` serializer for the given target. |
210 | | /// |
211 | | /// If the target is non-empty, |
212 | | /// its content is assumed to already be in `application/x-www-form-urlencoded` syntax. |
213 | 0 | pub fn new(target: T) -> Self { |
214 | 0 | Self::for_suffix(target, 0) |
215 | 0 | } |
216 | | |
217 | | /// Create a new `application/x-www-form-urlencoded` serializer |
218 | | /// for a suffix of the given target. |
219 | | /// |
220 | | /// If that suffix is non-empty, |
221 | | /// its content is assumed to already be in `application/x-www-form-urlencoded` syntax. |
222 | | /// If `start_position` exceeds the target's length, it is set to the target's length. |
223 | 0 | pub fn for_suffix(mut target: T, start_position: usize) -> Self { |
224 | 0 | let start_position = start_position.min(target.as_mut_string().len()); |
225 | | |
226 | 0 | Serializer { |
227 | 0 | target: Some(target), |
228 | 0 | start_position, |
229 | 0 | encoding: None, |
230 | 0 | } |
231 | 0 | } Unexecuted instantiation: <form_urlencoded::Serializer<url::UrlQuery>>::for_suffix Unexecuted instantiation: <form_urlencoded::Serializer<_>>::for_suffix |
232 | | |
233 | | /// Remove any existing name/value pair. |
234 | | /// |
235 | | /// Panics if called after `.finish()`. |
236 | 0 | pub fn clear(&mut self) -> &mut Self { |
237 | 0 | string(&mut self.target).truncate(self.start_position); |
238 | 0 | self |
239 | 0 | } |
240 | | |
241 | | /// Set the character encoding to be used for names and values before percent-encoding. |
242 | 0 | pub fn encoding_override(&mut self, new: EncodingOverride<'a>) -> &mut Self { |
243 | 0 | self.encoding = new; |
244 | 0 | self |
245 | 0 | } |
246 | | |
247 | | /// Serialize and append a name/value pair. |
248 | | /// |
249 | | /// Panics if called after `.finish()`. |
250 | 0 | pub fn append_pair(&mut self, name: &str, value: &str) -> &mut Self { |
251 | 0 | append_pair( |
252 | 0 | string(&mut self.target), |
253 | 0 | self.start_position, |
254 | 0 | self.encoding, |
255 | 0 | name, |
256 | 0 | value, |
257 | | ); |
258 | 0 | self |
259 | 0 | } |
260 | | |
261 | | /// Serialize and append a name of parameter without any value. |
262 | | /// |
263 | | /// Panics if called after `.finish()`. |
264 | 0 | pub fn append_key_only(&mut self, name: &str) -> &mut Self { |
265 | 0 | append_key_only( |
266 | 0 | string(&mut self.target), |
267 | 0 | self.start_position, |
268 | 0 | self.encoding, |
269 | 0 | name, |
270 | | ); |
271 | 0 | self |
272 | 0 | } |
273 | | |
274 | | /// Serialize and append a number of name/value pairs. |
275 | | /// |
276 | | /// This simply calls `append_pair` repeatedly. |
277 | | /// This can be more convenient, so the user doesn’t need to introduce a block |
278 | | /// to limit the scope of `Serializer`’s borrow of its string. |
279 | | /// |
280 | | /// Panics if called after `.finish()`. |
281 | 0 | pub fn extend_pairs<I, K, V>(&mut self, iter: I) -> &mut Self |
282 | 0 | where |
283 | 0 | I: IntoIterator, |
284 | 0 | I::Item: Borrow<(K, V)>, |
285 | 0 | K: AsRef<str>, |
286 | 0 | V: AsRef<str>, |
287 | | { |
288 | | { |
289 | 0 | let string = string(&mut self.target); |
290 | 0 | for pair in iter { |
291 | 0 | let (k, v) = pair.borrow(); |
292 | 0 | append_pair( |
293 | 0 | string, |
294 | 0 | self.start_position, |
295 | 0 | self.encoding, |
296 | 0 | k.as_ref(), |
297 | 0 | v.as_ref(), |
298 | 0 | ); |
299 | 0 | } |
300 | | } |
301 | 0 | self |
302 | 0 | } |
303 | | |
304 | | /// Serialize and append a number of names without values. |
305 | | /// |
306 | | /// This simply calls `append_key_only` repeatedly. |
307 | | /// This can be more convenient, so the user doesn’t need to introduce a block |
308 | | /// to limit the scope of `Serializer`’s borrow of its string. |
309 | | /// |
310 | | /// Panics if called after `.finish()`. |
311 | 0 | pub fn extend_keys_only<I, K>(&mut self, iter: I) -> &mut Self |
312 | 0 | where |
313 | 0 | I: IntoIterator, |
314 | 0 | I::Item: Borrow<K>, |
315 | 0 | K: AsRef<str>, |
316 | | { |
317 | | { |
318 | 0 | let string = string(&mut self.target); |
319 | 0 | for key in iter { |
320 | 0 | let k = key.borrow().as_ref(); |
321 | 0 | append_key_only(string, self.start_position, self.encoding, k); |
322 | 0 | } |
323 | | } |
324 | 0 | self |
325 | 0 | } |
326 | | |
327 | | /// If this serializer was constructed with a string, take and return that string. |
328 | | /// |
329 | | /// ```rust |
330 | | /// use form_urlencoded; |
331 | | /// let encoded: String = form_urlencoded::Serializer::new(String::new()) |
332 | | /// .append_pair("foo", "bar & baz") |
333 | | /// .append_pair("saison", "Été+hiver") |
334 | | /// .finish(); |
335 | | /// assert_eq!(encoded, "foo=bar+%26+baz&saison=%C3%89t%C3%A9%2Bhiver"); |
336 | | /// ``` |
337 | | /// |
338 | | /// Panics if called more than once. |
339 | 0 | pub fn finish(&mut self) -> T::Finished { |
340 | 0 | self.target |
341 | 0 | .take() |
342 | 0 | .expect("url::form_urlencoded::Serializer double finish") |
343 | 0 | .finish() |
344 | 0 | } |
345 | | } |
346 | | |
347 | 0 | fn append_separator_if_needed(string: &mut String, start_position: usize) { |
348 | 0 | if string.len() > start_position { |
349 | 0 | string.push('&') |
350 | 0 | } |
351 | 0 | } |
352 | | |
353 | 0 | fn string<T: Target>(target: &mut Option<T>) -> &mut String { |
354 | 0 | target |
355 | 0 | .as_mut() |
356 | 0 | .expect("url::form_urlencoded::Serializer finished") |
357 | 0 | .as_mut_string() |
358 | 0 | } |
359 | | |
360 | 0 | fn append_pair( |
361 | 0 | string: &mut String, |
362 | 0 | start_position: usize, |
363 | 0 | encoding: EncodingOverride<'_>, |
364 | 0 | name: &str, |
365 | 0 | value: &str, |
366 | 0 | ) { |
367 | 0 | append_separator_if_needed(string, start_position); |
368 | 0 | append_encoded(name, string, encoding); |
369 | 0 | string.push('='); |
370 | 0 | append_encoded(value, string, encoding); |
371 | 0 | } |
372 | | |
373 | 0 | fn append_key_only( |
374 | 0 | string: &mut String, |
375 | 0 | start_position: usize, |
376 | 0 | encoding: EncodingOverride, |
377 | 0 | name: &str, |
378 | 0 | ) { |
379 | 0 | append_separator_if_needed(string, start_position); |
380 | 0 | append_encoded(name, string, encoding); |
381 | 0 | } |
382 | | |
383 | 0 | fn append_encoded(s: &str, string: &mut String, encoding: EncodingOverride<'_>) { |
384 | 0 | string.extend(byte_serialize(&encode(encoding, s))) |
385 | 0 | } |
386 | | |
387 | 0 | pub(crate) fn encode<'a>(encoding_override: EncodingOverride<'_>, input: &'a str) -> Cow<'a, [u8]> { |
388 | 0 | if let Some(o) = encoding_override { |
389 | 0 | return o(input); |
390 | 0 | } |
391 | 0 | input.as_bytes().into() |
392 | 0 | } |
393 | | |
394 | | // std::ptr::addr_eq was stabilized in rust 1.76. Once we upgrade |
395 | | // the MSRV we can remove this lint override. |
396 | | #[allow(ambiguous_wide_pointer_comparisons)] |
397 | 0 | pub(crate) fn decode_utf8_lossy(input: Cow<'_, [u8]>) -> Cow<'_, str> { |
398 | | // Note: This function is duplicated in `percent_encoding/lib.rs`. |
399 | 0 | match input { |
400 | 0 | Cow::Borrowed(bytes) => String::from_utf8_lossy(bytes), |
401 | 0 | Cow::Owned(bytes) => { |
402 | 0 | match String::from_utf8_lossy(&bytes) { |
403 | 0 | Cow::Borrowed(utf8) => { |
404 | | // If from_utf8_lossy returns a Cow::Borrowed, then we can |
405 | | // be sure our original bytes were valid UTF-8. This is because |
406 | | // if the bytes were invalid UTF-8 from_utf8_lossy would have |
407 | | // to allocate a new owned string to back the Cow so it could |
408 | | // replace invalid bytes with a placeholder. |
409 | | |
410 | | // First we do a debug_assert to confirm our description above. |
411 | 0 | let raw_utf8: *const [u8] = utf8.as_bytes(); |
412 | 0 | debug_assert!(core::ptr::eq(raw_utf8, &*bytes)); |
413 | | |
414 | | // Given we know the original input bytes are valid UTF-8, |
415 | | // and we have ownership of those bytes, we re-use them and |
416 | | // return a Cow::Owned here. |
417 | 0 | Cow::Owned(unsafe { String::from_utf8_unchecked(bytes) }) |
418 | | } |
419 | 0 | Cow::Owned(s) => Cow::Owned(s), |
420 | | } |
421 | | } |
422 | | } |
423 | 0 | } |
424 | | |
425 | | pub type EncodingOverride<'a> = Option<&'a dyn Fn(&str) -> Cow<'_, [u8]>>; |