// Copyright 2013-2014 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 `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) -> Vec<(String, String)> { parse_bytes(input.as_bytes(), None, false, false).unwrap() } pub fn parse_bytes(input: &[u8], encoding_override: Option, mut use_charset: bool, mut isindex: bool) -> Option> { let mut encoding_override = encoding_override.unwrap_or(UTF_8 as EncodingRef); let mut pairs = Vec::new(); for piece in input.split(|&b| b == '&' as u8) { if piece.is_empty() { if isindex { pairs.push((Vec::new(), Vec::new())) } } 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.as_slice()) }) { 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]) -> Vec { input.iter().map(|&b| if b == '+' as u8 { ' ' as u8 } else { b }).collect() } #[inline] fn decode(input: Vec, encoding_override: EncodingRef) -> String { let bytes = percent_decode(input.as_slice()); encoding_override.decode(bytes.as_slice(), encoding::DecodeReplace).unwrap() } Some(pairs.move_iter().map( |(name, value)| (decode(name, encoding_override), decode(value, encoding_override)) ).collect()) } pub fn serialize(pairs: Vec<(String, String)>, encoding_override: Option) -> String { #[inline] fn byte_serialize(input: &str, output: &mut String, encoding_override: Option) { 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 { output.push_byte(byte) }, _ => percent_encode_byte(byte, output), } } } let mut output = String::new(); 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 }