parser.rs 46 KB

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