parser.rs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750
  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::error::Error;
  10. use std::fmt::{self, Formatter};
  11. use std::str::CharRange;
  12. use super::{UrlParser, Url, SchemeData, RelativeSchemeData, Host, SchemeType};
  13. use percent_encoding::{
  14. utf8_percent_encode_to, percent_encode,
  15. SIMPLE_ENCODE_SET, DEFAULT_ENCODE_SET, USERINFO_ENCODE_SET, QUERY_ENCODE_SET
  16. };
  17. pub type ParseResult<T> = Result<T, ParseError>;
  18. macro_rules! simple_enum_error {
  19. ($($name: ident => $description: expr,)+) => {
  20. /// Errors that can occur during parsing.
  21. #[derive(PartialEq, Eq, Clone, Copy, Debug)]
  22. pub enum ParseError {
  23. $(
  24. $name,
  25. )+
  26. }
  27. impl Error for ParseError {
  28. fn description(&self) -> &str {
  29. match *self {
  30. $(
  31. ParseError::$name => $description,
  32. )+
  33. }
  34. }
  35. }
  36. }
  37. }
  38. simple_enum_error! {
  39. EmptyHost => "empty host",
  40. InvalidScheme => "invalid scheme",
  41. InvalidPort => "invalid port number",
  42. InvalidIpv6Address => "invalid IPv6 address",
  43. InvalidDomainCharacter => "invalid domain character",
  44. InvalidCharacter => "invalid character",
  45. InvalidBackslash => "invalid backslash",
  46. InvalidPercentEncoded => "invalid percent-encoded sequence",
  47. InvalidAtSymbolInUser => "invalid @-symbol in user",
  48. ExpectedTwoSlashes => "expected two slashes (//)",
  49. ExpectedInitialSlash => "expected the input to start with a slash",
  50. NonUrlCodePoint => "non URL code point",
  51. RelativeUrlWithScheme => "relative URL with scheme",
  52. RelativeUrlWithoutBase => "relative URL without a base",
  53. RelativeUrlWithNonRelativeBase => "relative URL with a non-relative base",
  54. NonAsciiDomainsNotSupportedYet => "non-ASCII domains are not supported yet",
  55. CannotSetJavascriptFragment => "cannot set fragment on javascript: URL",
  56. CannotSetPortWithFileLikeScheme => "cannot set port with file-like scheme",
  57. CannotSetUsernameWithNonRelativeScheme => "cannot set username with non-relative scheme",
  58. CannotSetPasswordWithNonRelativeScheme => "cannot set password with non-relative scheme",
  59. CannotSetHostPortWithNonRelativeScheme => "cannot set host and port with non-relative scheme",
  60. CannotSetHostWithNonRelativeScheme => "cannot set host with non-relative scheme",
  61. CannotSetPortWithNonRelativeScheme => "cannot set port with non-relative scheme",
  62. CannotSetPathWithNonRelativeScheme => "cannot set path with non-relative scheme",
  63. }
  64. impl fmt::Display for ParseError {
  65. fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
  66. self.description().fmt(fmt)
  67. }
  68. }
  69. /// This is called on non-fatal parse errors.
  70. ///
  71. /// The handler can choose to continue or abort parsing by returning Ok() or Err(), respectively.
  72. /// See the `UrlParser::error_handler` method.
  73. ///
  74. /// FIXME: make this a by-ref closure when that’s supported.
  75. pub type ErrorHandler = fn(reason: ParseError) -> ParseResult<()>;
  76. #[derive(PartialEq, Eq)]
  77. pub enum Context {
  78. UrlParser,
  79. Setter,
  80. }
  81. pub fn parse_url(input: &str, parser: &UrlParser) -> ParseResult<Url> {
  82. let input = input.trim_matches([' ', '\t', '\n', '\r', '\x0C'].as_slice());
  83. let (scheme, remaining) = match parse_scheme(input, Context::UrlParser) {
  84. Some((scheme, remaining)) => (scheme, remaining),
  85. // No-scheme state
  86. None => return match parser.base_url {
  87. Some(&Url { ref scheme, scheme_data: SchemeData::Relative(ref base),
  88. ref query, .. }) => {
  89. let scheme_type = parser.get_scheme_type(scheme.as_slice());
  90. parse_relative_url(input, scheme.clone(), scheme_type, base, query, parser)
  91. },
  92. Some(_) => Err(ParseError::RelativeUrlWithNonRelativeBase),
  93. None => Err(ParseError::RelativeUrlWithoutBase),
  94. },
  95. };
  96. let scheme_type = parser.get_scheme_type(scheme.as_slice());
  97. match scheme_type {
  98. SchemeType::FileLike => {
  99. // Relative state?
  100. match parser.base_url {
  101. Some(&Url { scheme: ref base_scheme, scheme_data: SchemeData::Relative(ref base),
  102. ref query, .. })
  103. if scheme == *base_scheme => {
  104. parse_relative_url(remaining, scheme, scheme_type, base, query, parser)
  105. },
  106. // FIXME: Should not have to use a made-up base URL.
  107. _ => parse_relative_url(remaining, scheme, scheme_type, &RelativeSchemeData {
  108. username: String::new(), password: None, host: Host::Domain(String::new()),
  109. port: None, default_port: None, path: Vec::new()
  110. }, &None, parser)
  111. }
  112. },
  113. SchemeType::Relative(..) => {
  114. match parser.base_url {
  115. Some(&Url { scheme: ref base_scheme, scheme_data: SchemeData::Relative(ref base),
  116. ref query, .. })
  117. if scheme == *base_scheme && !remaining.starts_with("//") => {
  118. try!(parser.parse_error(ParseError::RelativeUrlWithScheme));
  119. parse_relative_url(remaining, scheme, scheme_type, base, query, parser)
  120. },
  121. _ => parse_absolute_url(scheme, scheme_type, remaining, parser),
  122. }
  123. },
  124. SchemeType::NonRelative => {
  125. // Scheme data state
  126. let (scheme_data, remaining) = try!(parse_scheme_data(remaining, parser));
  127. let (query, fragment) = try!(parse_query_and_fragment(remaining, parser));
  128. Ok(Url { scheme: scheme, scheme_data: SchemeData::NonRelative(scheme_data),
  129. query: query, fragment: fragment })
  130. }
  131. }
  132. }
  133. pub fn parse_scheme<'a>(input: &'a str, context: Context) -> Option<(String, &'a str)> {
  134. if input.is_empty() || !starts_with_ascii_alpha(input) {
  135. return None
  136. }
  137. for (i, c) in input.char_indices() {
  138. match c {
  139. 'a'...'z' | 'A'...'Z' | '0'...'9' | '+' | '-' | '.' => (),
  140. ':' => return Some((
  141. input[..i].to_ascii_lowercase(),
  142. &input[i + 1..],
  143. )),
  144. _ => return None,
  145. }
  146. }
  147. // EOF before ':'
  148. match context {
  149. Context::Setter => Some((input.to_ascii_lowercase(), "")),
  150. Context::UrlParser => None
  151. }
  152. }
  153. fn parse_absolute_url<'a>(scheme: String, scheme_type: SchemeType,
  154. input: &'a str, parser: &UrlParser) -> ParseResult<Url> {
  155. // Authority first slash state
  156. let remaining = try!(skip_slashes(input, parser));
  157. // Authority state
  158. let (username, password, remaining) = try!(parse_userinfo(remaining, parser));
  159. // Host state
  160. let (host, port, default_port, remaining) = try!(parse_host(remaining, scheme_type, parser));
  161. let (path, remaining) = try!(parse_path_start(
  162. remaining, Context::UrlParser, scheme_type, parser));
  163. let scheme_data = SchemeData::Relative(RelativeSchemeData {
  164. username: username, password: password,
  165. host: host, port: port, default_port: default_port,
  166. path: path });
  167. let (query, fragment) = try!(parse_query_and_fragment(remaining, parser));
  168. Ok(Url { scheme: scheme, scheme_data: scheme_data, query: query, fragment: fragment })
  169. }
  170. fn parse_relative_url<'a>(input: &'a str, scheme: String, scheme_type: SchemeType,
  171. base: &RelativeSchemeData, base_query: &Option<String>,
  172. parser: &UrlParser)
  173. -> ParseResult<Url> {
  174. if input.is_empty() {
  175. return Ok(Url { scheme: scheme, scheme_data: SchemeData::Relative(base.clone()),
  176. query: base_query.clone(), fragment: None })
  177. }
  178. match input.char_at(0) {
  179. '/' | '\\' => {
  180. // Relative slash state
  181. if input.len() > 1 && matches!(input.char_at(1), '/' | '\\') {
  182. if input.char_at(1) == '\\' {
  183. try!(parser.parse_error(ParseError::InvalidBackslash))
  184. }
  185. if scheme_type == SchemeType::FileLike {
  186. // File host state
  187. let remaining = &input[2..];
  188. let (host, remaining) = if remaining.len() >= 2
  189. && starts_with_ascii_alpha(remaining)
  190. && matches!(remaining.char_at(1), ':' | '|')
  191. && (remaining.len() == 2
  192. || matches!(remaining.char_at(2),
  193. '/' | '\\' | '?' | '#'))
  194. {
  195. // Windows drive letter quirk
  196. (Host::Domain(String::new()), remaining)
  197. } else {
  198. try!(parse_file_host(remaining, parser))
  199. };
  200. let (path, remaining) = try!(parse_path_start(
  201. remaining, Context::UrlParser, scheme_type, parser));
  202. let scheme_data = SchemeData::Relative(RelativeSchemeData {
  203. username: String::new(), password: None,
  204. host: host, port: None, default_port: None, path: path
  205. });
  206. let (query, fragment) = try!(parse_query_and_fragment(remaining, parser));
  207. Ok(Url { scheme: scheme, scheme_data: scheme_data,
  208. query: query, fragment: fragment })
  209. } else {
  210. parse_absolute_url(scheme, scheme_type, input, parser)
  211. }
  212. } else {
  213. // Relative path state
  214. let (path, remaining) = try!(parse_path(
  215. &[], &input[1..], Context::UrlParser, scheme_type, parser));
  216. let scheme_data = SchemeData::Relative(if scheme_type == SchemeType::FileLike {
  217. RelativeSchemeData {
  218. username: String::new(), password: None, host:
  219. Host::Domain(String::new()), port: None, default_port: None, path: path
  220. }
  221. } else {
  222. RelativeSchemeData {
  223. username: base.username.clone(),
  224. password: base.password.clone(),
  225. host: base.host.clone(),
  226. port: base.port.clone(),
  227. default_port: base.default_port.clone(),
  228. path: path
  229. }
  230. });
  231. let (query, fragment) = try!(
  232. parse_query_and_fragment(remaining, parser));
  233. Ok(Url { scheme: scheme, scheme_data: scheme_data,
  234. query: query, fragment: fragment })
  235. }
  236. },
  237. '?' => {
  238. let (query, fragment) = try!(parse_query_and_fragment(input, parser));
  239. Ok(Url { scheme: scheme, scheme_data: SchemeData::Relative(base.clone()),
  240. query: query, fragment: fragment })
  241. },
  242. '#' => {
  243. let fragment = Some(try!(parse_fragment(&input[1..], parser)));
  244. Ok(Url { scheme: scheme, scheme_data: SchemeData::Relative(base.clone()),
  245. query: base_query.clone(), fragment: fragment })
  246. }
  247. _ => {
  248. let (scheme_data, remaining) = if scheme_type == SchemeType::FileLike
  249. && input.len() >= 2
  250. && starts_with_ascii_alpha(input)
  251. && matches!(input.char_at(1), ':' | '|')
  252. && (input.len() == 2
  253. || matches!(input.char_at(2), '/' | '\\' | '?' | '#'))
  254. {
  255. // Windows drive letter quirk
  256. let (path, remaining) = try!(parse_path(
  257. &[], input, Context::UrlParser, scheme_type, parser));
  258. (SchemeData::Relative(RelativeSchemeData {
  259. username: String::new(), password: None,
  260. host: Host::Domain(String::new()),
  261. port: None,
  262. default_port: None,
  263. path: path
  264. }), remaining)
  265. } else {
  266. let base_path = &base.path[..base.path.len() - 1];
  267. // Relative path state
  268. let (path, remaining) = try!(parse_path(
  269. base_path, input, Context::UrlParser, scheme_type, parser));
  270. (SchemeData::Relative(RelativeSchemeData {
  271. username: base.username.clone(),
  272. password: base.password.clone(),
  273. host: base.host.clone(),
  274. port: base.port.clone(),
  275. default_port: base.default_port.clone(),
  276. path: path
  277. }), remaining)
  278. };
  279. let (query, fragment) = try!(parse_query_and_fragment(remaining, parser));
  280. Ok(Url { scheme: scheme, scheme_data: scheme_data,
  281. query: query, fragment: fragment })
  282. }
  283. }
  284. }
  285. fn skip_slashes<'a>(input: &'a str, parser: &UrlParser) -> ParseResult<&'a str> {
  286. let first_non_slash = input.find(|&:c| !matches!(c, '/' | '\\')).unwrap_or(input.len());
  287. if &input[..first_non_slash] != "//" {
  288. try!(parser.parse_error(ParseError::ExpectedTwoSlashes));
  289. }
  290. Ok(&input[first_non_slash..])
  291. }
  292. fn parse_userinfo<'a>(input: &'a str, parser: &UrlParser)
  293. -> ParseResult<(String, Option<String>, &'a str)> {
  294. let mut last_at = None;
  295. for (i, c) in input.char_indices() {
  296. match c {
  297. '@' => {
  298. if last_at.is_some() {
  299. try!(parser.parse_error(ParseError::InvalidAtSymbolInUser))
  300. }
  301. last_at = Some(i)
  302. },
  303. '/' | '\\' | '?' | '#' => break,
  304. _ => (),
  305. }
  306. }
  307. let (input, remaining) = match last_at {
  308. Some(at) => (&input[..at], &input[at + 1..]),
  309. None => return Ok((String::new(), None, input)),
  310. };
  311. let mut username = String::new();
  312. let mut password = None;
  313. for (i, c, next_i) in input.char_ranges() {
  314. match c {
  315. ':' => {
  316. password = Some(try!(parse_password(&input[i + 1..], parser)));
  317. break
  318. },
  319. '\t' | '\n' | '\r' => try!(parser.parse_error(ParseError::InvalidCharacter)),
  320. _ => {
  321. try!(check_url_code_point(input, i, c, parser));
  322. // The spec says to use the default encode set,
  323. // but also replaces '@' by '%40' in an earlier step.
  324. utf8_percent_encode_to(&input[i..next_i],
  325. USERINFO_ENCODE_SET, &mut username);
  326. }
  327. }
  328. }
  329. Ok((username, password, remaining))
  330. }
  331. fn parse_password(input: &str, parser: &UrlParser) -> ParseResult<String> {
  332. let mut password = String::new();
  333. for (i, c, next_i) in input.char_ranges() {
  334. match c {
  335. '\t' | '\n' | '\r' => try!(parser.parse_error(ParseError::InvalidCharacter)),
  336. _ => {
  337. try!(check_url_code_point(input, i, c, parser));
  338. // The spec says to use the default encode set,
  339. // but also replaces '@' by '%40' in an earlier step.
  340. utf8_percent_encode_to(&input[i..next_i],
  341. USERINFO_ENCODE_SET, &mut password);
  342. }
  343. }
  344. }
  345. Ok(password)
  346. }
  347. pub fn parse_host<'a>(input: &'a str, scheme_type: SchemeType, parser: &UrlParser)
  348. -> ParseResult<(Host, Option<u16>, Option<u16>, &'a str)> {
  349. let (host, remaining) = try!(parse_hostname(input, parser));
  350. let (port, default_port, remaining) = if remaining.starts_with(":") {
  351. try!(parse_port(&remaining[1..], scheme_type, parser))
  352. } else {
  353. (None, scheme_type.default_port(), remaining)
  354. };
  355. Ok((host, port, default_port, remaining))
  356. }
  357. pub fn parse_hostname<'a>(input: &'a str, parser: &UrlParser)
  358. -> ParseResult<(Host, &'a str)> {
  359. let mut inside_square_brackets = false;
  360. let mut host_input = String::new();
  361. let mut end = input.len();
  362. for (i, c) in input.char_indices() {
  363. match c {
  364. ':' if !inside_square_brackets => {
  365. end = i;
  366. break
  367. },
  368. '/' | '\\' | '?' | '#' => {
  369. end = i;
  370. break
  371. },
  372. '\t' | '\n' | '\r' => try!(parser.parse_error(ParseError::InvalidCharacter)),
  373. c => {
  374. match c {
  375. '[' => inside_square_brackets = true,
  376. ']' => inside_square_brackets = false,
  377. _ => (),
  378. }
  379. host_input.push(c)
  380. }
  381. }
  382. }
  383. let host = try!(Host::parse(host_input.as_slice()));
  384. Ok((host, &input[end..]))
  385. }
  386. pub fn parse_port<'a>(input: &'a str, scheme_type: SchemeType, parser: &UrlParser)
  387. -> ParseResult<(Option<u16>, Option<u16>, &'a str)> {
  388. let mut port = 0;
  389. let mut has_any_digit = false;
  390. let mut end = input.len();
  391. for (i, c) in input.char_indices() {
  392. match c {
  393. '0'...'9' => {
  394. port = port * 10 + (c as u32 - '0' as u32);
  395. if port > ::std::u16::MAX as u32 {
  396. return Err(ParseError::InvalidPort)
  397. }
  398. has_any_digit = true;
  399. },
  400. '/' | '\\' | '?' | '#' => {
  401. end = i;
  402. break
  403. },
  404. '\t' | '\n' | '\r' => try!(parser.parse_error(ParseError::InvalidCharacter)),
  405. _ => return Err(ParseError::InvalidPort)
  406. }
  407. }
  408. let default_port = scheme_type.default_port();
  409. let mut port = Some(port as u16);
  410. if !has_any_digit || port == default_port {
  411. port = None;
  412. }
  413. return Ok((port, default_port, &input[end..]))
  414. }
  415. fn parse_file_host<'a>(input: &'a str, parser: &UrlParser) -> ParseResult<(Host, &'a str)> {
  416. let mut host_input = String::new();
  417. let mut end = input.len();
  418. for (i, c) in input.char_indices() {
  419. match c {
  420. '/' | '\\' | '?' | '#' => {
  421. end = i;
  422. break
  423. },
  424. '\t' | '\n' | '\r' => try!(parser.parse_error(ParseError::InvalidCharacter)),
  425. _ => host_input.push(c)
  426. }
  427. }
  428. let host = if host_input.is_empty() {
  429. Host::Domain(String::new())
  430. } else {
  431. try!(Host::parse(host_input.as_slice()))
  432. };
  433. Ok((host, &input[end..]))
  434. }
  435. pub fn parse_standalone_path(input: &str, parser: &UrlParser)
  436. -> ParseResult<(Vec<String>, Option<String>, Option<String>)> {
  437. if !input.starts_with("/") {
  438. if input.starts_with("\\") {
  439. try!(parser.parse_error(ParseError::InvalidBackslash));
  440. } else {
  441. return Err(ParseError::ExpectedInitialSlash)
  442. }
  443. }
  444. let (path, remaining) = try!(parse_path(
  445. &[], &input[1..], Context::UrlParser, SchemeType::Relative(0), parser));
  446. let (query, fragment) = try!(parse_query_and_fragment(remaining, parser));
  447. Ok((path, query, fragment))
  448. }
  449. pub fn parse_path_start<'a>(input: &'a str, context: Context, scheme_type: SchemeType,
  450. parser: &UrlParser)
  451. -> ParseResult<(Vec<String>, &'a str)> {
  452. let mut i = 0;
  453. // Relative path start state
  454. if !input.is_empty() {
  455. match input.char_at(0) {
  456. '/' => i = 1,
  457. '\\' => {
  458. try!(parser.parse_error(ParseError::InvalidBackslash));
  459. i = 1;
  460. },
  461. _ => ()
  462. }
  463. }
  464. parse_path(&[], &input[i..], context, scheme_type, parser)
  465. }
  466. fn parse_path<'a>(base_path: &[String], input: &'a str, context: Context,
  467. scheme_type: SchemeType, parser: &UrlParser)
  468. -> ParseResult<(Vec<String>, &'a str)> {
  469. // Relative path state
  470. let mut path = base_path.to_vec();
  471. let mut iter = input.char_ranges();
  472. let mut end;
  473. loop {
  474. let mut path_part = String::new();
  475. let mut ends_with_slash = false;
  476. end = input.len();
  477. while let Some((i, c, next_i)) = iter.next() {
  478. match c {
  479. '/' => {
  480. ends_with_slash = true;
  481. end = i;
  482. break
  483. },
  484. '\\' => {
  485. try!(parser.parse_error(ParseError::InvalidBackslash));
  486. ends_with_slash = true;
  487. end = i;
  488. break
  489. },
  490. '?' | '#' if context == Context::UrlParser => {
  491. end = i;
  492. break
  493. },
  494. '\t' | '\n' | '\r' => try!(parser.parse_error(ParseError::InvalidCharacter)),
  495. _ => {
  496. try!(check_url_code_point(input, i, c, parser));
  497. utf8_percent_encode_to(&input[i..next_i],
  498. DEFAULT_ENCODE_SET, &mut path_part);
  499. }
  500. }
  501. }
  502. match path_part.as_slice() {
  503. ".." | ".%2e" | ".%2E" | "%2e." | "%2E." |
  504. "%2e%2e" | "%2E%2e" | "%2e%2E" | "%2E%2E" => {
  505. path.pop();
  506. if !ends_with_slash {
  507. path.push(String::new());
  508. }
  509. },
  510. "." | "%2e" | "%2E" => {
  511. if !ends_with_slash {
  512. path.push(String::new());
  513. }
  514. },
  515. _ => {
  516. if scheme_type == SchemeType::FileLike
  517. && path.is_empty()
  518. && path_part.len() == 2
  519. && starts_with_ascii_alpha(path_part.as_slice())
  520. && path_part.as_slice().char_at(1) == '|' {
  521. // Windows drive letter quirk
  522. unsafe {
  523. path_part.as_mut_vec()[1] = b':'
  524. }
  525. }
  526. path.push(path_part)
  527. }
  528. }
  529. if !ends_with_slash {
  530. break
  531. }
  532. }
  533. Ok((path, &input[end..]))
  534. }
  535. fn parse_scheme_data<'a>(input: &'a str, parser: &UrlParser)
  536. -> ParseResult<(String, &'a str)> {
  537. let mut scheme_data = String::new();
  538. let mut end = input.len();
  539. for (i, c, next_i) in input.char_ranges() {
  540. match c {
  541. '?' | '#' => {
  542. end = i;
  543. break
  544. },
  545. '\t' | '\n' | '\r' => try!(parser.parse_error(ParseError::InvalidCharacter)),
  546. _ => {
  547. try!(check_url_code_point(input, i, c, parser));
  548. utf8_percent_encode_to(&input[i..next_i],
  549. SIMPLE_ENCODE_SET, &mut scheme_data);
  550. }
  551. }
  552. }
  553. Ok((scheme_data, &input[end..]))
  554. }
  555. fn parse_query_and_fragment(input: &str, parser: &UrlParser)
  556. -> ParseResult<(Option<String>, Option<String>)> {
  557. if input.is_empty() {
  558. return Ok((None, None))
  559. }
  560. match input.char_at(0) {
  561. '#' => Ok((None, Some(try!(parse_fragment(&input[1..], parser))))),
  562. '?' => {
  563. let (query, remaining) = try!(parse_query(
  564. &input[1..], Context::UrlParser, parser));
  565. let fragment = match remaining {
  566. Some(remaining) => Some(try!(parse_fragment(remaining, parser))),
  567. None => None
  568. };
  569. Ok((Some(query), fragment))
  570. },
  571. _ => panic!("Programming error. parse_query_and_fragment() should not \
  572. have been called with input \"{}\"", input)
  573. }
  574. }
  575. pub fn parse_query<'a>(input: &'a str, context: Context, parser: &UrlParser)
  576. -> ParseResult<(String, Option<&'a str>)> {
  577. let mut query = String::new();
  578. let mut remaining = None;
  579. for (i, c) in input.char_indices() {
  580. match c {
  581. '#' if context == Context::UrlParser => {
  582. remaining = Some(&input[i + 1..]);
  583. break
  584. },
  585. '\t' | '\n' | '\r' => try!(parser.parse_error(ParseError::InvalidCharacter)),
  586. _ => {
  587. try!(check_url_code_point(input, i, c, parser));
  588. query.push(c);
  589. }
  590. }
  591. }
  592. let query_bytes = parser.query_encoding_override.encode(query.as_slice());
  593. Ok((percent_encode(query_bytes.as_slice(), QUERY_ENCODE_SET), remaining))
  594. }
  595. pub fn parse_fragment<'a>(input: &'a str, parser: &UrlParser) -> ParseResult<String> {
  596. let mut fragment = String::new();
  597. for (i, c, next_i) in input.char_ranges() {
  598. match c {
  599. '\t' | '\n' | '\r' => try!(parser.parse_error(ParseError::InvalidCharacter)),
  600. _ => {
  601. try!(check_url_code_point(input, i, c, parser));
  602. utf8_percent_encode_to(&input[i..next_i],
  603. SIMPLE_ENCODE_SET, &mut fragment);
  604. }
  605. }
  606. }
  607. Ok(fragment)
  608. }
  609. #[inline]
  610. pub fn starts_with_ascii_alpha(string: &str) -> bool {
  611. matches!(string.as_bytes()[0], b'a'...b'z' | b'A'...b'Z')
  612. }
  613. #[inline]
  614. fn is_ascii_hex_digit(byte: u8) -> bool {
  615. matches!(byte, b'a'...b'f' | b'A'...b'F' | b'0'...b'9')
  616. }
  617. #[inline]
  618. fn starts_with_2_hex(input: &str) -> bool {
  619. input.len() >= 2
  620. && is_ascii_hex_digit(input.as_bytes()[0])
  621. && is_ascii_hex_digit(input.as_bytes()[1])
  622. }
  623. #[inline]
  624. fn is_url_code_point(c: char) -> bool {
  625. matches!(c,
  626. 'a'...'z' |
  627. 'A'...'Z' |
  628. '0'...'9' |
  629. '!' | '$' | '&' | '\'' | '(' | ')' | '*' | '+' | ',' | '-' |
  630. '.' | '/' | ':' | ';' | '=' | '?' | '@' | '_' | '~' |
  631. '\u{A0}'...'\u{D7FF}' | '\u{E000}'...'\u{FDCF}' | '\u{FDF0}'...'\u{FFFD}' |
  632. '\u{10000}'...'\u{1FFFD}' | '\u{20000}'...'\u{2FFFD}' |
  633. '\u{30000}'...'\u{3FFFD}' | '\u{40000}'...'\u{4FFFD}' |
  634. '\u{50000}'...'\u{5FFFD}' | '\u{60000}'...'\u{6FFFD}' |
  635. '\u{70000}'...'\u{7FFFD}' | '\u{80000}'...'\u{8FFFD}' |
  636. '\u{90000}'...'\u{9FFFD}' | '\u{A0000}'...'\u{AFFFD}' |
  637. '\u{B0000}'...'\u{BFFFD}' | '\u{C0000}'...'\u{CFFFD}' |
  638. '\u{D0000}'...'\u{DFFFD}' | '\u{E1000}'...'\u{EFFFD}' |
  639. '\u{F0000}'...'\u{FFFFD}' | '\u{100000}'...'\u{10FFFD}')
  640. }
  641. // Non URL code points:
  642. // U+0000 to U+0020 (space)
  643. // " # % < > [ \ ] ^ ` { | }
  644. // U+007F to U+009F
  645. // surrogates
  646. // U+FDD0 to U+FDEF
  647. // Last two of each plane: U+__FFFE to U+__FFFF for __ in 00 to 10 hex
  648. pub trait StrCharRanges<'a> {
  649. fn char_ranges(&self) -> CharRanges<'a>;
  650. }
  651. impl<'a> StrCharRanges<'a> for &'a str {
  652. #[inline]
  653. fn char_ranges(&self) -> CharRanges<'a> {
  654. CharRanges { slice: *self, position: 0 }
  655. }
  656. }
  657. pub struct CharRanges<'a> {
  658. slice: &'a str,
  659. position: usize,
  660. }
  661. impl<'a> Iterator for CharRanges<'a> {
  662. type Item = (usize, char, usize);
  663. #[inline]
  664. fn next(&mut self) -> Option<(usize, char, usize)> {
  665. if self.position == self.slice.len() {
  666. None
  667. } else {
  668. let position = self.position;
  669. let CharRange { ch, next } = self.slice.char_range_at(position);
  670. self.position = next;
  671. Some((position, ch, next))
  672. }
  673. }
  674. }
  675. #[inline]
  676. fn check_url_code_point(input: &str, i: usize, c: char, parser: &UrlParser)
  677. -> ParseResult<()> {
  678. if c == '%' {
  679. if !starts_with_2_hex(&input[i + 1..]) {
  680. try!(parser.parse_error(ParseError::InvalidPercentEncoded));
  681. }
  682. } else if !is_url_code_point(c) {
  683. try!(parser.parse_error(ParseError::NonUrlCodePoint));
  684. }
  685. Ok(())
  686. }