lib.rs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. #[macro_use] extern crate matches;
  2. pub extern crate mime;
  3. pub enum DataUrlError {
  4. NotADataUrl,
  5. NoComma,
  6. }
  7. pub struct DataUrl<'a> {
  8. mime_type: mime::Mime,
  9. base64: bool,
  10. encoded_body_plus_fragment: &'a str,
  11. }
  12. pub enum DecodeError<E> {
  13. InvalidBase64(InvalidBase64),
  14. WriteError(E),
  15. }
  16. pub struct InvalidBase64(());
  17. impl<E> From<InvalidBase64> for DecodeError<E> {
  18. fn from(e: InvalidBase64) -> Self { DecodeError::InvalidBase64(e) }
  19. }
  20. /// The URL’s fragment identifier (after `#`) encoded as in the original input.
  21. ///
  22. /// It needs to be either percent-encoded to obtain the same string as in a parsed URL,
  23. /// or percent-decoded to interpret it as text.
  24. pub struct UrlFragmentIdentifier<'a>(pub &'a str);
  25. impl<'a> DataUrl<'a> {
  26. /// <https://fetch.spec.whatwg.org/#data-url-processor>
  27. /// but starting from a string rather than a Url, to avoid extra string copies.
  28. pub fn process(input: &'a str) -> Result<Self, DataUrlError> {
  29. use DataUrlError::*;
  30. let after_colon = pretend_parse_data_url(input).ok_or(NotADataUrl)?;
  31. let (from_colon_to_comma, encoded_body_plus_fragment) =
  32. find_comma_before_fragment(after_colon).ok_or(NoComma)?;
  33. let (mime_type, base64) = parse_header(from_colon_to_comma);
  34. Ok(DataUrl { mime_type, base64, encoded_body_plus_fragment })
  35. }
  36. pub fn mime_type(&self) -> &mime::Mime {
  37. &self.mime_type
  38. }
  39. /// Streaming-decode the data URL’s body to `write_body_bytes`,
  40. /// and return the URL’s fragment identifier is returned if it has one.
  41. pub fn decode<F, E>(&self, write_body_bytes: F)
  42. -> Result<Option<UrlFragmentIdentifier<'a>>, DecodeError<E>>
  43. where F: FnMut(&[u8]) -> Result<(), E>
  44. {
  45. if self.base64 {
  46. decode_with_base64(self.encoded_body_plus_fragment, write_body_bytes)
  47. } else {
  48. decode_without_base64(self.encoded_body_plus_fragment, write_body_bytes)
  49. .map_err(DecodeError::WriteError)
  50. }
  51. }
  52. /// Return the decoded body and the URL’s fragment identifier
  53. pub fn decode_to_vec(&self)
  54. -> Result<(Vec<u8>, Option<UrlFragmentIdentifier<'a>>), InvalidBase64>
  55. {
  56. enum Impossible {}
  57. let mut body = Vec::new();
  58. let result = self.decode::<_, Impossible>(|bytes| Ok(body.extend_from_slice(bytes)));
  59. match result {
  60. Ok(url_fragment) => Ok((body, url_fragment)),
  61. Err(DecodeError::InvalidBase64(e)) => Err(e),
  62. Err(DecodeError::WriteError(e)) => match e {}
  63. }
  64. }
  65. }
  66. macro_rules! require {
  67. ($condition: expr) => {
  68. if !$condition {
  69. return None
  70. }
  71. }
  72. }
  73. /// Similar to <https://url.spec.whatwg.org/#concept-basic-url-parser>
  74. /// followed by <https://url.spec.whatwg.org/#concept-url-serializer>
  75. ///
  76. /// * `None`: not a data URL.
  77. ///
  78. /// * `Some(s)`: sort of the result of serialization, except:
  79. ///
  80. /// - `data:` prefix removed
  81. /// - The fragment is included
  82. /// - Other components are **not** UTF-8 percent-encoded
  83. /// - ASCII tabs and newlines in the middle are **not** removed
  84. fn pretend_parse_data_url(input: &str) -> Option<&str> {
  85. // Trim C0 control or space
  86. let left_trimmed = input.trim_left_matches(|ch| ch <= ' ');
  87. let mut bytes = left_trimmed.bytes();
  88. {
  89. // Ignore ASCII tabs or newlines
  90. let mut iter = bytes.by_ref().filter(|&byte| !matches!(byte, b'\t' | b'\n' | b'\r'));
  91. require!(iter.next()?.to_ascii_lowercase() == b'd');
  92. require!(iter.next()?.to_ascii_lowercase() == b'a');
  93. require!(iter.next()?.to_ascii_lowercase() == b't');
  94. require!(iter.next()?.to_ascii_lowercase() == b'a');
  95. require!(iter.next()? == b':');
  96. }
  97. let bytes_consumed = left_trimmed.len() - bytes.len();
  98. let after_colon = &left_trimmed[bytes_consumed..];
  99. // Trim C0 control or space
  100. Some(after_colon.trim_right_matches(|ch| ch <= ' '))
  101. }
  102. fn find_comma_before_fragment(after_colon: &str) -> Option<(&str, &str)> {
  103. for (i, byte) in after_colon.bytes().enumerate() {
  104. if byte == b',' {
  105. return Some((&after_colon[..i], &after_colon[i + 1..]))
  106. }
  107. if byte == b'#' {
  108. break
  109. }
  110. }
  111. None
  112. }
  113. fn parse_header(from_colon_to_comma: &str) -> (mime::Mime, bool) {
  114. let input = from_colon_to_comma.chars()
  115. .filter(|&c| !matches!(c, '\t' | '\n' | '\r')) // Removed by the URL parser
  116. .collect::<String>();
  117. let mut string;
  118. let input = input.trim_matches(' ');
  119. let (mut input, base64) = match without_base64_suffix(input) {
  120. Some(s) => (s, true),
  121. None => (input, false),
  122. };
  123. // FIXME: percent-encode
  124. if input.starts_with(';') {
  125. string = String::from("text/plain");
  126. string.push_str(input);
  127. input = &*string;
  128. }
  129. // FIXME: does Mime::from_str match the MIME Sniffing Standard’s parsing algorithm?
  130. // <https://mimesniff.spec.whatwg.org/#parse-a-mime-type>
  131. let mime_type = input.parse()
  132. .unwrap_or_else(|_| "text/plain;charset=US-ASCII".parse().unwrap());
  133. (mime_type, base64)
  134. }
  135. /// None: no base64 suffix
  136. fn without_base64_suffix(s: &str) -> Option<&str> {
  137. remove_suffix(
  138. remove_suffix(s, "base64", str::eq_ignore_ascii_case)?
  139. .trim_right_matches(' '),
  140. ";", str::eq
  141. )
  142. }
  143. fn remove_suffix<'a, Eq>(haystack: &'a str, needle: &str, eq: Eq) -> Option<&'a str>
  144. where Eq: Fn(&str, &str) -> bool
  145. {
  146. let start_index = haystack.len().checked_sub(needle.len())?;
  147. let (before, after) = haystack.split_at(start_index);
  148. if eq(after, needle) {
  149. Some(before)
  150. } else {
  151. None
  152. }
  153. }
  154. /// This is <https://url.spec.whatwg.org/#string-percent-decode> while also:
  155. ///
  156. /// * Ignoring ASCII tab or newlines
  157. /// * Stopping at the first '#' (which indicates the start of the fragment)
  158. ///
  159. /// Anything that would have been UTF-8 percent-encoded by the URL parser
  160. /// would be percent-decoded here.
  161. /// We skip that round-trip and pass it through unchanged.
  162. fn decode_without_base64<F, E>(encoded_body_plus_fragment: &str, mut write_bytes: F)
  163. -> Result<Option<UrlFragmentIdentifier>, E>
  164. where F: FnMut(&[u8]) -> Result<(), E>
  165. {
  166. let bytes = encoded_body_plus_fragment.as_bytes();
  167. let mut slice_start = 0;
  168. for (i, &byte) in bytes.iter().enumerate() {
  169. // We only need to look for 5 different "special" byte values.
  170. // For everything else we make slices as large as possible, borrowing the input,
  171. // in order to make fewer write_all() calls.
  172. if matches!(byte, b'%' | b'#' | b'\t' | b'\n' | b'\r') {
  173. // Write everything (if anything) "non-special" we’ve accumulated
  174. // before this special byte
  175. if i > slice_start {
  176. write_bytes(&bytes[slice_start..i])?;
  177. }
  178. // Then deal with the special byte.
  179. match byte {
  180. b'%' => {
  181. let l = bytes.get(i + 2).and_then(|&b| (b as char).to_digit(16));
  182. let h = bytes.get(i + 1).and_then(|&b| (b as char).to_digit(16));
  183. if let (Some(h), Some(l)) = (h, l) {
  184. // '%' followed by two ASCII hex digits
  185. let one_byte = h as u8 * 0x10 + l as u8;
  186. write_bytes(&[one_byte])?;
  187. slice_start = i + 3;
  188. } else {
  189. // Do nothing. Leave slice_start unchanged.
  190. // The % sign will be part of the next slice.
  191. }
  192. }
  193. b'#' => {
  194. let fragment_start = i + 1;
  195. let fragment = &encoded_body_plus_fragment[fragment_start..];
  196. return Ok(Some(UrlFragmentIdentifier(fragment)))
  197. }
  198. // Ignore over '\t' | '\n' | '\r'
  199. _ => slice_start = i + 1
  200. }
  201. }
  202. }
  203. write_bytes(&bytes[slice_start..])?;
  204. Ok(None)
  205. }
  206. /// `decode_without_base64()` composed with
  207. /// <https://infra.spec.whatwg.org/#isomorphic-decode> composed with
  208. /// <https://infra.spec.whatwg.org/#forgiving-base64-decode>.
  209. fn decode_with_base64<F, E>(encoded_body_plus_fragment: &str, mut write_bytes: F)
  210. -> Result<Option<UrlFragmentIdentifier>, DecodeError<E>>
  211. where F: FnMut(&[u8]) -> Result<(), E>
  212. {
  213. let mut bit_buffer: u32 = 0;
  214. let mut buffer_bit_length: u8 = 0;
  215. let mut padding_symbols: u8 = 0;
  216. let fragment = decode_without_base64::<_, DecodeError<E>>(encoded_body_plus_fragment, |bytes| {
  217. for &byte in bytes.iter() {
  218. let value = BASE64_DECODE_TABLE[byte as usize];
  219. if value < 0 {
  220. // A character that’s not part of the alphabet
  221. // Remove ASCII whitespace
  222. // '\t' | '\n' | '\r' was already filtered by decode_without_base64()
  223. if byte == b' ' || byte == b'\x0C' {
  224. continue
  225. }
  226. if byte == b'=' {
  227. padding_symbols = padding_symbols.saturating_add(8);
  228. continue
  229. }
  230. Err(InvalidBase64(()))?
  231. }
  232. if padding_symbols > 0 {
  233. // Alphabet symbols after padding
  234. Err(InvalidBase64(()))?
  235. }
  236. bit_buffer <<= 6;
  237. bit_buffer |= value as u32;
  238. if buffer_bit_length < 24 {
  239. buffer_bit_length += 6;
  240. } else {
  241. // We’ve accumulated four times 6 bits, which equals three times 8 bits.
  242. let byte_buffer = [
  243. (bit_buffer >> 16) as u8,
  244. (bit_buffer >> 8) as u8,
  245. bit_buffer as u8,
  246. ];
  247. write_bytes(&byte_buffer).map_err(DecodeError::WriteError)?;
  248. buffer_bit_length = 0;
  249. // No need to reset bit_buffer,
  250. // since next time we’re only gonna read relevant bits.
  251. }
  252. }
  253. Ok(())
  254. })?;
  255. match (buffer_bit_length, padding_symbols) {
  256. (0, 0) => {
  257. // A multiple of four of alphabet symbols, and nothing else.
  258. }
  259. (12, 2) | (12, 0) => {
  260. // A multiple of four of alphabet symbols, followed by two more symbols,
  261. // optionally followed by two padding characters (which make a total multiple of four).
  262. let byte_buffer = [
  263. (bit_buffer >> 4) as u8,
  264. ];
  265. write_bytes(&byte_buffer).map_err(DecodeError::WriteError)?;
  266. }
  267. (18, 1) | (18, 0) => {
  268. // A multiple of four of alphabet symbols, followed by three more symbols,
  269. // optionally followed by one padding character (which make a total multiple of four).
  270. let byte_buffer = [
  271. (bit_buffer >> 10) as u8,
  272. (bit_buffer >> 2) as u8,
  273. ];
  274. write_bytes(&byte_buffer).map_err(DecodeError::WriteError)?;
  275. }
  276. _ => {
  277. // No other combination is acceptable
  278. Err(InvalidBase64(()))?
  279. }
  280. }
  281. Ok(fragment)
  282. }
  283. /// Generated by `make_base64_decode_table.py` based on "Table 1: The Base 64 Alphabet"
  284. /// at <https://tools.ietf.org/html/rfc4648#section-4>
  285. ///
  286. /// Array indices are the byte value of symbols.
  287. /// Array values are their positions in the base64 alphabet,
  288. /// or -1 for symbols not in the alphabet.
  289. /// The position contributes 6 bits to the decoded bytes.
  290. const BASE64_DECODE_TABLE: [i8; 256] = [
  291. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  292. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  293. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
  294. 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
  295. -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
  296. 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
  297. -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
  298. 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
  299. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  300. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  301. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  302. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  303. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  304. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  305. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  306. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  307. ];