parser.rs 28 KB

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