parser.rs 28 KB

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