// Copyright 2013-2015 Simon Sapin. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Parser and serializer for the [`application/x-www-form-urlencoded` format]( //! http://url.spec.whatwg.org/#application/x-www-form-urlencoded), //! as used by HTML forms. //! //! Converts between a string (such as an URL’s query string) //! and a sequence of (name, value) pairs. use std::borrow::Borrow; use std::ascii::AsciiExt; use encoding::EncodingOverride; use percent_encoding::{percent_encode, percent_decode, FORM_URLENCODED_ENCODE_SET}; /// Convert a byte string in the `application/x-www-form-urlencoded` format /// into a vector of (name, value) pairs. /// /// Use `parse(input.as_bytes())` to parse a `&str` string. /// /// The names and values are URL-decoded. For instance, `%23first=%25try%25` will be /// converted to `[("#first", "%try%")]`. #[inline] pub fn parse(input: &[u8]) -> Vec<(String, String)> { parse_internal(input, EncodingOverride::utf8(), false).unwrap() } /// Convert a byte string in the `application/x-www-form-urlencoded` format /// into a vector of (name, value) pairs. /// /// Use `parse(input.as_bytes())` to parse a `&str` string. /// /// This function is only available if the `query_encoding` Cargo feature is enabled. /// /// Arguments: /// /// * `encoding_override`: The character encoding each name and values is decoded as /// after percent-decoding. Defaults to UTF-8. /// * `use_charset`: The *use _charset_ flag*. If in doubt, set to `false`. #[cfg(feature = "query_encoding")] #[inline] pub fn parse_with_encoding(input: &[u8], encoding_override: Option<::encoding::EncodingRef>, use_charset: bool) -> Option> { parse_internal(input, EncodingOverride::from_opt_encoding(encoding_override), use_charset) } fn parse_internal(input: &[u8], mut encoding_override: EncodingOverride, mut use_charset: bool) -> Option> { let mut pairs = Vec::new(); for piece in input.split(|&b| b == b'&') { if !piece.is_empty() { let (name, value) = match piece.iter().position(|b| *b == b'=') { Some(position) => (&piece[..position], &piece[position + 1..]), None => (piece, &[][..]) }; #[inline] fn replace_plus(input: &[u8]) -> Vec { input.iter().map(|&b| if b == b'+' { b' ' } else { b }).collect() } let name = replace_plus(name); let value = replace_plus(value); if use_charset && name == b"_charset_" { if let Some(encoding) = EncodingOverride::lookup(&value) { encoding_override = encoding; } use_charset = false; } pairs.push((name, value)); } } if !(encoding_override.is_utf8() || input.is_ascii()) { return None } Some(pairs.into_iter().map(|(name, value)| ( encoding_override.decode(&percent_decode(&name).collect::>()), encoding_override.decode(&percent_decode(&value).collect::>()), )).collect()) } /// Convert an iterator of (name, value) pairs /// into a string in the `application/x-www-form-urlencoded` format. #[inline] pub fn serialize(pairs: I) -> String where I: IntoIterator, I::Item: Borrow<(K, V)>, K: AsRef, V: AsRef { serialize_internal(pairs, EncodingOverride::utf8()) } /// Convert an iterator of (name, value) pairs /// into a string in the `application/x-www-form-urlencoded` format. /// /// This function is only available if the `query_encoding` Cargo feature is enabled. /// /// Arguments: /// /// * `encoding_override`: The character encoding each name and values is encoded as /// before percent-encoding. Defaults to UTF-8. #[cfg(feature = "query_encoding")] #[inline] pub fn serialize_with_encoding(pairs: I, encoding_override: Option<::encoding::EncodingRef>) -> String where I: IntoIterator, I::Item: Borrow<(K, V)>, K: AsRef, V: AsRef { serialize_internal(pairs, EncodingOverride::from_opt_encoding(encoding_override)) } fn serialize_internal(pairs: I, encoding_override: EncodingOverride) -> String where I: IntoIterator, I::Item: Borrow<(K, V)>, K: AsRef, V: AsRef { #[inline] fn byte_serialize(input: &str, output: &mut String, encoding_override: EncodingOverride) { for &byte in encoding_override.encode(input).iter() { if byte == b' ' { output.push_str("+") } else { output.extend(percent_encode(&[byte], FORM_URLENCODED_ENCODE_SET)) } } } let mut output = String::new(); for pair in pairs { let &(ref name, ref value) = pair.borrow(); if !output.is_empty() { output.push_str("&"); } byte_serialize(name.as_ref(), &mut output, encoding_override); output.push_str("="); byte_serialize(value.as_ref(), &mut output, encoding_override); } output }