forked from servo/rust-url
-
Notifications
You must be signed in to change notification settings - Fork 0
/
form_urlencoded.rs
111 lines (96 loc) · 3.88 KB
/
form_urlencoded.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// Copyright 2013-2014 Simon Sapin.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/// Parser and serializer for `application/x-www-form-urlencoded`
///
/// Converts between a string (such as an URL’s query string)
/// and a list of name/value pairs.
use std::str;
use encoding;
use encoding::EncodingRef;
use encoding::all::UTF_8;
use encoding::label::encoding_from_whatwg_label;
use super::{percent_encode_byte, percent_decode};
pub fn parse_str(input: &str) -> ~[(~str, ~str)] {
parse_bytes(input.as_bytes(), None, false, false).unwrap()
}
pub fn parse_bytes(input: &[u8], encoding_override: Option<EncodingRef>,
mut use_charset: bool, mut isindex: bool) -> Option<~[(~str, ~str)]> {
let mut encoding_override = encoding_override.unwrap_or(UTF_8 as EncodingRef);
let mut pairs = ~[];
for piece in input.split(|&b| b == '&' as u8) {
if piece.is_empty() {
if isindex {
pairs.push((~[], ~[]))
}
} else {
let (name, value) = match piece.position_elem(&('=' as u8)) {
Some(position) => (piece.slice_to(position), piece.slice_from(position + 1)),
None => if isindex { (&[], piece) } else { (piece, &[]) }
};
let name = replace_plus(name);
let value = replace_plus(value);
if use_charset && name.as_slice() == "_charset_".as_bytes() {
// Non-UTF8 here is ok, encoding_from_whatwg_label only matches in the ASCII range.
match encoding_from_whatwg_label(unsafe { str::raw::from_utf8(value) }) {
Some(encoding) => encoding_override = encoding,
None => (),
}
use_charset = false;
}
pairs.push((name, value));
}
isindex = false;
}
if encoding_override.name() != "utf-8" && !input.is_ascii() {
return None
}
#[inline]
fn replace_plus(input: &[u8]) -> ~[u8] {
input.iter().map(|&b| if b == '+' as u8 { ' ' as u8 } else { b }).collect()
}
#[inline]
fn decode(input: ~[u8], encoding_override: EncodingRef) -> ~str {
let bytes = percent_decode(input.as_slice());
encoding_override.decode(bytes, encoding::DecodeReplace).unwrap()
}
Some(pairs.move_iter().map(
|(name, value)| (decode(name, encoding_override), decode(value, encoding_override))
).collect())
}
pub fn serialize(pairs: ~[(~str, ~str)], encoding_override: Option<EncodingRef>) -> ~str {
#[inline]
fn byte_serialize(input: &str, output: &mut ~str,
encoding_override: Option<EncodingRef>) {
let keep_alive;
let input = match encoding_override {
None => input.as_bytes(), // "Encode" to UTF-8
Some(encoding) => {
keep_alive = encoding.encode(input, encoding::EncodeNcrEscape).unwrap();
keep_alive.as_slice()
}
};
for byte in input.iter() {
match *byte {
0x20 => output.push_str("+"),
0x2A | 0x2D | 0x2E | 0x30 .. 0x39 | 0x41 .. 0x5A | 0x5F | 0x61 .. 0x7A
=> unsafe { str::raw::push_byte(output, *byte) },
_ => percent_encode_byte(*byte, output),
}
}
}
let mut output = ~"";
for &(ref name, ref value) in pairs.iter() {
if output.len() > 0 {
output.push_str("&");
byte_serialize(name.as_slice(), &mut output, encoding_override);
output.push_str("=");
byte_serialize(value.as_slice(), &mut output, encoding_override);
}
}
output
}