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