percent_encoding.rs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. // Copyright 2013-2014 Simon Sapin.
  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. use std::ascii::AsciiExt;
  9. use std::borrow::Cow;
  10. use std::fmt::{self, Write};
  11. use std::slice;
  12. /// Represents a set of characters / bytes that should be percent-encoded.
  13. ///
  14. /// See [encode sets specification](http://url.spec.whatwg.org/#simple-encode-set).
  15. ///
  16. /// Different characters need to be encoded in different parts of an URL.
  17. /// For example, a literal `?` question mark in an URL’s path would indicate
  18. /// the start of the query string.
  19. /// A question mark meant to be part of the path therefore needs to be percent-encoded.
  20. /// In the query string however, a question mark does not have any special meaning
  21. /// and does not need to be percent-encoded.
  22. ///
  23. /// A few sets are defined in this module.
  24. /// Use the [`define_encode_set!`](../macro.define_encode_set!.html) macro to define different ones.
  25. pub trait EncodeSet: Clone {
  26. /// Called with UTF-8 bytes rather than code points.
  27. /// Should return false for all non-ASCII bytes.
  28. fn contains(&self, byte: u8) -> bool;
  29. }
  30. /// Define a new struct
  31. /// that implements the [`EncodeSet`](percent_encoding/trait.EncodeSet.html) trait,
  32. /// for use in [`percent_decode()`](percent_encoding/fn.percent_encode.html)
  33. /// and related functions.
  34. ///
  35. /// Parameters are characters to include in the set in addition to those of the base set.
  36. /// See [encode sets specification](http://url.spec.whatwg.org/#simple-encode-set).
  37. ///
  38. /// Example
  39. /// =======
  40. ///
  41. /// ```rust
  42. /// #[macro_use] extern crate url;
  43. /// use url::percent_encoding::{utf8_percent_encode, SIMPLE_ENCODE_SET};
  44. /// define_encode_set! {
  45. /// /// This encode set is used in the URL parser for query strings.
  46. /// pub QUERY_ENCODE_SET = [SIMPLE_ENCODE_SET] | {' ', '"', '#', '<', '>'}
  47. /// }
  48. /// # fn main() {
  49. /// assert_eq!(utf8_percent_encode("foo bar", QUERY_ENCODE_SET).collect::<String>(), "foo%20bar");
  50. /// # }
  51. /// ```
  52. #[macro_export]
  53. macro_rules! define_encode_set {
  54. ($(#[$attr: meta])* pub $name: ident = [$base_set: expr] | {$($ch: pat),*}) => {
  55. $(#[$attr])*
  56. #[derive(Copy, Clone)]
  57. #[allow(non_camel_case_types)]
  58. pub struct $name;
  59. impl $crate::percent_encoding::EncodeSet for $name {
  60. #[inline]
  61. fn contains(&self, byte: u8) -> bool {
  62. match byte as char {
  63. $(
  64. $ch => true,
  65. )*
  66. _ => $base_set.contains(byte)
  67. }
  68. }
  69. }
  70. }
  71. }
  72. /// This encode set is used for fragment identifier and non-relative scheme data.
  73. #[derive(Copy, Clone)]
  74. #[allow(non_camel_case_types)]
  75. pub struct SIMPLE_ENCODE_SET;
  76. impl EncodeSet for SIMPLE_ENCODE_SET {
  77. #[inline]
  78. fn contains(&self, byte: u8) -> bool {
  79. byte < 0x20 || byte > 0x7E
  80. }
  81. }
  82. define_encode_set! {
  83. /// This encode set is used in the URL parser for query strings.
  84. pub QUERY_ENCODE_SET = [SIMPLE_ENCODE_SET] | {' ', '"', '#', '<', '>'}
  85. }
  86. define_encode_set! {
  87. /// This encode set is used for path components.
  88. pub DEFAULT_ENCODE_SET = [QUERY_ENCODE_SET] | {'`', '?', '{', '}'}
  89. }
  90. define_encode_set! {
  91. /// This encode set is used for username and password.
  92. pub PATH_SEGMENT_ENCODE_SET = [DEFAULT_ENCODE_SET] | {'%'}
  93. }
  94. define_encode_set! {
  95. /// This encode set is used for username and password.
  96. pub USERINFO_ENCODE_SET = [DEFAULT_ENCODE_SET] | {
  97. '/', ':', ';', '=', '@', '[', '\\', ']', '^', '|'
  98. }
  99. }
  100. define_encode_set! {
  101. /// This encode set is used in `application/x-www-form-urlencoded` serialization.
  102. pub FORM_URLENCODED_ENCODE_SET = [SIMPLE_ENCODE_SET] | {
  103. ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '+', ',', '/', ':', ';',
  104. '<', '=', '>', '?', '@', '[', '\\', ']', '^', '`', '{', '|', '}', '~'
  105. }
  106. }
  107. /// Percent-encode the given bytes and return an iterator of `char` in the ASCII range.
  108. #[inline]
  109. pub fn percent_encode<E: EncodeSet>(input: &[u8], encode_set: E) -> PercentEncode<E> {
  110. PercentEncode {
  111. iter: input.iter(),
  112. encode_set: encode_set,
  113. state: PercentEncodeState::NextByte,
  114. }
  115. }
  116. /// Percent-encode the UTF-8 encoding of the given string
  117. /// and return an iterator of `char` in the ASCII range.
  118. #[inline]
  119. pub fn utf8_percent_encode<E: EncodeSet>(input: &str, encode_set: E) -> PercentEncode<E> {
  120. percent_encode(input.as_bytes(), encode_set)
  121. }
  122. #[derive(Clone)]
  123. pub struct PercentEncode<'a, E: EncodeSet> {
  124. iter: slice::Iter<'a, u8>,
  125. encode_set: E,
  126. state: PercentEncodeState,
  127. }
  128. #[derive(Clone)]
  129. enum PercentEncodeState {
  130. NextByte,
  131. HexHigh(u8),
  132. HexLow(u8),
  133. }
  134. impl<'a, E: EncodeSet> Iterator for PercentEncode<'a, E> {
  135. type Item = char;
  136. fn next(&mut self) -> Option<char> {
  137. // str::char::from_digit always returns lowercase.
  138. const UPPER_HEX: [char; 16] = ['0', '1', '2', '3', '4', '5', '6', '7',
  139. '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
  140. match self.state {
  141. PercentEncodeState::HexHigh(byte) => {
  142. self.state = PercentEncodeState::HexLow(byte);
  143. Some(UPPER_HEX[(byte >> 4) as usize])
  144. }
  145. PercentEncodeState::HexLow(byte) => {
  146. self.state = PercentEncodeState::NextByte;
  147. Some(UPPER_HEX[(byte & 0x0F) as usize])
  148. }
  149. PercentEncodeState::NextByte => {
  150. self.iter.next().map(|&byte| {
  151. if self.encode_set.contains(byte) {
  152. self.state = PercentEncodeState::HexHigh(byte);
  153. '%'
  154. } else {
  155. assert!(byte.is_ascii());
  156. byte as char
  157. }
  158. })
  159. }
  160. }
  161. }
  162. fn size_hint(&self) -> (usize, Option<usize>) {
  163. let (low, high) = self.iter.size_hint();
  164. (low.saturating_add(2) / 3, high)
  165. }
  166. }
  167. impl<'a, E: EncodeSet> fmt::Display for PercentEncode<'a, E> {
  168. fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
  169. for c in (*self).clone() {
  170. try!(formatter.write_char(c))
  171. }
  172. Ok(())
  173. }
  174. }
  175. /// Percent-decode the given bytes and return an iterator of bytes.
  176. #[inline]
  177. pub fn percent_decode(input: &[u8]) -> PercentDecode {
  178. PercentDecode {
  179. iter: input.iter()
  180. }
  181. }
  182. #[derive(Clone)]
  183. pub struct PercentDecode<'a> {
  184. iter: slice::Iter<'a, u8>,
  185. }
  186. impl<'a> Iterator for PercentDecode<'a> {
  187. type Item = u8;
  188. fn next(&mut self) -> Option<u8> {
  189. self.iter.next().map(|&byte| {
  190. if byte == b'%' {
  191. let after_percent_sign = self.iter.clone();
  192. let h = self.iter.next().and_then(|&b| (b as char).to_digit(16));
  193. let l = self.iter.next().and_then(|&b| (b as char).to_digit(16));
  194. if let (Some(h), Some(l)) = (h, l) {
  195. return h as u8 * 0x10 + l as u8
  196. }
  197. self.iter = after_percent_sign;
  198. }
  199. byte
  200. })
  201. }
  202. fn size_hint(&self) -> (usize, Option<usize>) {
  203. let (low, high) = self.iter.size_hint();
  204. (low, high.and_then(|high| high.checked_mul(3)))
  205. }
  206. }
  207. /// Percent-decode the given bytes, and decode the result as UTF-8.
  208. ///
  209. /// This is return `Err` when the percent-decoded bytes are not well-formed in UTF-8.
  210. pub fn utf8_percent_decode(input: &[u8]) -> Result<String, ::std::string::FromUtf8Error> {
  211. let bytes = percent_decode(input).collect::<Vec<u8>>();
  212. String::from_utf8(bytes)
  213. }
  214. /// Percent-decode the given bytes, and decode the result as UTF-8.
  215. ///
  216. /// This is “lossy”: invalid UTF-8 percent-encoded byte sequences
  217. /// will be replaced � U+FFFD, the replacement character.
  218. pub fn lossy_utf8_percent_decode(input: &[u8]) -> String {
  219. let bytes = percent_decode(input).collect::<Vec<u8>>();
  220. match String::from_utf8_lossy(&bytes) {
  221. Cow::Owned(s) => return s,
  222. Cow::Borrowed(_) => {}
  223. }
  224. unsafe {
  225. String::from_utf8_unchecked(bytes)
  226. }
  227. }