parser.rs 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080
  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, Write};
  11. use super::{Url, EncodingOverride};
  12. use host::{self, HostInternal};
  13. use percent_encoding::{
  14. utf8_percent_encode, 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. IdnaError => "invalid international domain name",
  41. InvalidPort => "invalid port number",
  42. InvalidIpv4Address => "invalid IPv4 address",
  43. InvalidIpv6Address => "invalid IPv6 address",
  44. InvalidDomainCharacter => "invalid domain character",
  45. RelativeUrlWithoutBase => "relative URL without a base",
  46. RelativeUrlWithNonRelativeBase => "relative URL with a non-relative base",
  47. Overflow => "URLs more than 4 GB are not supported",
  48. }
  49. impl fmt::Display for ParseError {
  50. fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
  51. self.description().fmt(fmt)
  52. }
  53. }
  54. impl From<::idna::uts46::Errors> for ParseError {
  55. fn from(_: ::idna::uts46::Errors) -> ParseError { ParseError::IdnaError }
  56. }
  57. #[derive(Copy, Clone)]
  58. pub enum SchemeType {
  59. File,
  60. SpecialNotFile,
  61. NotSpecial,
  62. }
  63. impl SchemeType {
  64. fn is_special(&self) -> bool {
  65. !matches!(*self, SchemeType::NotSpecial)
  66. }
  67. fn is_file(&self) -> bool {
  68. matches!(*self, SchemeType::File)
  69. }
  70. fn from(s: &str) -> Self {
  71. match s {
  72. "http" | "https" | "ws" | "wss" | "ftp" | "gopher" => SchemeType::SpecialNotFile,
  73. "file" => SchemeType::File,
  74. _ => SchemeType::NotSpecial,
  75. }
  76. }
  77. }
  78. pub fn default_port(scheme: &str) -> Option<u16> {
  79. match scheme {
  80. "http" | "ws" => Some(80),
  81. "https" | "wss" => Some(443),
  82. "ftp" => Some(21),
  83. "gopher" => Some(70),
  84. _ => None,
  85. }
  86. }
  87. pub struct Parser<'a> {
  88. pub serialization: String,
  89. pub base_url: Option<&'a Url>,
  90. pub query_encoding_override: EncodingOverride,
  91. pub log_syntax_violation: Option<&'a Fn(&'static str)>,
  92. pub context: Context,
  93. }
  94. #[derive(PartialEq, Eq)]
  95. pub enum Context {
  96. UrlParser,
  97. Setter,
  98. }
  99. impl<'a> Parser<'a> {
  100. fn syntax_violation(&self, reason: &'static str) {
  101. if let Some(log) = self.log_syntax_violation {
  102. log(reason)
  103. }
  104. }
  105. fn syntax_violation_if<F: Fn() -> bool>(&self, reason: &'static str, test: F) {
  106. // Skip test if not logging.
  107. if let Some(log) = self.log_syntax_violation {
  108. if test() {
  109. log(reason)
  110. }
  111. }
  112. }
  113. /// https://url.spec.whatwg.org/#concept-basic-url-parser
  114. pub fn parse_url(mut self, original_input: &str) -> ParseResult<Url> {
  115. let input = original_input.trim_matches(c0_control_or_space);
  116. if input.len() < original_input.len() {
  117. self.syntax_violation("leading or trailing control or space character")
  118. }
  119. if let Ok(remaining) = self.parse_scheme(input) {
  120. return self.parse_with_scheme(remaining)
  121. }
  122. // No-scheme state
  123. if let Some(base_url) = self.base_url {
  124. if input.starts_with("#") {
  125. self.fragment_only(base_url, input)
  126. } else if base_url.non_relative() {
  127. Err(ParseError::RelativeUrlWithNonRelativeBase)
  128. } else {
  129. let scheme_type = SchemeType::from(base_url.scheme());
  130. if scheme_type.is_file() {
  131. self.parse_file(input, Some(base_url))
  132. } else {
  133. self.parse_relative(input, scheme_type, base_url)
  134. }
  135. }
  136. } else {
  137. Err(ParseError::RelativeUrlWithoutBase)
  138. }
  139. }
  140. pub fn parse_scheme<'i>(&mut self, input: &'i str) -> Result<&'i str, ()> {
  141. if input.is_empty() || !input.starts_with(ascii_alpha) {
  142. return Err(())
  143. }
  144. debug_assert!(self.serialization.is_empty());
  145. for (i, c) in input.char_indices() {
  146. match c {
  147. 'a'...'z' | 'A'...'Z' | '0'...'9' | '+' | '-' | '.' => {
  148. self.serialization.push(c.to_ascii_lowercase())
  149. }
  150. ':' => return Ok(&input[i + 1..]),
  151. _ => {
  152. self.serialization.clear();
  153. return Err(())
  154. }
  155. }
  156. }
  157. // EOF before ':'
  158. match self.context {
  159. Context::Setter => Ok(""),
  160. Context::UrlParser => {
  161. self.serialization.clear();
  162. Err(())
  163. }
  164. }
  165. }
  166. fn parse_with_scheme(mut self, input: &str) -> ParseResult<Url> {
  167. let scheme_end = try!(to_u32(self.serialization.len()));
  168. let scheme_type = SchemeType::from(&self.serialization);
  169. self.serialization.push(':');
  170. match scheme_type {
  171. SchemeType::File => {
  172. self.syntax_violation_if("expected // after file:", || !input.starts_with("//"));
  173. let base_file_url = self.base_url.and_then(|base| {
  174. if base.scheme() == "file" { Some(base) } else { None }
  175. });
  176. self.serialization.clear();
  177. self.parse_file(input, base_file_url)
  178. }
  179. SchemeType::SpecialNotFile => {
  180. // special relative or authority state
  181. let slashes_count = input.find(|c| !matches!(c, '/' | '\\')).unwrap_or(input.len());
  182. if let Some(base_url) = self.base_url {
  183. if slashes_count < 2 &&
  184. base_url.scheme() == &self.serialization[..scheme_end as usize] {
  185. // Non-relative URLs only happen with "not special" schemes.
  186. debug_assert!(!base_url.non_relative());
  187. self.serialization.clear();
  188. return self.parse_relative(input, scheme_type, base_url)
  189. }
  190. }
  191. // special authority slashes state
  192. self.syntax_violation_if("expected //", || &input[..slashes_count] != "//");
  193. self.after_double_slash(&input[slashes_count..], scheme_type, scheme_end)
  194. }
  195. SchemeType::NotSpecial => self.parse_non_special(input, scheme_type, scheme_end)
  196. }
  197. }
  198. /// Scheme other than file, http, https, ws, ws, ftp, gopher.
  199. fn parse_non_special(mut self, input: &str, scheme_type: SchemeType, scheme_end: u32)
  200. -> ParseResult<Url> {
  201. // path or authority state (
  202. if input.starts_with("//") {
  203. return self.after_double_slash(&input[2..], scheme_type, scheme_end)
  204. }
  205. // Anarchist URL (no authority)
  206. let path_start = try!(to_u32(self.serialization.len()));
  207. let username_end = path_start;
  208. let host_start = path_start;
  209. let host_end = path_start;
  210. let host = HostInternal::None;
  211. let port = None;
  212. let remaining = if input.starts_with("/") {
  213. let path_start = self.serialization.len();
  214. self.serialization.push('/');
  215. self.parse_path(scheme_type, &mut false, path_start, &input[1..])
  216. } else {
  217. self.parse_non_relative_path(input)
  218. };
  219. self.with_query_and_fragment(scheme_end, username_end, host_start,
  220. host_end, host, port, path_start, remaining)
  221. }
  222. fn parse_file(mut self, input: &str, mut base_file_url: Option<&Url>) -> ParseResult<Url> {
  223. // file state
  224. debug_assert!(self.serialization.is_empty());
  225. let c = input.chars().next();
  226. match c {
  227. None => {
  228. if let Some(base_url) = base_file_url {
  229. // Copy everything except the fragment
  230. let before_fragment = match base_url.fragment_start {
  231. Some(i) => &base_url.serialization[..i as usize],
  232. None => &*base_url.serialization,
  233. };
  234. self.serialization.push_str(before_fragment);
  235. Ok(Url {
  236. serialization: self.serialization,
  237. fragment_start: None,
  238. ..*base_url
  239. })
  240. } else {
  241. self.serialization.push_str("file:///");
  242. let scheme_end = "file".len() as u32;
  243. let path_start = "file://".len() as u32;
  244. Ok(Url {
  245. serialization: self.serialization,
  246. scheme_end: scheme_end,
  247. username_end: path_start,
  248. host_start: path_start,
  249. host_end: path_start,
  250. host: HostInternal::None,
  251. port: None,
  252. path_start: path_start,
  253. query_start: None,
  254. fragment_start: None,
  255. })
  256. }
  257. },
  258. Some('?') => {
  259. if let Some(base_url) = base_file_url {
  260. // Copy everything up to the query string
  261. let before_query = match (base_url.query_start, base_url.fragment_start) {
  262. (None, None) => &*base_url.serialization,
  263. (Some(i), _) |
  264. (None, Some(i)) => base_url.slice(..i)
  265. };
  266. self.serialization.push_str(before_query);
  267. let (query_start, fragment_start) =
  268. try!(self.parse_query_and_fragment(base_url.scheme_end, input));
  269. Ok(Url {
  270. serialization: self.serialization,
  271. query_start: query_start,
  272. fragment_start: fragment_start,
  273. ..*base_url
  274. })
  275. } else {
  276. self.serialization.push_str("file:///");
  277. let scheme_end = "file".len() as u32;
  278. let path_start = "file://".len() as u32;
  279. let (query_start, fragment_start) =
  280. try!(self.parse_query_and_fragment(scheme_end, input));
  281. Ok(Url {
  282. serialization: self.serialization,
  283. scheme_end: scheme_end,
  284. username_end: path_start,
  285. host_start: path_start,
  286. host_end: path_start,
  287. host: HostInternal::None,
  288. port: None,
  289. path_start: path_start,
  290. query_start: query_start,
  291. fragment_start: fragment_start,
  292. })
  293. }
  294. },
  295. Some('#') => {
  296. if let Some(base_url) = base_file_url {
  297. self.fragment_only(base_url, input)
  298. } else {
  299. self.serialization.push_str("file:///");
  300. let scheme_end = "file".len() as u32;
  301. let path_start = "file://".len() as u32;
  302. let fragment_start = "file:///".len() as u32;
  303. self.parse_fragment(&input[1..]);
  304. Ok(Url {
  305. serialization: self.serialization,
  306. scheme_end: scheme_end,
  307. username_end: path_start,
  308. host_start: path_start,
  309. host_end: path_start,
  310. host: HostInternal::None,
  311. port: None,
  312. path_start: path_start,
  313. query_start: None,
  314. fragment_start: Some(fragment_start),
  315. })
  316. }
  317. }
  318. Some('/') | Some('\\') => {
  319. self.syntax_violation_if("backslash", || c == Some('\\'));
  320. let input = &input[1..];
  321. // file slash state
  322. let c = input.chars().next();
  323. self.syntax_violation_if("backslash", || c == Some('\\'));
  324. if matches!(c, Some('/') | Some('\\')) {
  325. // file host state
  326. self.serialization.push_str("file://");
  327. let scheme_end = "file".len() as u32;
  328. let host_start = "file://".len() as u32;
  329. let (path_start, host, remaining) = try!(self.parse_file_host(&input[1..]));
  330. let host_end = try!(to_u32(self.serialization.len()));
  331. let mut has_host = !matches!(host, HostInternal::None);
  332. let remaining = if path_start {
  333. self.parse_path_start(SchemeType::File, &mut has_host, remaining)
  334. } else {
  335. let path_start = self.serialization.len();
  336. self.serialization.push('/');
  337. self.parse_path(SchemeType::File, &mut has_host, path_start, remaining)
  338. };
  339. // FIXME: deal with has_host
  340. let (query_start, fragment_start) =
  341. try!(self.parse_query_and_fragment(scheme_end, remaining));
  342. Ok(Url {
  343. serialization: self.serialization,
  344. scheme_end: scheme_end,
  345. username_end: host_start,
  346. host_start: host_start,
  347. host_end: host_end,
  348. host: host,
  349. port: None,
  350. path_start: host_end,
  351. query_start: query_start,
  352. fragment_start: fragment_start,
  353. })
  354. } else {
  355. self.serialization.push_str("file:///");
  356. let scheme_end = "file".len() as u32;
  357. let path_start = "file://".len();
  358. if let Some(base_url) = base_file_url {
  359. let first_segment = base_url.path_segments().unwrap().next().unwrap();
  360. // FIXME: *normalized* drive letter
  361. if is_windows_drive_letter(first_segment) {
  362. self.serialization.push_str(first_segment);
  363. self.serialization.push('/');
  364. }
  365. }
  366. let remaining = self.parse_path(
  367. SchemeType::File, &mut false, path_start, input);
  368. let (query_start, fragment_start) =
  369. try!(self.parse_query_and_fragment(scheme_end, remaining));
  370. let path_start = path_start as u32;
  371. Ok(Url {
  372. serialization: self.serialization,
  373. scheme_end: scheme_end,
  374. username_end: path_start,
  375. host_start: path_start,
  376. host_end: path_start,
  377. host: HostInternal::None,
  378. port: None,
  379. path_start: path_start,
  380. query_start: query_start,
  381. fragment_start: fragment_start,
  382. })
  383. }
  384. }
  385. _ => {
  386. if starts_with_windows_drive_letter_segment(input) {
  387. base_file_url = None;
  388. }
  389. if let Some(base_url) = base_file_url {
  390. let before_query = match (base_url.query_start, base_url.fragment_start) {
  391. (None, None) => &*base_url.serialization,
  392. (Some(i), _) |
  393. (None, Some(i)) => base_url.slice(..i)
  394. };
  395. self.serialization.push_str(before_query);
  396. self.pop_path(SchemeType::File, base_url.path_start as usize);
  397. let remaining = self.parse_path(
  398. SchemeType::File, &mut true, base_url.path_start as usize, input);
  399. self.with_query_and_fragment(
  400. base_url.scheme_end, base_url.username_end, base_url.host_start,
  401. base_url.host_end, base_url.host, base_url.port, base_url.path_start, remaining)
  402. } else {
  403. self.serialization.push_str("file:///");
  404. let scheme_end = "file".len() as u32;
  405. let path_start = "file://".len();
  406. let remaining = self.parse_path(
  407. SchemeType::File, &mut false, path_start, input);
  408. let (query_start, fragment_start) =
  409. try!(self.parse_query_and_fragment(scheme_end, remaining));
  410. let path_start = path_start as u32;
  411. Ok(Url {
  412. serialization: self.serialization,
  413. scheme_end: scheme_end,
  414. username_end: path_start,
  415. host_start: path_start,
  416. host_end: path_start,
  417. host: HostInternal::None,
  418. port: None,
  419. path_start: path_start,
  420. query_start: query_start,
  421. fragment_start: fragment_start,
  422. })
  423. }
  424. }
  425. }
  426. }
  427. fn parse_relative(mut self, input: &str, scheme_type: SchemeType, base_url: &Url)
  428. -> ParseResult<Url> {
  429. // relative state
  430. debug_assert!(self.serialization.is_empty());
  431. match input.chars().next() {
  432. None => {
  433. // Copy everything except the fragment
  434. let before_fragment = match base_url.fragment_start {
  435. Some(i) => &base_url.serialization[..i as usize],
  436. None => &*base_url.serialization,
  437. };
  438. self.serialization.push_str(before_fragment);
  439. Ok(Url {
  440. serialization: self.serialization,
  441. fragment_start: None,
  442. ..*base_url
  443. })
  444. },
  445. Some('?') => {
  446. // Copy everything up to the query string
  447. let before_query = match (base_url.query_start, base_url.fragment_start) {
  448. (None, None) => &*base_url.serialization,
  449. (Some(i), _) |
  450. (None, Some(i)) => base_url.slice(..i)
  451. };
  452. self.serialization.push_str(before_query);
  453. let (query_start, fragment_start) =
  454. try!(self.parse_query_and_fragment(base_url.scheme_end, input));
  455. Ok(Url {
  456. serialization: self.serialization,
  457. query_start: query_start,
  458. fragment_start: fragment_start,
  459. ..*base_url
  460. })
  461. },
  462. Some('#') => self.fragment_only(base_url, input),
  463. Some('/') | Some('\\') => {
  464. let slashes_count = input.find(|c| !matches!(c, '/' | '\\')).unwrap_or(input.len());
  465. if slashes_count >= 2 {
  466. self.syntax_violation_if("expected //", || &input[..slashes_count] != "//");
  467. let scheme_end = base_url.scheme_end;
  468. debug_assert!(base_url.byte_at(scheme_end) == b':');
  469. self.serialization.push_str(base_url.slice(..scheme_end + 1));
  470. return self.after_double_slash(&input[slashes_count..], scheme_type, scheme_end)
  471. }
  472. let path_start = base_url.path_start;
  473. debug_assert!(base_url.byte_at(path_start) == b'/');
  474. self.serialization.push_str(base_url.slice(..path_start + 1));
  475. let remaining = self.parse_path(
  476. scheme_type, &mut true, path_start as usize, &input[1..]);
  477. self.with_query_and_fragment(
  478. base_url.scheme_end, base_url.username_end, base_url.host_start,
  479. base_url.host_end, base_url.host, base_url.port, base_url.path_start, remaining)
  480. }
  481. _ => {
  482. let before_query = match (base_url.query_start, base_url.fragment_start) {
  483. (None, None) => &*base_url.serialization,
  484. (Some(i), _) |
  485. (None, Some(i)) => base_url.slice(..i)
  486. };
  487. self.serialization.push_str(before_query);
  488. // FIXME spec says just "remove last entry", not the "pop" algorithm
  489. self.pop_path(scheme_type, base_url.path_start as usize);
  490. let remaining = self.parse_path(
  491. scheme_type, &mut true, base_url.path_start as usize, input);
  492. self.with_query_and_fragment(
  493. base_url.scheme_end, base_url.username_end, base_url.host_start,
  494. base_url.host_end, base_url.host, base_url.port, base_url.path_start, remaining)
  495. }
  496. }
  497. }
  498. fn after_double_slash(mut self, input: &str, scheme_type: SchemeType, scheme_end: u32)
  499. -> ParseResult<Url> {
  500. self.serialization.push('/');
  501. self.serialization.push('/');
  502. // authority state
  503. let (username_end, remaining) = try!(self.parse_userinfo(input, scheme_type));
  504. // host state
  505. let host_start = try!(to_u32(self.serialization.len()));
  506. let (host_end, host, port, remaining) =
  507. try!(self.parse_host_and_port(remaining, scheme_end, scheme_type));
  508. // path state
  509. let path_start = try!(to_u32(self.serialization.len()));
  510. let remaining = self.parse_path_start(
  511. scheme_type, &mut true, remaining);
  512. self.with_query_and_fragment(scheme_end, username_end, host_start,
  513. host_end, host, port, path_start, remaining)
  514. }
  515. /// Return (username_end, remaining)
  516. fn parse_userinfo<'i>(&mut self, input: &'i str, scheme_type: SchemeType)
  517. -> ParseResult<(u32, &'i str)> {
  518. let mut last_at = None;
  519. for (i, c) in input.char_indices() {
  520. match c {
  521. '@' => {
  522. if last_at.is_some() {
  523. self.syntax_violation("unencoded @ sign in username or password")
  524. } else {
  525. self.syntax_violation(
  526. "embedding authentification information (username or password) \
  527. in an URL is not recommended")
  528. }
  529. last_at = Some(i)
  530. },
  531. '/' | '?' | '#' => break,
  532. '\\' if scheme_type.is_special() => break,
  533. _ => (),
  534. }
  535. }
  536. let (input, remaining) = match last_at {
  537. None => return Ok((try!(to_u32(self.serialization.len())), input)),
  538. Some(0) => return Ok((try!(to_u32(self.serialization.len())), &input[1..])),
  539. Some(at) => (&input[..at], &input[at + 1..]),
  540. };
  541. let mut username_end = None;
  542. for (i, c, next_i) in input.char_ranges() {
  543. match c {
  544. ':' if username_end.is_none() => {
  545. // Start parsing password
  546. username_end = Some(try!(to_u32(self.serialization.len())));
  547. self.serialization.push(':');
  548. },
  549. '\t' | '\n' | '\r' => {},
  550. _ => {
  551. self.check_url_code_point(input, i, c);
  552. let utf8_c = &input[i..next_i];
  553. self.serialization.extend(utf8_percent_encode(utf8_c, USERINFO_ENCODE_SET));
  554. }
  555. }
  556. }
  557. let username_end = match username_end {
  558. Some(i) => i,
  559. None => try!(to_u32(self.serialization.len())),
  560. };
  561. self.serialization.push('@');
  562. Ok((username_end, remaining))
  563. }
  564. pub fn parse_host_and_port<'i>(&mut self, input: &'i str,
  565. scheme_end: u32, scheme_type: SchemeType)
  566. -> ParseResult<(u32, HostInternal, Option<u16>, &'i str)> {
  567. let (host, remaining) = try!(self.parse_host(input, scheme_type));
  568. let host_end = try!(to_u32(self.serialization.len()));
  569. let (port, remaining) = if remaining.starts_with(":") {
  570. try!(self.parse_port(&remaining[1..], scheme_end))
  571. } else {
  572. (None, remaining)
  573. };
  574. Ok((host_end, host, port, remaining))
  575. }
  576. pub fn parse_host<'i>(&mut self, input: &'i str, scheme_type: SchemeType)
  577. -> ParseResult<(HostInternal, &'i str)> {
  578. let mut inside_square_brackets = false;
  579. let mut has_ignored_chars = false;
  580. let mut end = input.len();
  581. for (i, b) in input.bytes().enumerate() {
  582. match b {
  583. b':' if !inside_square_brackets => {
  584. end = i;
  585. break
  586. },
  587. b'/' | b'?' | b'#' => {
  588. end = i;
  589. break
  590. }
  591. b'\\' if scheme_type.is_special() => {
  592. end = i;
  593. break
  594. }
  595. b'\t' | b'\n' | b'\r' => {
  596. self.syntax_violation("invalid character");
  597. has_ignored_chars = true;
  598. }
  599. b'[' => inside_square_brackets = true,
  600. b']' => inside_square_brackets = false,
  601. _ => {}
  602. }
  603. }
  604. let replaced: String;
  605. let host_input = if has_ignored_chars {
  606. replaced = input[..end].chars().filter(|&c| !matches!(c, '\t' | '\n' | '\r')).collect();
  607. &*replaced
  608. } else {
  609. &input[..end]
  610. };
  611. if scheme_type.is_special() && host_input.is_empty() {
  612. return Err(ParseError::EmptyHost)
  613. }
  614. let host = try!(host::parse(&host_input, &mut self.serialization));
  615. Ok((host, &input[end..]))
  616. }
  617. pub fn parse_file_host<'i>(&mut self, input: &'i str)
  618. -> ParseResult<(bool, HostInternal, &'i str)> {
  619. let mut has_ignored_chars = false;
  620. let mut end = input.len();
  621. for (i, b) in input.bytes().enumerate() {
  622. match b {
  623. b'/' | b'\\' | b'?' | b'#' => {
  624. end = i;
  625. break
  626. }
  627. b'\t' | b'\n' | b'\r' => {
  628. self.syntax_violation("invalid character");
  629. has_ignored_chars = true;
  630. }
  631. _ => {}
  632. }
  633. }
  634. let replaced: String;
  635. let host_input = if has_ignored_chars {
  636. replaced = input[..end].chars().filter(|&c| !matches!(c, '\t' | '\n' | '\r')).collect();
  637. &*replaced
  638. } else {
  639. &input[..end]
  640. };
  641. if is_windows_drive_letter(host_input) {
  642. return Ok((false, HostInternal::None, input))
  643. }
  644. let mut host;
  645. if host_input.is_empty() {
  646. host = HostInternal::None;
  647. } else {
  648. let host_start = self.serialization.len();
  649. host = try!(host::parse(&host_input, &mut self.serialization));
  650. if &self.serialization[host_start..] == "localhost" {
  651. host = HostInternal::None;
  652. self.serialization.truncate(host_start);
  653. }
  654. }
  655. Ok((true, host, &input[end..]))
  656. }
  657. pub fn parse_port<'i>(&mut self, input: &'i str, scheme_end: u32)
  658. -> ParseResult<(Option<u16>, &'i str)> {
  659. let mut port = 0;
  660. let mut has_any_digit = false;
  661. let mut end = input.len();
  662. for (i, c) in input.char_indices() {
  663. if let Some(digit) = c.to_digit(10) {
  664. port = port * 10 + digit;
  665. if port > ::std::u16::MAX as u32 {
  666. return Err(ParseError::InvalidPort)
  667. }
  668. has_any_digit = true;
  669. } else {
  670. match c {
  671. '/' | '\\' | '?' | '#' => {
  672. end = i;
  673. break
  674. },
  675. '\t' | '\n' | '\r' => self.syntax_violation("invalid character"),
  676. _ => return Err(ParseError::InvalidPort)
  677. }
  678. }
  679. }
  680. let mut opt_port = Some(port as u16);
  681. if !has_any_digit || opt_port == default_port(&self.serialization[..scheme_end as usize]) {
  682. opt_port = None;
  683. } else {
  684. self.serialization.push(':');
  685. write!(&mut self.serialization, "{}", port).unwrap();
  686. }
  687. return Ok((opt_port, &input[end..]))
  688. }
  689. fn parse_path_start<'i>(&mut self, scheme_type: SchemeType, has_host: &mut bool,
  690. mut input: &'i str)
  691. -> &'i str {
  692. // Path start state
  693. let mut iter = input.chars();
  694. match iter.next() {
  695. Some('/') => input = iter.as_str(),
  696. Some('\\') => {
  697. self.syntax_violation("backslash");
  698. input = iter.as_str()
  699. }
  700. _ => {}
  701. }
  702. let path_start = self.serialization.len();
  703. self.serialization.push('/');
  704. self.parse_path(scheme_type, has_host, path_start, input)
  705. }
  706. fn parse_path<'i>(&mut self, scheme_type: SchemeType, has_host: &mut bool,
  707. path_start: usize, input: &'i str)
  708. -> &'i str {
  709. // Relative path state
  710. debug_assert!(self.serialization.ends_with("/"));
  711. let mut iter = input.char_ranges();
  712. let mut end;
  713. loop {
  714. let segment_start = self.serialization.len();
  715. let mut ends_with_slash = false;
  716. end = input.len();
  717. while let Some((i, c, next_i)) = iter.next() {
  718. match c {
  719. '/' => {
  720. ends_with_slash = true;
  721. end = i;
  722. break
  723. },
  724. '\\' if scheme_type.is_special() => {
  725. self.syntax_violation("backslash");
  726. ends_with_slash = true;
  727. end = i;
  728. break
  729. },
  730. '?' | '#' if self.context == Context::UrlParser => {
  731. end = i;
  732. break
  733. },
  734. '\t' | '\n' | '\r' => self.syntax_violation("invalid characters"),
  735. _ => {
  736. self.check_url_code_point(input, i, c);
  737. if c == '%' {
  738. let after_percent_sign = iter.clone();
  739. if matches!(iter.next(), Some((_, '2', _))) &&
  740. matches!(iter.next(), Some((_, 'E', _)) | Some((_, 'e', _))) {
  741. self.serialization.push('.');
  742. continue
  743. }
  744. iter = after_percent_sign
  745. }
  746. self.serialization.extend(utf8_percent_encode(
  747. &input[i..next_i], DEFAULT_ENCODE_SET));
  748. }
  749. }
  750. }
  751. match &self.serialization[segment_start..] {
  752. ".." => {
  753. debug_assert!(self.serialization.as_bytes()[segment_start - 1] == b'/');
  754. self.serialization.truncate(segment_start - 1); // Truncate "/.."
  755. self.pop_path(scheme_type, path_start);
  756. if !self.serialization[path_start..].ends_with("/") {
  757. self.serialization.push('/')
  758. }
  759. },
  760. "." => {
  761. self.serialization.truncate(segment_start);
  762. },
  763. _ => {
  764. if scheme_type.is_file() && is_windows_drive_letter(
  765. &self.serialization[path_start + 1..]
  766. ) {
  767. unsafe {
  768. *self.serialization.as_mut_vec().last_mut().unwrap() = b':'
  769. }
  770. if *has_host {
  771. self.syntax_violation("file: with host and Windows drive letter");
  772. *has_host = false; // FIXME account for this in callers
  773. }
  774. }
  775. if ends_with_slash {
  776. self.serialization.push('/')
  777. }
  778. }
  779. }
  780. if !ends_with_slash {
  781. break
  782. }
  783. }
  784. &input[end..]
  785. }
  786. /// https://url.spec.whatwg.org/#pop-a-urls-path
  787. fn pop_path(&mut self, scheme_type: SchemeType, path_start: usize) {
  788. if self.serialization.len() > path_start {
  789. let slash_position = self.serialization[path_start..].rfind('/').unwrap();
  790. // + 1 since rfind returns the position before the slash.
  791. let segment_start = path_start + slash_position + 1;
  792. // Don’t pop a Windows drive letter
  793. // FIXME: *normalized* Windows drive letter
  794. if !(
  795. scheme_type.is_file() &&
  796. is_windows_drive_letter(&self.serialization[segment_start..])
  797. ) {
  798. self.serialization.truncate(segment_start);
  799. }
  800. }
  801. }
  802. fn parse_non_relative_path<'i>(&mut self, input: &'i str) -> &'i str {
  803. for (i, c, next_i) in input.char_ranges() {
  804. match c {
  805. '?' | '#' => return &input[i..],
  806. '\t' | '\n' | '\r' => self.syntax_violation("invalid character"),
  807. _ => {
  808. self.check_url_code_point(input, i, c);
  809. self.serialization.extend(utf8_percent_encode(
  810. &input[i..next_i], SIMPLE_ENCODE_SET));
  811. }
  812. }
  813. }
  814. ""
  815. }
  816. fn with_query_and_fragment(mut self, scheme_end: u32, username_end: u32,
  817. host_start: u32, host_end: u32, host: HostInternal,
  818. port: Option<u16>, path_start: u32, remaining: &str)
  819. -> ParseResult<Url> {
  820. let (query_start, fragment_start) =
  821. try!(self.parse_query_and_fragment(scheme_end, remaining));
  822. Ok(Url {
  823. serialization: self.serialization,
  824. scheme_end: scheme_end,
  825. username_end: username_end,
  826. host_start: host_start,
  827. host_end: host_end,
  828. host: host,
  829. port: port,
  830. path_start: path_start,
  831. query_start: query_start,
  832. fragment_start: fragment_start
  833. })
  834. }
  835. /// Return (query_start, fragment_start)
  836. fn parse_query_and_fragment(&mut self, scheme_end: u32, mut input: &str)
  837. -> ParseResult<(Option<u32>, Option<u32>)> {
  838. let mut query_start = None;
  839. match input.chars().next() {
  840. Some('#') => {}
  841. Some('?') => {
  842. query_start = Some(try!(to_u32(self.serialization.len())));
  843. self.serialization.push('?');
  844. let remaining = self.parse_query(scheme_end, &input[1..]);
  845. if let Some(remaining) = remaining {
  846. input = remaining
  847. } else {
  848. return Ok((query_start, None))
  849. }
  850. }
  851. None => return Ok((None, None)),
  852. _ => panic!("Programming error. parse_query_and_fragment() should not \
  853. have been called with input \"{}\"", input)
  854. };
  855. let fragment_start = try!(to_u32(self.serialization.len()));
  856. self.serialization.push('#');
  857. debug_assert!(input.starts_with("#"));
  858. self.parse_fragment(&input[1..]);
  859. Ok((query_start, Some(fragment_start)))
  860. }
  861. pub fn parse_query<'i>(&mut self, scheme_end: u32, input: &'i str)
  862. -> Option<&'i str> {
  863. let mut query = String::new(); // FIXME: use a streaming decoder instead
  864. let mut remaining = None;
  865. for (i, c) in input.char_indices() {
  866. match c {
  867. '#' if self.context == Context::UrlParser => {
  868. remaining = Some(&input[i..]);
  869. break
  870. },
  871. '\t' | '\n' | '\r' => self.syntax_violation("invalid characters"),
  872. _ => {
  873. self.check_url_code_point(input, i, c);
  874. query.push(c);
  875. }
  876. }
  877. }
  878. let encoding = match &self.serialization[..scheme_end as usize] {
  879. "http" | "https" | "file" | "ftp" | "gopher" => self.query_encoding_override,
  880. _ => EncodingOverride::utf8(),
  881. };
  882. let query_bytes = encoding.encode(&query);
  883. self.serialization.extend(percent_encode(&query_bytes, QUERY_ENCODE_SET));
  884. remaining
  885. }
  886. fn fragment_only(mut self, base_url: &Url, input: &str) -> ParseResult<Url> {
  887. let before_fragment = match base_url.fragment_start {
  888. Some(i) => base_url.slice(..i),
  889. None => &*base_url.serialization,
  890. };
  891. debug_assert!(self.serialization.is_empty());
  892. self.serialization.reserve(before_fragment.len() + input.len());
  893. self.serialization.push_str(before_fragment);
  894. self.serialization.push('#');
  895. debug_assert!(input.starts_with("#"));
  896. self.parse_fragment(&input[1..]);
  897. Ok(Url {
  898. serialization: self.serialization,
  899. fragment_start: Some(try!(to_u32(before_fragment.len()))),
  900. ..*base_url
  901. })
  902. }
  903. pub fn parse_fragment(&mut self, input: &str) {
  904. for (i, c) in input.char_indices() {
  905. match c {
  906. '\0' | '\t' | '\n' | '\r' => self.syntax_violation("invalid character"),
  907. _ => {
  908. self.check_url_code_point(input, i, c);
  909. self.serialization.push(c); // No percent-encoding here.
  910. }
  911. }
  912. }
  913. }
  914. fn check_url_code_point(&self, input: &str, i: usize, c: char) {
  915. if let Some(log) = self.log_syntax_violation {
  916. if c == '%' {
  917. if !starts_with_2_hex(&input[i + 1..]) {
  918. log("expected 2 hex digits after %")
  919. }
  920. } else if !is_url_code_point(c) {
  921. log("non-URL code point")
  922. }
  923. }
  924. }
  925. }
  926. #[inline]
  927. fn is_ascii_hex_digit(byte: u8) -> bool {
  928. matches!(byte, b'a'...b'f' | b'A'...b'F' | b'0'...b'9')
  929. }
  930. #[inline]
  931. fn starts_with_2_hex(input: &str) -> bool {
  932. input.len() >= 2
  933. && is_ascii_hex_digit(input.as_bytes()[0])
  934. && is_ascii_hex_digit(input.as_bytes()[1])
  935. }
  936. // Non URL code points:
  937. // U+0000 to U+0020 (space)
  938. // " # % < > [ \ ] ^ ` { | }
  939. // U+007F to U+009F
  940. // surrogates
  941. // U+FDD0 to U+FDEF
  942. // Last two of each plane: U+__FFFE to U+__FFFF for __ in 00 to 10 hex
  943. #[inline]
  944. fn is_url_code_point(c: char) -> bool {
  945. matches!(c,
  946. 'a'...'z' |
  947. 'A'...'Z' |
  948. '0'...'9' |
  949. '!' | '$' | '&' | '\'' | '(' | ')' | '*' | '+' | ',' | '-' |
  950. '.' | '/' | ':' | ';' | '=' | '?' | '@' | '_' | '~' |
  951. '\u{A0}'...'\u{D7FF}' | '\u{E000}'...'\u{FDCF}' | '\u{FDF0}'...'\u{FFFD}' |
  952. '\u{10000}'...'\u{1FFFD}' | '\u{20000}'...'\u{2FFFD}' |
  953. '\u{30000}'...'\u{3FFFD}' | '\u{40000}'...'\u{4FFFD}' |
  954. '\u{50000}'...'\u{5FFFD}' | '\u{60000}'...'\u{6FFFD}' |
  955. '\u{70000}'...'\u{7FFFD}' | '\u{80000}'...'\u{8FFFD}' |
  956. '\u{90000}'...'\u{9FFFD}' | '\u{A0000}'...'\u{AFFFD}' |
  957. '\u{B0000}'...'\u{BFFFD}' | '\u{C0000}'...'\u{CFFFD}' |
  958. '\u{D0000}'...'\u{DFFFD}' | '\u{E1000}'...'\u{EFFFD}' |
  959. '\u{F0000}'...'\u{FFFFD}' | '\u{100000}'...'\u{10FFFD}')
  960. }
  961. pub trait StrCharRanges<'a> {
  962. fn char_ranges(&self) -> CharRanges<'a>;
  963. }
  964. impl<'a> StrCharRanges<'a> for &'a str {
  965. #[inline]
  966. fn char_ranges(&self) -> CharRanges<'a> {
  967. CharRanges { slice: *self, position: 0 }
  968. }
  969. }
  970. #[derive(Clone)]
  971. pub struct CharRanges<'a> {
  972. slice: &'a str,
  973. position: usize,
  974. }
  975. impl<'a> Iterator for CharRanges<'a> {
  976. type Item = (usize, char, usize);
  977. #[inline]
  978. fn next(&mut self) -> Option<(usize, char, usize)> {
  979. match self.slice[self.position..].chars().next() {
  980. Some(ch) => {
  981. let position = self.position;
  982. self.position = position + ch.len_utf8();
  983. Some((position, ch, position + ch.len_utf8()))
  984. }
  985. None => None,
  986. }
  987. }
  988. }
  989. /// https://url.spec.whatwg.org/#c0-controls-and-space
  990. #[inline]
  991. fn c0_control_or_space(ch: char) -> bool {
  992. ch <= ' ' // U+0000 to U+0020
  993. }
  994. /// https://url.spec.whatwg.org/#ascii-alpha
  995. #[inline]
  996. pub fn ascii_alpha(ch: char) -> bool {
  997. matches!(ch, 'a'...'z' | 'A'...'Z')
  998. }
  999. #[inline]
  1000. pub fn to_u32(i: usize) -> ParseResult<u32> {
  1001. if i <= ::std::u32::MAX as usize {
  1002. Ok(i as u32)
  1003. } else {
  1004. Err(ParseError::Overflow)
  1005. }
  1006. }
  1007. /// Wether the scheme is file:, the path has a single segment, and that segment
  1008. /// is a Windows drive letter
  1009. fn is_windows_drive_letter(segment: &str) -> bool {
  1010. segment.len() == 2
  1011. && starts_with_windows_drive_letter(segment)
  1012. }
  1013. fn starts_with_windows_drive_letter(s: &str) -> bool {
  1014. ascii_alpha(s.as_bytes()[0] as char)
  1015. && matches!(s.as_bytes()[1], b':' | b'|')
  1016. }
  1017. fn starts_with_windows_drive_letter_segment(s: &str) -> bool {
  1018. s.len() >= 3
  1019. && starts_with_windows_drive_letter(s)
  1020. && matches!(s.as_bytes()[2], b'/' | b'\\' | b'?' | b'#')
  1021. }