1use lazy_static::lazy_static;
9use regex::Regex;
10use rustc_hash::FxHashMap;
11use serde_json::{Value, from_str};
12use std::borrow::Cow;
13use std::convert::Infallible;
14
15lazy_static! {
16 static ref PARENTHESES_RE: Regex = Regex::new(r"(^\[.*\]$|^\{.*\}$)").unwrap();
17}
18
19#[inline]
26fn url_decode_optimized(input: &[u8]) -> Cow<'_, str> {
27 let has_encoded = input.iter().any(|&b| b == b'+' || b == b'%');
28
29 if !has_encoded {
30 return match std::str::from_utf8(input) {
31 Ok(s) => Cow::Borrowed(s),
32 Err(_) => Cow::Owned(String::from_utf8_lossy(input).into_owned()),
33 };
34 }
35
36 let mut result = Vec::with_capacity(input.len());
37 let mut i = 0;
38
39 while i < input.len() {
40 match input[i] {
41 b'+' => {
42 result.push(b' ');
43 i += 1;
44 }
45 b'%' if i + 2 < input.len() => {
46 if let (Some(hi), Some(lo)) = (
47 char::from(input[i + 1]).to_digit(16),
48 char::from(input[i + 2]).to_digit(16),
49 ) {
50 result.push((hi * 16 + lo) as u8);
51 i += 3;
52 } else {
53 result.push(input[i]);
54 i += 1;
55 }
56 }
57 b => {
58 result.push(b);
59 i += 1;
60 }
61 }
62 }
63
64 Cow::Owned(String::from_utf8_lossy(&result).into_owned())
65}
66
67#[inline]
87pub fn parse_query_string(qs: &[u8], separator: char) -> Vec<(String, String)> {
88 if qs.is_empty() {
89 return Vec::new();
90 }
91
92 let separator_byte = separator as u8;
93 let mut result = Vec::with_capacity(8);
94
95 let mut start = 0;
96 let mut i = 0;
97
98 while i <= qs.len() {
99 if i == qs.len() || qs[i] == separator_byte {
100 if i > start {
101 let pair = &qs[start..i];
102
103 if let Some(eq_pos) = pair.iter().position(|&b| b == b'=') {
104 let key = url_decode_optimized(&pair[..eq_pos]);
105 let value = url_decode_optimized(&pair[eq_pos + 1..]);
106 result.push((key.into_owned(), value.into_owned()));
107 } else {
108 let key = url_decode_optimized(pair);
109 result.push((key.into_owned(), String::new()));
110 }
111 }
112
113 start = i + 1;
114 }
115
116 i += 1;
117 }
118
119 result
120}
121
122#[inline]
131fn decode_value(raw: &str, parse_numbers: bool) -> Value {
132 if PARENTHESES_RE.is_match(raw) {
133 let result: Value = match from_str(raw) {
134 Ok(value) => value,
135 Err(_) => match from_str(raw.replace('\'', "\"").as_str()) {
136 Ok(normalized) => normalized,
137 Err(_) => Value::Null,
138 },
139 };
140 return result;
141 }
142
143 let normalized = if raw.as_bytes().contains(&b'"') {
144 Cow::Owned(raw.replace('"', ""))
145 } else {
146 Cow::Borrowed(raw)
147 };
148
149 let json_boolean = parse_boolean(&normalized);
150 let json_null = Ok::<_, Infallible>(normalized.as_ref() == "null");
151
152 if parse_numbers {
153 let json_integer = normalized.as_ref().parse::<i64>();
154 let json_float = normalized.as_ref().parse::<f64>();
155 return match (json_integer, json_float, json_boolean, json_null) {
156 (Ok(json_integer), _, _, _) => Value::from(json_integer),
157 (_, Ok(json_float), _, _) => Value::from(json_float),
158 (_, _, Ok(json_boolean), _) => Value::from(json_boolean),
159 (_, _, _, Ok(true)) => Value::Null,
160 _ => Value::from(normalized.as_ref()),
161 };
162 }
163
164 match (json_boolean, json_null) {
165 (Ok(json_boolean), _) => Value::from(json_boolean),
166 (_, Ok(true)) => Value::Null,
167 _ => Value::from(normalized.as_ref()),
168 }
169}
170
171#[inline]
180fn parse_boolean(s: &str) -> Result<bool, ()> {
181 if s.eq_ignore_ascii_case("true") || s == "1" {
182 Ok(true)
183 } else if s.eq_ignore_ascii_case("false") || s == "0" {
184 Ok(false)
185 } else {
186 Err(())
187 }
188}
189
190#[inline]
198pub fn parse_query_pairs_to_json(pairs: &[(String, String)], parse_numbers: bool) -> Value {
199 let mut array_map: FxHashMap<String, Vec<Value>> = FxHashMap::default();
200
201 for (key, value) in pairs {
202 match array_map.get_mut(key) {
203 Some(entry) => {
204 entry.push(decode_value(value, parse_numbers));
205 }
206 None => {
207 array_map.insert(key.clone(), vec![decode_value(value, parse_numbers)]);
208 }
209 }
210 }
211
212 array_map
213 .iter()
214 .map(|(key, value)| {
215 if value.len() == 1 {
216 (key, value[0].to_owned())
217 } else {
218 (key, Value::Array(value.to_owned()))
219 }
220 })
221 .collect::<Value>()
222}
223
224#[inline]
241pub fn parse_query_string_to_json(qs: &[u8], parse_numbers: bool) -> Value {
242 let pairs = parse_query_string(qs, '&');
243 parse_query_pairs_to_json(&pairs, parse_numbers)
244}
245
246#[cfg(test)]
247mod tests {
248 use super::*;
249 use serde_json::{json, to_string};
250
251 fn eq_str(value: Value, string: &str) {
252 assert_eq!(&to_string(&value).unwrap_or_default(), string)
253 }
254
255 #[test]
256 fn test_ampersand_separator() {
257 assert_eq!(
258 parse_query_string(b"key=1&key=2&anotherKey=a&yetAnother=z", '&'),
259 vec![
260 (String::from("key"), String::from("1")),
261 (String::from("key"), String::from("2")),
262 (String::from("anotherKey"), String::from("a")),
263 (String::from("yetAnother"), String::from("z")),
264 ]
265 );
266 }
267
268 #[test]
269 fn test_handles_url_encoded_ampersand() {
270 assert_eq!(
271 parse_query_string(b"first=%26%40A.ac&second=aaa", '&'),
272 vec![
273 (String::from("first"), String::from("&@A.ac")),
274 (String::from("second"), String::from("aaa")),
275 ]
276 );
277 }
278
279 #[test]
280 fn parse_query_string_to_json_parses_simple_string() {
281 eq_str(parse_query_string_to_json(b"0=foo", true), r#"{"0":"foo"}"#);
282 }
283
284 #[test]
285 fn parse_query_string_to_json_parses_numbers() {
286 assert_eq!(parse_query_string_to_json(b"a=1", true), json!({"a": 1}));
287 assert_eq!(parse_query_string_to_json(b"a=1.1", true), json!({"a": 1.1}));
288 }
289
290 #[test]
291 fn parse_query_string_to_json_parses_booleans() {
292 assert_eq!(parse_query_string_to_json(b"a=true", false), json!({"a": true}));
293 assert_eq!(parse_query_string_to_json(b"a=false", false), json!({"a": false}));
294 }
295
296 #[test]
297 fn parse_query_string_to_json_parses_booleans_from_numbers() {
298 assert_eq!(parse_query_string_to_json(b"a=1", false), json!({"a": true}));
299 assert_eq!(parse_query_string_to_json(b"a=0", false), json!({"a": false}));
300 }
301
302 #[test]
303 fn parse_query_string_to_json_parses_case_insensitive_booleans() {
304 assert_eq!(parse_query_string_to_json(b"a=True", false), json!({"a": true}));
305 assert_eq!(parse_query_string_to_json(b"a=TRUE", false), json!({"a": true}));
306 assert_eq!(parse_query_string_to_json(b"a=False", false), json!({"a": false}));
307 assert_eq!(parse_query_string_to_json(b"a=FALSE", false), json!({"a": false}));
308 }
309
310 #[test]
311 fn parse_query_string_to_json_parses_multiple_values() {
312 assert_eq!(
313 parse_query_string_to_json(b"a=1&a=2&a=3", true),
314 json!({ "a": [1,2,3] })
315 );
316 }
317
318 #[test]
319 fn parse_query_string_to_json_parses_null() {
320 assert_eq!(parse_query_string_to_json(b"a=null", true), json!({ "a": null }));
321 }
322
323 #[test]
324 fn parse_query_string_to_json_parses_empty_string() {
325 assert_eq!(parse_query_string_to_json(b"a=", true), json!({ "a": "" }));
326 }
327
328 #[test]
329 fn parse_query_string_to_json_parses_empty_string_without_number_parsing() {
330 assert_eq!(parse_query_string_to_json(b"a=", false), json!({ "a": "" }));
331 }
332
333 #[test]
334 fn parse_query_string_to_json_parses_multiple_string_values() {
335 assert_eq!(
336 parse_query_string_to_json(b"q=foo&q=bar", true),
337 json!({ "q": ["foo", "bar"] })
338 );
339 }
340
341 #[test]
342 fn parse_query_string_to_json_parses_multiple_string_values_with_parse_numbers_false() {
343 assert_eq!(
344 parse_query_string_to_json(b"q=foo&q=bar", false),
345 json!({ "q": ["foo", "bar"] })
346 );
347 }
348
349 #[test]
350 fn parse_query_string_to_json_preserves_order_and_duplicates() {
351 assert_eq!(
352 parse_query_string_to_json(b"q=foo&q=bar&q=baz", true),
353 json!({ "q": ["foo", "bar", "baz"] })
354 );
355
356 assert_eq!(
357 parse_query_string_to_json(b"q=foo&q=foo&q=bar", true),
358 json!({ "q": ["foo", "foo", "bar"] })
359 );
360 }
361
362 #[test]
363 fn test_url_encoded_special_chars_in_values() {
364 let result = parse_query_string_to_json(b"email=x%40test.com&special=%26%40A.ac", false);
365 assert_eq!(
366 result,
367 json!({
368 "email": "x@test.com",
369 "special": "&@A.ac"
370 })
371 );
372 }
373
374 #[test]
375 fn test_url_encoded_space() {
376 let result = parse_query_string_to_json(b"name=hello%20world", false);
377 assert_eq!(result, json!({ "name": "hello world" }));
378 }
379
380 #[test]
381 fn test_url_encoded_complex_chars() {
382 let result = parse_query_string_to_json(b"name=test%26value%3D123", false);
383 assert_eq!(result, json!({ "name": "test&value=123" }));
384 }
385
386 #[test]
387 fn test_malformed_percent_single_char() {
388 let result = parse_query_string(b"key=%", '&');
389 assert_eq!(result, vec![(String::from("key"), String::from("%"))]);
390 }
391
392 #[test]
393 fn test_malformed_percent_single_hex_only() {
394 let result = parse_query_string(b"key=%2", '&');
395 assert_eq!(result, vec![(String::from("key"), String::from("%2"))]);
396 }
397
398 #[test]
399 fn test_malformed_percent_invalid_hex_chars() {
400 let result = parse_query_string(b"key=%GG&other=value", '&');
401 assert_eq!(
402 result,
403 vec![
404 (String::from("key"), String::from("%GG")),
405 (String::from("other"), String::from("value")),
406 ]
407 );
408 }
409
410 #[test]
411 fn test_malformed_percent_mixed_invalid_hex() {
412 let result = parse_query_string(b"key=%2G&other=value", '&');
413 assert_eq!(
414 result,
415 vec![
416 (String::from("key"), String::from("%2G")),
417 (String::from("other"), String::from("value")),
418 ]
419 );
420 }
421
422 #[test]
423 fn test_percent_encoding_lowercase_hex() {
424 let result = parse_query_string(b"key=%2f&other=test", '&');
425 assert_eq!(
426 result,
427 vec![
428 (String::from("key"), String::from("/")),
429 (String::from("other"), String::from("test")),
430 ]
431 );
432 }
433
434 #[test]
435 fn test_percent_encoding_uppercase_hex() {
436 let result = parse_query_string(b"key=%2F&other=test", '&');
437 assert_eq!(
438 result,
439 vec![
440 (String::from("key"), String::from("/")),
441 (String::from("other"), String::from("test")),
442 ]
443 );
444 }
445
446 #[test]
447 fn test_percent_encoding_mixed_case_hex() {
448 let result = parse_query_string(b"key=%2f%3D%4A", '&');
449 assert_eq!(result, vec![(String::from("key"), String::from("/=J"))]);
450 }
451
452 #[test]
453 fn test_plus_as_space_in_value() {
454 let result = parse_query_string(b"message=hello+world", '&');
455 assert_eq!(result, vec![(String::from("message"), String::from("hello world"))]);
456 }
457
458 #[test]
459 fn test_plus_as_space_multiple_plus() {
460 let result = parse_query_string(b"message=a+b+c+d", '&');
461 assert_eq!(result, vec![(String::from("message"), String::from("a b c d"))]);
462 }
463
464 #[test]
465 fn test_percent_encoded_space_vs_plus() {
466 let result = parse_query_string(b"a=%20space&b=+plus", '&');
467 assert_eq!(
468 result,
469 vec![
470 (String::from("a"), String::from(" space")),
471 (String::from("b"), String::from(" plus")),
472 ]
473 );
474 }
475
476 #[test]
477 fn test_mixed_plus_and_percent_encoded_space() {
478 let result = parse_query_string(b"text=hello+%20world", '&');
479 assert_eq!(result, vec![(String::from("text"), String::from("hello world"))]);
480 }
481
482 #[test]
483 fn test_ampersand_in_value_encoded() {
484 let result = parse_query_string(b"text=foo%26bar", '&');
485 assert_eq!(result, vec![(String::from("text"), String::from("foo&bar"))]);
486 }
487
488 #[test]
489 fn test_equals_in_value_encoded() {
490 let result = parse_query_string(b"text=a%3Db", '&');
491 assert_eq!(result, vec![(String::from("text"), String::from("a=b"))]);
492 }
493
494 #[test]
495 fn test_question_mark_in_value_encoded() {
496 let result = parse_query_string(b"text=what%3F", '&');
497 assert_eq!(result, vec![(String::from("text"), String::from("what?"))]);
498 }
499
500 #[test]
501 fn test_hash_in_value_encoded() {
502 let result = parse_query_string(b"text=anchor%23top", '&');
503 assert_eq!(result, vec![(String::from("text"), String::from("anchor#top"))]);
504 }
505
506 #[test]
507 fn test_multiple_encoded_special_chars() {
508 let result = parse_query_string(b"text=%26%3D%3F%23", '&');
509 assert_eq!(result, vec![(String::from("text"), String::from("&=?#"))]);
510 }
511
512 #[test]
513 fn test_empty_query_string() {
514 let result = parse_query_string(b"", '&');
515 assert_eq!(result, vec![]);
516 }
517
518 #[test]
519 fn test_multiple_consecutive_separators() {
520 let result = parse_query_string(b"a=1&&&b=2", '&');
521 assert_eq!(
522 result,
523 vec![
524 (String::from("a"), String::from("1")),
525 (String::from("b"), String::from("2")),
526 ]
527 );
528 }
529
530 #[test]
531 fn test_key_without_value() {
532 let result = parse_query_string(b"key=", '&');
533 assert_eq!(result, vec![(String::from("key"), String::from(""))]);
534 }
535
536 #[test]
537 fn test_key_without_equals() {
538 let result = parse_query_string(b"key", '&');
539 assert_eq!(result, vec![(String::from("key"), String::from(""))]);
540 }
541
542 #[test]
543 fn test_value_without_key() {
544 let result = parse_query_string(b"=value", '&');
545 assert_eq!(result, vec![(String::from(""), String::from("value"))]);
546 }
547
548 #[test]
549 fn test_multiple_equals_in_pair() {
550 let result = parse_query_string(b"key=val=more", '&');
551 assert_eq!(result, vec![(String::from("key"), String::from("val=more"))]);
552 }
553
554 #[test]
555 fn test_separator_at_start() {
556 let result = parse_query_string(b"&key=value", '&');
557 assert_eq!(result, vec![(String::from("key"), String::from("value"))]);
558 }
559
560 #[test]
561 fn test_separator_at_end() {
562 let result = parse_query_string(b"key=value&", '&');
563 assert_eq!(result, vec![(String::from("key"), String::from("value"))]);
564 }
565
566 #[test]
567 fn test_separator_at_both_ends() {
568 let result = parse_query_string(b"&key=value&", '&');
569 assert_eq!(result, vec![(String::from("key"), String::from("value"))]);
570 }
571
572 #[test]
573 fn test_multiple_values_same_key() {
574 let result = parse_query_string(b"tag=foo&tag=bar&tag=baz", '&');
575 assert_eq!(
576 result,
577 vec![
578 (String::from("tag"), String::from("foo")),
579 (String::from("tag"), String::from("bar")),
580 (String::from("tag"), String::from("baz")),
581 ]
582 );
583 }
584
585 #[test]
586 fn test_multiple_values_mixed_keys() {
587 let result = parse_query_string(b"tag=foo&id=1&tag=bar&id=2", '&');
588 assert_eq!(
589 result,
590 vec![
591 (String::from("tag"), String::from("foo")),
592 (String::from("id"), String::from("1")),
593 (String::from("tag"), String::from("bar")),
594 (String::from("id"), String::from("2")),
595 ]
596 );
597 }
598
599 #[test]
600 fn test_json_conversion_empty_key() {
601 let result = parse_query_string_to_json(b"=value", false);
602 assert_eq!(result, json!({ "": "value" }));
603 }
604
605 #[test]
606 fn test_json_conversion_all_empty_values() {
607 let result = parse_query_string_to_json(b"a=&b=&c=", false);
608 assert_eq!(result, json!({ "a": "", "b": "", "c": "" }));
609 }
610
611 #[test]
612 fn test_json_conversion_malformed_json_object() {
613 let result = parse_query_string_to_json(b"data={invalid", false);
614 assert_eq!(result, json!({ "data": "{invalid" }));
615 }
616
617 #[test]
618 fn test_json_conversion_malformed_json_array() {
619 let result = parse_query_string_to_json(b"items=[1,2,", false);
620 assert_eq!(result, json!({ "items": "[1,2," }));
621 }
622
623 #[test]
624 fn test_json_conversion_with_quotes_in_value() {
625 let result = parse_query_string_to_json(b"text=\"hello\"", false);
626 assert_eq!(result, json!({ "text": "hello" }));
627 }
628
629 #[test]
630 fn test_json_conversion_single_quotes_in_object() {
631 let result = parse_query_string_to_json(b"obj={'key':'value'}", false);
632 let value = result.get("obj");
633 assert!(value.is_some());
634 }
635
636 #[test]
637 fn test_boolean_case_insensitive_variations() {
638 assert_eq!(parse_query_string_to_json(b"a=tRuE", false), json!({"a": true}));
639 assert_eq!(parse_query_string_to_json(b"a=FaLsE", false), json!({"a": false}));
640 assert_eq!(
641 parse_query_string_to_json(b"a=tRuE&b=FaLsE", false),
642 json!({"a": true, "b": false})
643 );
644 }
645
646 #[test]
647 fn test_boolean_with_numbers_no_parse() {
648 assert_eq!(parse_query_string_to_json(b"a=1", false), json!({"a": true}));
649 assert_eq!(parse_query_string_to_json(b"a=0", false), json!({"a": false}));
650 }
651
652 #[test]
653 fn test_number_parsing_negative() {
654 assert_eq!(parse_query_string_to_json(b"a=-123", true), json!({"a": -123}));
655 assert_eq!(parse_query_string_to_json(b"a=-1.5", true), json!({"a": -1.5}));
656 }
657
658 #[test]
659 fn test_number_parsing_zero() {
660 assert_eq!(parse_query_string_to_json(b"a=0", true), json!({"a": 0}));
661 assert_eq!(parse_query_string_to_json(b"a=0.0", true), json!({"a": 0.0}));
662 }
663
664 #[test]
665 fn test_number_parsing_scientific_notation() {
666 assert_eq!(parse_query_string_to_json(b"a=1e10", true), json!({"a": 1e10}));
667 assert_eq!(parse_query_string_to_json(b"a=1.5e-3", true), json!({"a": 1.5e-3}));
668 }
669
670 #[test]
671 fn test_array_mixed_types_with_number_parsing() {
672 assert_eq!(
673 parse_query_string_to_json(b"vals=1&vals=2.5&vals=true&vals=test", true),
674 json!({"vals": [1, 2.5, true, "test"]})
675 );
676 }
677
678 #[test]
679 fn test_array_mixed_types_without_number_parsing() {
680 assert_eq!(
681 parse_query_string_to_json(b"vals=1&vals=2.5&vals=true&vals=test", false),
682 json!({"vals": [true, "2.5", true, "test"]})
683 );
684 }
685
686 #[test]
687 fn test_utf8_chinese_characters() {
688 let result = parse_query_string("name=中文".as_bytes(), '&');
689 assert_eq!(result, vec![(String::from("name"), String::from("中文"))]);
690 }
691
692 #[test]
693 fn test_utf8_emoji() {
694 let result = parse_query_string("emoji=🚀".as_bytes(), '&');
695 assert_eq!(result, vec![(String::from("emoji"), String::from("🚀"))]);
696 }
697
698 #[test]
699 fn test_utf8_mixed_with_encoding() {
700 let result = parse_query_string("text=hello%20中文".as_bytes(), '&');
701 assert_eq!(result, vec![(String::from("text"), String::from("hello 中文"))]);
702 }
703
704 #[test]
705 fn test_custom_separator_semicolon() {
706 let result = parse_query_string(b"a=1;b=2;c=3", ';');
707 assert_eq!(
708 result,
709 vec![
710 (String::from("a"), String::from("1")),
711 (String::from("b"), String::from("2")),
712 (String::from("c"), String::from("3")),
713 ]
714 );
715 }
716
717 #[test]
718 fn test_custom_separator_comma() {
719 let result = parse_query_string(b"a=1,b=2,c=3", ',');
720 assert_eq!(
721 result,
722 vec![
723 (String::from("a"), String::from("1")),
724 (String::from("b"), String::from("2")),
725 (String::from("c"), String::from("3")),
726 ]
727 );
728 }
729
730 #[test]
731 fn test_percent_encoding_all_byte_values() {
732 let result = parse_query_string(b"space=%20&at=%40&hash=%23&dollar=%24", '&');
733 assert_eq!(
734 result,
735 vec![
736 (String::from("space"), String::from(" ")),
737 (String::from("at"), String::from("@")),
738 (String::from("hash"), String::from("#")),
739 (String::from("dollar"), String::from("$")),
740 ]
741 );
742 }
743
744 #[test]
745 fn test_high_byte_values_in_percent_encoding() {
746 let result = parse_query_string(b"high=%ff%fe%fd", '&');
747 assert_eq!(result.len(), 1);
748 assert_eq!(result[0].0, "high");
749 }
750
751 #[test]
752 fn test_very_long_query_string() {
753 let mut long_query = String::from("key=");
754 long_query.push_str(&"a".repeat(10000));
755 let result = parse_query_string(long_query.as_bytes(), '&');
756 assert_eq!(result.len(), 1);
757 assert_eq!(result[0].0, "key");
758 assert_eq!(result[0].1.len(), 10000);
759 }
760
761 #[test]
762 fn test_very_large_number_of_parameters() {
763 let mut query = String::new();
764 for i in 0..100 {
765 if i > 0 {
766 query.push('&');
767 }
768 query.push_str(&format!("param{}=value{}", i, i));
769 }
770 let result = parse_query_string(query.as_bytes(), '&');
771 assert_eq!(result.len(), 100);
772 assert_eq!(result[0].0, "param0");
773 assert_eq!(result[99].0, "param99");
774 }
775
776 #[test]
777 fn test_literal_space_in_value() {
778 let result = parse_query_string(b"name=hello world", '&');
779 assert_eq!(result, vec![(String::from("name"), String::from("hello world"))]);
780 }
781
782 #[test]
783 fn test_tab_in_value() {
784 let result = parse_query_string(b"name=hello\tworld", '&');
785 assert_eq!(result, vec![(String::from("name"), String::from("hello\tworld"))]);
786 }
787
788 #[test]
789 fn test_newline_in_value() {
790 let result = parse_query_string(b"name=hello\nworld", '&');
791 assert_eq!(result, vec![(String::from("name"), String::from("hello\nworld"))]);
792 }
793}