parser.rs 59 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606
  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::error::Error;
  9. use std::fmt::{self, Formatter, Write};
  10. use std::str;
  11. use host::{Host, HostInternal};
  12. use percent_encoding::{percent_encode, utf8_percent_encode, AsciiSet, CONTROLS};
  13. use query_encoding::EncodingOverride;
  14. use Url;
  15. /// https://url.spec.whatwg.org/#fragment-percent-encode-set
  16. const FRAGMENT: &AsciiSet = &CONTROLS.add(b' ').add(b'"').add(b'<').add(b'>').add(b'`');
  17. /// https://url.spec.whatwg.org/#path-percent-encode-set
  18. const PATH: &AsciiSet = &FRAGMENT.add(b'#').add(b'?').add(b'{').add(b'}');
  19. /// https://url.spec.whatwg.org/#userinfo-percent-encode-set
  20. pub(crate) const USERINFO: &AsciiSet = &PATH
  21. .add(b'/')
  22. .add(b':')
  23. .add(b';')
  24. .add(b'=')
  25. .add(b'@')
  26. .add(b'[')
  27. .add(b'\\')
  28. .add(b']')
  29. .add(b'^')
  30. .add(b'|');
  31. pub(crate) const PATH_SEGMENT: &AsciiSet = &PATH.add(b'/').add(b'%');
  32. // The backslash (\) character is treated as a path separator in special URLs
  33. // so it needs to be additionally escaped in that case.
  34. pub(crate) const SPECIAL_PATH_SEGMENT: &AsciiSet = &PATH_SEGMENT.add(b'\\');
  35. // https://url.spec.whatwg.org/#query-state
  36. const QUERY: &AsciiSet = &CONTROLS.add(b' ').add(b'"').add(b'#').add(b'<').add(b'>');
  37. const SPECIAL_QUERY: &AsciiSet = &QUERY.add(b'\'');
  38. pub type ParseResult<T> = Result<T, ParseError>;
  39. macro_rules! simple_enum_error {
  40. ($($name: ident => $description: expr,)+) => {
  41. /// Errors that can occur during parsing.
  42. ///
  43. /// This may be extended in the future so exhaustive matching is
  44. /// discouraged with an unused variant.
  45. #[derive(PartialEq, Eq, Clone, Copy, Debug)]
  46. pub enum ParseError {
  47. $(
  48. $name,
  49. )+
  50. /// Unused variant enable non-exhaustive matching
  51. #[doc(hidden)]
  52. __FutureProof,
  53. }
  54. impl Error for ParseError {
  55. fn description(&self) -> &str {
  56. match *self {
  57. $(
  58. ParseError::$name => $description,
  59. )+
  60. ParseError::__FutureProof => {
  61. unreachable!("Don't abuse the FutureProof!");
  62. }
  63. }
  64. }
  65. }
  66. }
  67. }
  68. simple_enum_error! {
  69. EmptyHost => "empty host",
  70. IdnaError => "invalid international domain name",
  71. InvalidPort => "invalid port number",
  72. InvalidIpv4Address => "invalid IPv4 address",
  73. InvalidIpv6Address => "invalid IPv6 address",
  74. InvalidDomainCharacter => "invalid domain character",
  75. RelativeUrlWithoutBase => "relative URL without a base",
  76. RelativeUrlWithCannotBeABaseBase => "relative URL with a cannot-be-a-base base",
  77. SetHostOnCannotBeABaseUrl => "a cannot-be-a-base URL doesn’t have a host to set",
  78. Overflow => "URLs more than 4 GB are not supported",
  79. }
  80. impl fmt::Display for ParseError {
  81. fn fmt(&self, f: &mut Formatter) -> fmt::Result {
  82. fmt::Display::fmt(self.description(), f)
  83. }
  84. }
  85. impl From<::idna::Errors> for ParseError {
  86. fn from(_: ::idna::Errors) -> ParseError {
  87. ParseError::IdnaError
  88. }
  89. }
  90. macro_rules! syntax_violation_enum {
  91. ($($name: ident => $description: expr,)+) => {
  92. /// Non-fatal syntax violations that can occur during parsing.
  93. ///
  94. /// This may be extended in the future so exhaustive matching is
  95. /// discouraged with an unused variant.
  96. #[derive(PartialEq, Eq, Clone, Copy, Debug)]
  97. pub enum SyntaxViolation {
  98. $(
  99. $name,
  100. )+
  101. /// Unused variant enable non-exhaustive matching
  102. #[doc(hidden)]
  103. __FutureProof,
  104. }
  105. impl SyntaxViolation {
  106. pub fn description(&self) -> &'static str {
  107. match *self {
  108. $(
  109. SyntaxViolation::$name => $description,
  110. )+
  111. SyntaxViolation::__FutureProof => {
  112. unreachable!("Don't abuse the FutureProof!");
  113. }
  114. }
  115. }
  116. }
  117. }
  118. }
  119. syntax_violation_enum! {
  120. Backslash => "backslash",
  121. C0SpaceIgnored =>
  122. "leading or trailing control or space character are ignored in URLs",
  123. EmbeddedCredentials =>
  124. "embedding authentication information (username or password) \
  125. in an URL is not recommended",
  126. ExpectedDoubleSlash => "expected //",
  127. ExpectedFileDoubleSlash => "expected // after file:",
  128. FileWithHostAndWindowsDrive => "file: with host and Windows drive letter",
  129. NonUrlCodePoint => "non-URL code point",
  130. NullInFragment => "NULL characters are ignored in URL fragment identifiers",
  131. PercentDecode => "expected 2 hex digits after %",
  132. TabOrNewlineIgnored => "tabs or newlines are ignored in URLs",
  133. UnencodedAtSign => "unencoded @ sign in username or password",
  134. }
  135. impl fmt::Display for SyntaxViolation {
  136. fn fmt(&self, f: &mut Formatter) -> fmt::Result {
  137. fmt::Display::fmt(self.description(), f)
  138. }
  139. }
  140. #[derive(Copy, Clone, PartialEq)]
  141. pub enum SchemeType {
  142. File,
  143. SpecialNotFile,
  144. NotSpecial,
  145. }
  146. impl SchemeType {
  147. pub fn is_special(&self) -> bool {
  148. !matches!(*self, SchemeType::NotSpecial)
  149. }
  150. pub fn is_file(&self) -> bool {
  151. matches!(*self, SchemeType::File)
  152. }
  153. pub fn from(s: &str) -> Self {
  154. match s {
  155. "http" | "https" | "ws" | "wss" | "ftp" | "gopher" => SchemeType::SpecialNotFile,
  156. "file" => SchemeType::File,
  157. _ => SchemeType::NotSpecial,
  158. }
  159. }
  160. }
  161. pub fn default_port(scheme: &str) -> Option<u16> {
  162. match scheme {
  163. "http" | "ws" => Some(80),
  164. "https" | "wss" => Some(443),
  165. "ftp" => Some(21),
  166. "gopher" => Some(70),
  167. _ => None,
  168. }
  169. }
  170. #[derive(Clone)]
  171. pub struct Input<'i> {
  172. chars: str::Chars<'i>,
  173. }
  174. impl<'i> Input<'i> {
  175. pub fn new(input: &'i str) -> Self {
  176. Input::with_log(input, None)
  177. }
  178. pub fn no_trim(input: &'i str) -> Self {
  179. Input {
  180. chars: input.chars(),
  181. }
  182. }
  183. pub fn trim_tab_and_newlines(
  184. original_input: &'i str,
  185. vfn: Option<&dyn Fn(SyntaxViolation)>,
  186. ) -> Self {
  187. let input = original_input.trim_matches(ascii_tab_or_new_line);
  188. if let Some(vfn) = vfn {
  189. if input.len() < original_input.len() {
  190. vfn(SyntaxViolation::C0SpaceIgnored)
  191. }
  192. if input.chars().any(|c| matches!(c, '\t' | '\n' | '\r')) {
  193. vfn(SyntaxViolation::TabOrNewlineIgnored)
  194. }
  195. }
  196. Input {
  197. chars: input.chars(),
  198. }
  199. }
  200. pub fn with_log(original_input: &'i str, vfn: Option<&dyn Fn(SyntaxViolation)>) -> Self {
  201. let input = original_input.trim_matches(c0_control_or_space);
  202. if let Some(vfn) = vfn {
  203. if input.len() < original_input.len() {
  204. vfn(SyntaxViolation::C0SpaceIgnored)
  205. }
  206. if input.chars().any(|c| matches!(c, '\t' | '\n' | '\r')) {
  207. vfn(SyntaxViolation::TabOrNewlineIgnored)
  208. }
  209. }
  210. Input {
  211. chars: input.chars(),
  212. }
  213. }
  214. #[inline]
  215. pub fn is_empty(&self) -> bool {
  216. self.clone().next().is_none()
  217. }
  218. #[inline]
  219. fn starts_with<P: Pattern>(&self, p: P) -> bool {
  220. p.split_prefix(&mut self.clone())
  221. }
  222. #[inline]
  223. pub fn split_prefix<P: Pattern>(&self, p: P) -> Option<Self> {
  224. let mut remaining = self.clone();
  225. if p.split_prefix(&mut remaining) {
  226. Some(remaining)
  227. } else {
  228. None
  229. }
  230. }
  231. #[inline]
  232. fn split_first(&self) -> (Option<char>, Self) {
  233. let mut remaining = self.clone();
  234. (remaining.next(), remaining)
  235. }
  236. #[inline]
  237. fn count_matching<F: Fn(char) -> bool>(&self, f: F) -> (u32, Self) {
  238. let mut count = 0;
  239. let mut remaining = self.clone();
  240. loop {
  241. let mut input = remaining.clone();
  242. if matches!(input.next(), Some(c) if f(c)) {
  243. remaining = input;
  244. count += 1;
  245. } else {
  246. return (count, remaining);
  247. }
  248. }
  249. }
  250. #[inline]
  251. fn next_utf8(&mut self) -> Option<(char, &'i str)> {
  252. loop {
  253. let utf8 = self.chars.as_str();
  254. match self.chars.next() {
  255. Some(c) => {
  256. if !matches!(c, '\t' | '\n' | '\r') {
  257. return Some((c, &utf8[..c.len_utf8()]));
  258. }
  259. }
  260. None => return None,
  261. }
  262. }
  263. }
  264. }
  265. pub trait Pattern {
  266. fn split_prefix<'i>(self, input: &mut Input<'i>) -> bool;
  267. }
  268. impl Pattern for char {
  269. fn split_prefix<'i>(self, input: &mut Input<'i>) -> bool {
  270. input.next() == Some(self)
  271. }
  272. }
  273. impl<'a> Pattern for &'a str {
  274. fn split_prefix<'i>(self, input: &mut Input<'i>) -> bool {
  275. for c in self.chars() {
  276. if input.next() != Some(c) {
  277. return false;
  278. }
  279. }
  280. true
  281. }
  282. }
  283. impl<F: FnMut(char) -> bool> Pattern for F {
  284. fn split_prefix<'i>(self, input: &mut Input<'i>) -> bool {
  285. input.next().map_or(false, self)
  286. }
  287. }
  288. impl<'i> Iterator for Input<'i> {
  289. type Item = char;
  290. fn next(&mut self) -> Option<char> {
  291. self.chars
  292. .by_ref()
  293. .find(|&c| !matches!(c, '\t' | '\n' | '\r'))
  294. }
  295. }
  296. pub struct Parser<'a> {
  297. pub serialization: String,
  298. pub base_url: Option<&'a Url>,
  299. pub query_encoding_override: EncodingOverride<'a>,
  300. pub violation_fn: Option<&'a dyn Fn(SyntaxViolation)>,
  301. pub context: Context,
  302. }
  303. #[derive(PartialEq, Eq, Copy, Clone)]
  304. pub enum Context {
  305. UrlParser,
  306. Setter,
  307. PathSegmentSetter,
  308. }
  309. impl<'a> Parser<'a> {
  310. fn log_violation(&self, v: SyntaxViolation) {
  311. if let Some(f) = self.violation_fn {
  312. f(v)
  313. }
  314. }
  315. fn log_violation_if(&self, v: SyntaxViolation, test: impl FnOnce() -> bool) {
  316. if let Some(f) = self.violation_fn {
  317. if test() {
  318. f(v)
  319. }
  320. }
  321. }
  322. pub fn for_setter(serialization: String) -> Parser<'a> {
  323. Parser {
  324. serialization,
  325. base_url: None,
  326. query_encoding_override: None,
  327. violation_fn: None,
  328. context: Context::Setter,
  329. }
  330. }
  331. /// https://url.spec.whatwg.org/#concept-basic-url-parser
  332. pub fn parse_url(mut self, input: &str) -> ParseResult<Url> {
  333. let input = Input::with_log(input, self.violation_fn);
  334. if let Ok(remaining) = self.parse_scheme(input.clone()) {
  335. return self.parse_with_scheme(remaining);
  336. }
  337. // No-scheme state
  338. if let Some(base_url) = self.base_url {
  339. if input.starts_with('#') {
  340. self.fragment_only(base_url, input)
  341. } else if base_url.cannot_be_a_base() {
  342. Err(ParseError::RelativeUrlWithCannotBeABaseBase)
  343. } else {
  344. let scheme_type = SchemeType::from(base_url.scheme());
  345. if scheme_type.is_file() {
  346. self.parse_file(input, scheme_type, Some(base_url))
  347. } else {
  348. self.parse_relative(input, scheme_type, base_url)
  349. }
  350. }
  351. } else {
  352. Err(ParseError::RelativeUrlWithoutBase)
  353. }
  354. }
  355. pub fn parse_scheme<'i>(&mut self, mut input: Input<'i>) -> Result<Input<'i>, ()> {
  356. if input.is_empty() || !input.starts_with(ascii_alpha) {
  357. return Err(());
  358. }
  359. debug_assert!(self.serialization.is_empty());
  360. while let Some(c) = input.next() {
  361. match c {
  362. 'a'..='z' | 'A'..='Z' | '0'..='9' | '+' | '-' | '.' => {
  363. self.serialization.push(c.to_ascii_lowercase())
  364. }
  365. ':' => return Ok(input),
  366. _ => {
  367. self.serialization.clear();
  368. return Err(());
  369. }
  370. }
  371. }
  372. // EOF before ':'
  373. if self.context == Context::Setter {
  374. Ok(input)
  375. } else {
  376. self.serialization.clear();
  377. Err(())
  378. }
  379. }
  380. fn parse_with_scheme(mut self, input: Input) -> ParseResult<Url> {
  381. use SyntaxViolation::{ExpectedDoubleSlash, ExpectedFileDoubleSlash};
  382. let scheme_end = to_u32(self.serialization.len())?;
  383. let scheme_type = SchemeType::from(&self.serialization);
  384. self.serialization.push(':');
  385. match scheme_type {
  386. SchemeType::File => {
  387. self.log_violation_if(ExpectedFileDoubleSlash, || !input.starts_with("//"));
  388. let base_file_url = self.base_url.and_then(|base| {
  389. if base.scheme() == "file" {
  390. Some(base)
  391. } else {
  392. None
  393. }
  394. });
  395. self.serialization.clear();
  396. self.parse_file(input, scheme_type, base_file_url)
  397. }
  398. SchemeType::SpecialNotFile => {
  399. // special relative or authority state
  400. let (slashes_count, remaining) = input.count_matching(|c| matches!(c, '/' | '\\'));
  401. if let Some(base_url) = self.base_url {
  402. if slashes_count < 2
  403. && base_url.scheme() == &self.serialization[..scheme_end as usize]
  404. {
  405. // "Cannot-be-a-base" URLs only happen with "not special" schemes.
  406. debug_assert!(!base_url.cannot_be_a_base());
  407. self.serialization.clear();
  408. return self.parse_relative(input, scheme_type, base_url);
  409. }
  410. }
  411. // special authority slashes state
  412. self.log_violation_if(ExpectedDoubleSlash, || {
  413. input
  414. .clone()
  415. .take_while(|&c| matches!(c, '/' | '\\'))
  416. .collect::<String>()
  417. != "//"
  418. });
  419. self.after_double_slash(remaining, scheme_type, scheme_end)
  420. }
  421. SchemeType::NotSpecial => self.parse_non_special(input, scheme_type, scheme_end),
  422. }
  423. }
  424. /// Scheme other than file, http, https, ws, ws, ftp, gopher.
  425. fn parse_non_special(
  426. mut self,
  427. input: Input,
  428. scheme_type: SchemeType,
  429. scheme_end: u32,
  430. ) -> ParseResult<Url> {
  431. // path or authority state (
  432. if let Some(input) = input.split_prefix("//") {
  433. return self.after_double_slash(input, scheme_type, scheme_end);
  434. }
  435. // Anarchist URL (no authority)
  436. let path_start = to_u32(self.serialization.len())?;
  437. let username_end = path_start;
  438. let host_start = path_start;
  439. let host_end = path_start;
  440. let host = HostInternal::None;
  441. let port = None;
  442. let remaining = if let Some(input) = input.split_prefix('/') {
  443. let path_start = self.serialization.len();
  444. self.serialization.push('/');
  445. self.parse_path(scheme_type, &mut false, path_start, input)
  446. } else {
  447. self.parse_cannot_be_a_base_path(input)
  448. };
  449. self.with_query_and_fragment(
  450. scheme_type,
  451. scheme_end,
  452. username_end,
  453. host_start,
  454. host_end,
  455. host,
  456. port,
  457. path_start,
  458. remaining,
  459. )
  460. }
  461. fn parse_file(
  462. mut self,
  463. input: Input,
  464. scheme_type: SchemeType,
  465. base_file_url: Option<&Url>,
  466. ) -> ParseResult<Url> {
  467. use SyntaxViolation::Backslash;
  468. // file state
  469. debug_assert!(self.serialization.is_empty());
  470. let (first_char, input_after_first_char) = input.split_first();
  471. if matches!(first_char, Some('/') | Some('\\')) {
  472. self.log_violation_if(SyntaxViolation::Backslash, || first_char == Some('\\'));
  473. // file slash state
  474. let (next_char, input_after_next_char) = input_after_first_char.split_first();
  475. if matches!(next_char, Some('/') | Some('\\')) {
  476. self.log_violation_if(Backslash, || next_char == Some('\\'));
  477. // file host state
  478. self.serialization.push_str("file://");
  479. let scheme_end = "file".len() as u32;
  480. let host_start = "file://".len() as u32;
  481. let (path_start, mut host, remaining) =
  482. self.parse_file_host(input_after_next_char)?;
  483. let mut host_end = to_u32(self.serialization.len())?;
  484. let mut has_host = !matches!(host, HostInternal::None);
  485. let remaining = if path_start {
  486. self.parse_path_start(SchemeType::File, &mut has_host, remaining)
  487. } else {
  488. let path_start = self.serialization.len();
  489. self.serialization.push('/');
  490. self.parse_path(SchemeType::File, &mut has_host, path_start, remaining)
  491. };
  492. // For file URLs that have a host and whose path starts
  493. // with the windows drive letter we just remove the host.
  494. if !has_host {
  495. self.serialization
  496. .drain(host_start as usize..host_end as usize);
  497. host_end = host_start;
  498. host = HostInternal::None;
  499. }
  500. let (query_start, fragment_start) =
  501. self.parse_query_and_fragment(scheme_type, scheme_end, remaining)?;
  502. return Ok(Url {
  503. serialization: self.serialization,
  504. scheme_end: scheme_end,
  505. username_end: host_start,
  506. host_start: host_start,
  507. host_end: host_end,
  508. host: host,
  509. port: None,
  510. path_start: host_end,
  511. query_start: query_start,
  512. fragment_start: fragment_start,
  513. });
  514. } else {
  515. self.serialization.push_str("file://");
  516. let scheme_end = "file".len() as u32;
  517. let host_start = "file://".len();
  518. let mut host_end = host_start;
  519. let mut host = HostInternal::None;
  520. if !starts_with_windows_drive_letter_segment(&input_after_first_char) {
  521. if let Some(base_url) = base_file_url {
  522. let first_segment = base_url.path_segments().unwrap().next().unwrap();
  523. if is_normalized_windows_drive_letter(first_segment) {
  524. self.serialization.push('/');
  525. self.serialization.push_str(first_segment);
  526. } else if let Some(host_str) = base_url.host_str() {
  527. self.serialization.push_str(host_str);
  528. host_end = self.serialization.len();
  529. host = base_url.host.clone();
  530. }
  531. }
  532. }
  533. // If c is the EOF code point, U+002F (/), U+005C (\), U+003F (?), or U+0023 (#), then decrease pointer by one
  534. let parse_path_input = if let Some(c) = first_char {
  535. if c == '/' || c == '\\' || c == '?' || c == '#' {
  536. input
  537. } else {
  538. input_after_first_char
  539. }
  540. } else {
  541. input_after_first_char
  542. };
  543. let remaining =
  544. self.parse_path(SchemeType::File, &mut false, host_end, parse_path_input);
  545. let host_start = host_start as u32;
  546. let (query_start, fragment_start) =
  547. self.parse_query_and_fragment(scheme_type, scheme_end, remaining)?;
  548. let host_end = host_end as u32;
  549. return Ok(Url {
  550. serialization: self.serialization,
  551. scheme_end: scheme_end,
  552. username_end: host_start,
  553. host_start,
  554. host_end,
  555. host,
  556. port: None,
  557. path_start: host_end,
  558. query_start: query_start,
  559. fragment_start: fragment_start,
  560. });
  561. }
  562. }
  563. if let Some(base_url) = base_file_url {
  564. match first_char {
  565. None => {
  566. // Copy everything except the fragment
  567. let before_fragment = match base_url.fragment_start {
  568. Some(i) => &base_url.serialization[..i as usize],
  569. None => &*base_url.serialization,
  570. };
  571. self.serialization.push_str(before_fragment);
  572. Ok(Url {
  573. serialization: self.serialization,
  574. fragment_start: None,
  575. ..*base_url
  576. })
  577. }
  578. Some('?') => {
  579. // Copy everything up to the query string
  580. let before_query = match (base_url.query_start, base_url.fragment_start) {
  581. (None, None) => &*base_url.serialization,
  582. (Some(i), _) | (None, Some(i)) => base_url.slice(..i),
  583. };
  584. self.serialization.push_str(before_query);
  585. let (query_start, fragment_start) =
  586. self.parse_query_and_fragment(scheme_type, base_url.scheme_end, input)?;
  587. Ok(Url {
  588. serialization: self.serialization,
  589. query_start,
  590. fragment_start,
  591. ..*base_url
  592. })
  593. }
  594. Some('#') => self.fragment_only(base_url, input),
  595. _ => {
  596. if !starts_with_windows_drive_letter_segment(&input) {
  597. let before_query = match (base_url.query_start, base_url.fragment_start) {
  598. (None, None) => &*base_url.serialization,
  599. (Some(i), _) | (None, Some(i)) => base_url.slice(..i),
  600. };
  601. self.serialization.push_str(before_query);
  602. self.shorten_path(SchemeType::File, base_url.path_start as usize);
  603. let remaining = self.parse_path(
  604. SchemeType::File,
  605. &mut true,
  606. base_url.path_start as usize,
  607. input,
  608. );
  609. self.with_query_and_fragment(
  610. SchemeType::File,
  611. base_url.scheme_end,
  612. base_url.username_end,
  613. base_url.host_start,
  614. base_url.host_end,
  615. base_url.host,
  616. base_url.port,
  617. base_url.path_start,
  618. remaining,
  619. )
  620. } else {
  621. self.serialization.push_str("file:///");
  622. let scheme_end = "file".len() as u32;
  623. let path_start = "file://".len();
  624. let remaining =
  625. self.parse_path(SchemeType::File, &mut false, path_start, input);
  626. let (query_start, fragment_start) =
  627. self.parse_query_and_fragment(SchemeType::File, scheme_end, remaining)?;
  628. let path_start = path_start as u32;
  629. Ok(Url {
  630. serialization: self.serialization,
  631. scheme_end: scheme_end,
  632. username_end: path_start,
  633. host_start: path_start,
  634. host_end: path_start,
  635. host: HostInternal::None,
  636. port: None,
  637. path_start: path_start,
  638. query_start: query_start,
  639. fragment_start: fragment_start,
  640. })
  641. }
  642. }
  643. }
  644. } else {
  645. self.serialization.push_str("file:///");
  646. let scheme_end = "file".len() as u32;
  647. let path_start = "file://".len();
  648. let remaining = self.parse_path(SchemeType::File, &mut false, path_start, input);
  649. let (query_start, fragment_start) =
  650. self.parse_query_and_fragment(SchemeType::File, scheme_end, remaining)?;
  651. let path_start = path_start as u32;
  652. Ok(Url {
  653. serialization: self.serialization,
  654. scheme_end: scheme_end,
  655. username_end: path_start,
  656. host_start: path_start,
  657. host_end: path_start,
  658. host: HostInternal::None,
  659. port: None,
  660. path_start: path_start,
  661. query_start: query_start,
  662. fragment_start: fragment_start,
  663. })
  664. }
  665. }
  666. fn parse_relative(
  667. mut self,
  668. input: Input,
  669. scheme_type: SchemeType,
  670. base_url: &Url,
  671. ) -> ParseResult<Url> {
  672. // relative state
  673. debug_assert!(self.serialization.is_empty());
  674. let (first_char, input_after_first_char) = input.split_first();
  675. match first_char {
  676. None => {
  677. // Copy everything except the fragment
  678. let before_fragment = match base_url.fragment_start {
  679. Some(i) => &base_url.serialization[..i as usize],
  680. None => &*base_url.serialization,
  681. };
  682. self.serialization.push_str(before_fragment);
  683. Ok(Url {
  684. serialization: self.serialization,
  685. fragment_start: None,
  686. ..*base_url
  687. })
  688. }
  689. Some('?') => {
  690. // Copy everything up to the query string
  691. let before_query = match (base_url.query_start, base_url.fragment_start) {
  692. (None, None) => &*base_url.serialization,
  693. (Some(i), _) | (None, Some(i)) => base_url.slice(..i),
  694. };
  695. self.serialization.push_str(before_query);
  696. let (query_start, fragment_start) =
  697. self.parse_query_and_fragment(scheme_type, base_url.scheme_end, input)?;
  698. Ok(Url {
  699. serialization: self.serialization,
  700. query_start,
  701. fragment_start,
  702. ..*base_url
  703. })
  704. }
  705. Some('#') => self.fragment_only(base_url, input),
  706. Some('/') | Some('\\') => {
  707. let (slashes_count, remaining) = input.count_matching(|c| matches!(c, '/' | '\\'));
  708. if slashes_count >= 2 {
  709. self.log_violation_if(SyntaxViolation::ExpectedDoubleSlash, || {
  710. input
  711. .clone()
  712. .take_while(|&c| matches!(c, '/' | '\\'))
  713. .collect::<String>()
  714. != "//"
  715. });
  716. let scheme_end = base_url.scheme_end;
  717. debug_assert!(base_url.byte_at(scheme_end) == b':');
  718. self.serialization
  719. .push_str(base_url.slice(..scheme_end + 1));
  720. if let Some(after_prefix) = input.split_prefix("//") {
  721. return self.after_double_slash(after_prefix, scheme_type, scheme_end);
  722. }
  723. return self.after_double_slash(remaining, scheme_type, scheme_end);
  724. }
  725. let path_start = base_url.path_start;
  726. self.serialization.push_str(base_url.slice(..path_start));
  727. self.serialization.push_str("/");
  728. let remaining = self.parse_path(
  729. scheme_type,
  730. &mut true,
  731. path_start as usize,
  732. input_after_first_char,
  733. );
  734. self.with_query_and_fragment(
  735. scheme_type,
  736. base_url.scheme_end,
  737. base_url.username_end,
  738. base_url.host_start,
  739. base_url.host_end,
  740. base_url.host,
  741. base_url.port,
  742. base_url.path_start,
  743. remaining,
  744. )
  745. }
  746. _ => {
  747. let before_query = match (base_url.query_start, base_url.fragment_start) {
  748. (None, None) => &*base_url.serialization,
  749. (Some(i), _) | (None, Some(i)) => base_url.slice(..i),
  750. };
  751. self.serialization.push_str(before_query);
  752. // FIXME spec says just "remove last entry", not the "pop" algorithm
  753. self.pop_path(scheme_type, base_url.path_start as usize);
  754. // A special url always has a path.
  755. // A path always starts with '/'
  756. if self.serialization.len() == base_url.path_start as usize {
  757. if SchemeType::from(base_url.scheme()).is_special() || !input.is_empty() {
  758. self.serialization.push('/');
  759. }
  760. }
  761. let remaining = match input.split_first() {
  762. (Some('/'), remaining) => self.parse_path(
  763. scheme_type,
  764. &mut true,
  765. base_url.path_start as usize,
  766. remaining,
  767. ),
  768. _ => {
  769. self.parse_path(scheme_type, &mut true, base_url.path_start as usize, input)
  770. }
  771. };
  772. self.with_query_and_fragment(
  773. scheme_type,
  774. base_url.scheme_end,
  775. base_url.username_end,
  776. base_url.host_start,
  777. base_url.host_end,
  778. base_url.host,
  779. base_url.port,
  780. base_url.path_start,
  781. remaining,
  782. )
  783. }
  784. }
  785. }
  786. fn after_double_slash(
  787. mut self,
  788. input: Input,
  789. scheme_type: SchemeType,
  790. scheme_end: u32,
  791. ) -> ParseResult<Url> {
  792. self.serialization.push('/');
  793. self.serialization.push('/');
  794. // authority state
  795. let before_authority = self.serialization.len();
  796. let (username_end, remaining) = self.parse_userinfo(input, scheme_type)?;
  797. let has_authority = before_authority != self.serialization.len();
  798. // host state
  799. let host_start = to_u32(self.serialization.len())?;
  800. let (host_end, host, port, remaining) =
  801. self.parse_host_and_port(remaining, scheme_end, scheme_type)?;
  802. if host == HostInternal::None && has_authority {
  803. return Err(ParseError::EmptyHost);
  804. }
  805. // path state
  806. let path_start = to_u32(self.serialization.len())?;
  807. let remaining = self.parse_path_start(scheme_type, &mut true, remaining);
  808. self.with_query_and_fragment(
  809. scheme_type,
  810. scheme_end,
  811. username_end,
  812. host_start,
  813. host_end,
  814. host,
  815. port,
  816. path_start,
  817. remaining,
  818. )
  819. }
  820. /// Return (username_end, remaining)
  821. fn parse_userinfo<'i>(
  822. &mut self,
  823. mut input: Input<'i>,
  824. scheme_type: SchemeType,
  825. ) -> ParseResult<(u32, Input<'i>)> {
  826. let mut last_at = None;
  827. let mut remaining = input.clone();
  828. let mut char_count = 0;
  829. while let Some(c) = remaining.next() {
  830. match c {
  831. '@' => {
  832. if last_at.is_some() {
  833. self.log_violation(SyntaxViolation::UnencodedAtSign)
  834. } else {
  835. self.log_violation(SyntaxViolation::EmbeddedCredentials)
  836. }
  837. last_at = Some((char_count, remaining.clone()))
  838. }
  839. '/' | '?' | '#' => break,
  840. '\\' if scheme_type.is_special() => break,
  841. _ => (),
  842. }
  843. char_count += 1;
  844. }
  845. let (mut userinfo_char_count, remaining) = match last_at {
  846. None => return Ok((to_u32(self.serialization.len())?, input)),
  847. Some((0, remaining)) => {
  848. // Otherwise, if one of the following is true
  849. // c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
  850. // url is special and c is U+005C (\)
  851. // If @ flag is set and buffer is the empty string, validation error, return failure.
  852. if let (Some(c), _) = remaining.split_first() {
  853. if c == '/' || c == '?' || c == '#' || scheme_type.is_special() && c == '\\' {
  854. return Err(ParseError::EmptyHost);
  855. }
  856. }
  857. return Ok((to_u32(self.serialization.len())?, remaining));
  858. }
  859. Some(x) => x,
  860. };
  861. let mut username_end = None;
  862. let mut has_password = false;
  863. let mut has_username = false;
  864. while userinfo_char_count > 0 {
  865. let (c, utf8_c) = input.next_utf8().unwrap();
  866. userinfo_char_count -= 1;
  867. if c == ':' && username_end.is_none() {
  868. // Start parsing password
  869. username_end = Some(to_u32(self.serialization.len())?);
  870. // We don't add a colon if the password is empty
  871. if userinfo_char_count > 0 {
  872. self.serialization.push(':');
  873. has_password = true;
  874. }
  875. } else {
  876. if !has_password {
  877. has_username = true;
  878. }
  879. self.check_url_code_point(c, &input);
  880. self.serialization
  881. .extend(utf8_percent_encode(utf8_c, USERINFO));
  882. }
  883. }
  884. let username_end = match username_end {
  885. Some(i) => i,
  886. None => to_u32(self.serialization.len())?,
  887. };
  888. if has_username || has_password {
  889. self.serialization.push('@');
  890. }
  891. Ok((username_end, remaining))
  892. }
  893. fn parse_host_and_port<'i>(
  894. &mut self,
  895. input: Input<'i>,
  896. scheme_end: u32,
  897. scheme_type: SchemeType,
  898. ) -> ParseResult<(u32, HostInternal, Option<u16>, Input<'i>)> {
  899. let (host, remaining) = Parser::parse_host(input, scheme_type)?;
  900. write!(&mut self.serialization, "{}", host).unwrap();
  901. let host_end = to_u32(self.serialization.len())?;
  902. if let Host::Domain(h) = &host {
  903. if h.is_empty() {
  904. // Port with an empty host
  905. if remaining.starts_with(":") {
  906. return Err(ParseError::EmptyHost);
  907. }
  908. if scheme_type.is_special() {
  909. return Err(ParseError::EmptyHost);
  910. }
  911. }
  912. };
  913. let (port, remaining) = if let Some(remaining) = remaining.split_prefix(':') {
  914. let scheme = || default_port(&self.serialization[..scheme_end as usize]);
  915. Parser::parse_port(remaining, scheme, self.context)?
  916. } else {
  917. (None, remaining)
  918. };
  919. if let Some(port) = port {
  920. write!(&mut self.serialization, ":{}", port).unwrap()
  921. }
  922. Ok((host_end, host.into(), port, remaining))
  923. }
  924. pub fn parse_host(
  925. mut input: Input,
  926. scheme_type: SchemeType,
  927. ) -> ParseResult<(Host<String>, Input)> {
  928. if scheme_type.is_file() {
  929. return Parser::get_file_host(input);
  930. }
  931. // Undo the Input abstraction here to avoid allocating in the common case
  932. // where the host part of the input does not contain any tab or newline
  933. let input_str = input.chars.as_str();
  934. let mut inside_square_brackets = false;
  935. let mut has_ignored_chars = false;
  936. let mut non_ignored_chars = 0;
  937. let mut bytes = 0;
  938. for c in input_str.chars() {
  939. match c {
  940. ':' if !inside_square_brackets => break,
  941. '\\' if scheme_type.is_special() => break,
  942. '/' | '?' | '#' => break,
  943. '\t' | '\n' | '\r' => {
  944. has_ignored_chars = true;
  945. }
  946. '[' => {
  947. inside_square_brackets = true;
  948. non_ignored_chars += 1
  949. }
  950. ']' => {
  951. inside_square_brackets = false;
  952. non_ignored_chars += 1
  953. }
  954. _ => non_ignored_chars += 1,
  955. }
  956. bytes += c.len_utf8();
  957. }
  958. let replaced: String;
  959. let host_str;
  960. {
  961. let host_input = input.by_ref().take(non_ignored_chars);
  962. if has_ignored_chars {
  963. replaced = host_input.collect();
  964. host_str = &*replaced
  965. } else {
  966. for _ in host_input {}
  967. host_str = &input_str[..bytes]
  968. }
  969. }
  970. if scheme_type == SchemeType::SpecialNotFile && host_str.is_empty() {
  971. return Err(ParseError::EmptyHost);
  972. }
  973. if !scheme_type.is_special() {
  974. let host = Host::parse_opaque(host_str)?;
  975. return Ok((host, input));
  976. }
  977. let host = Host::parse(host_str)?;
  978. Ok((host, input))
  979. }
  980. fn get_file_host<'i>(input: Input<'i>) -> ParseResult<(Host<String>, Input)> {
  981. let (_, host_str, remaining) = Parser::file_host(input)?;
  982. let host = match Host::parse(&host_str)? {
  983. Host::Domain(ref d) if d == "localhost" => Host::Domain("".to_string()),
  984. host => host,
  985. };
  986. Ok((host, remaining))
  987. }
  988. fn parse_file_host<'i>(
  989. &mut self,
  990. input: Input<'i>,
  991. ) -> ParseResult<(bool, HostInternal, Input<'i>)> {
  992. let has_host;
  993. let (_, host_str, remaining) = Parser::file_host(input)?;
  994. let host = if host_str.is_empty() {
  995. has_host = false;
  996. HostInternal::None
  997. } else {
  998. match Host::parse(&host_str)? {
  999. Host::Domain(ref d) if d == "localhost" => {
  1000. has_host = false;
  1001. HostInternal::None
  1002. }
  1003. host => {
  1004. write!(&mut self.serialization, "{}", host).unwrap();
  1005. has_host = true;
  1006. host.into()
  1007. }
  1008. }
  1009. };
  1010. Ok((has_host, host, remaining))
  1011. }
  1012. pub fn file_host<'i>(input: Input<'i>) -> ParseResult<(bool, String, Input<'i>)> {
  1013. // Undo the Input abstraction here to avoid allocating in the common case
  1014. // where the host part of the input does not contain any tab or newline
  1015. let input_str = input.chars.as_str();
  1016. let mut has_ignored_chars = false;
  1017. let mut non_ignored_chars = 0;
  1018. let mut bytes = 0;
  1019. for c in input_str.chars() {
  1020. match c {
  1021. '/' | '\\' | '?' | '#' => break,
  1022. '\t' | '\n' | '\r' => has_ignored_chars = true,
  1023. _ => non_ignored_chars += 1,
  1024. }
  1025. bytes += c.len_utf8();
  1026. }
  1027. let replaced: String;
  1028. let host_str;
  1029. let mut remaining = input.clone();
  1030. {
  1031. let host_input = remaining.by_ref().take(non_ignored_chars);
  1032. if has_ignored_chars {
  1033. replaced = host_input.collect();
  1034. host_str = &*replaced
  1035. } else {
  1036. for _ in host_input {}
  1037. host_str = &input_str[..bytes]
  1038. }
  1039. }
  1040. if is_windows_drive_letter(host_str) {
  1041. return Ok((false, "".to_string(), input));
  1042. }
  1043. Ok((true, host_str.to_string(), remaining))
  1044. }
  1045. pub fn parse_port<P>(
  1046. mut input: Input,
  1047. default_port: P,
  1048. context: Context,
  1049. ) -> ParseResult<(Option<u16>, Input)>
  1050. where
  1051. P: Fn() -> Option<u16>,
  1052. {
  1053. let mut port: u32 = 0;
  1054. let mut has_any_digit = false;
  1055. while let (Some(c), remaining) = input.split_first() {
  1056. if let Some(digit) = c.to_digit(10) {
  1057. port = port * 10 + digit;
  1058. if port > ::std::u16::MAX as u32 {
  1059. return Err(ParseError::InvalidPort);
  1060. }
  1061. has_any_digit = true;
  1062. } else if context == Context::UrlParser && !matches!(c, '/' | '\\' | '?' | '#') {
  1063. return Err(ParseError::InvalidPort);
  1064. } else {
  1065. break;
  1066. }
  1067. input = remaining;
  1068. }
  1069. let mut opt_port = Some(port as u16);
  1070. if !has_any_digit || opt_port == default_port() {
  1071. opt_port = None;
  1072. }
  1073. Ok((opt_port, input))
  1074. }
  1075. pub fn parse_path_start<'i>(
  1076. &mut self,
  1077. scheme_type: SchemeType,
  1078. has_host: &mut bool,
  1079. input: Input<'i>,
  1080. ) -> Input<'i> {
  1081. let path_start = self.serialization.len();
  1082. let (maybe_c, remaining) = input.split_first();
  1083. // If url is special, then:
  1084. if scheme_type.is_special() {
  1085. if maybe_c == Some('\\') {
  1086. // If c is U+005C (\), validation error.
  1087. self.log_violation(SyntaxViolation::Backslash);
  1088. }
  1089. // A special URL always has a non-empty path.
  1090. if !self.serialization.ends_with("/") {
  1091. self.serialization.push('/');
  1092. // We have already made sure the forward slash is present.
  1093. if maybe_c == Some('/') || maybe_c == Some('\\') {
  1094. return self.parse_path(scheme_type, has_host, path_start, remaining);
  1095. }
  1096. }
  1097. return self.parse_path(scheme_type, has_host, path_start, input);
  1098. } else if maybe_c == Some('?') || maybe_c == Some('#') {
  1099. // Otherwise, if state override is not given and c is U+003F (?),
  1100. // set url’s query to the empty string and state to query state.
  1101. // Otherwise, if state override is not given and c is U+0023 (#),
  1102. // set url’s fragment to the empty string and state to fragment state.
  1103. // The query and path states will be handled by the caller.
  1104. return input;
  1105. }
  1106. // Otherwise, if c is not the EOF code point:
  1107. self.parse_path(scheme_type, has_host, path_start, input)
  1108. }
  1109. pub fn parse_path<'i>(
  1110. &mut self,
  1111. scheme_type: SchemeType,
  1112. has_host: &mut bool,
  1113. path_start: usize,
  1114. mut input: Input<'i>,
  1115. ) -> Input<'i> {
  1116. // Relative path state
  1117. loop {
  1118. let segment_start = self.serialization.len();
  1119. let mut ends_with_slash = false;
  1120. loop {
  1121. let input_before_c = input.clone();
  1122. let (c, utf8_c) = if let Some(x) = input.next_utf8() {
  1123. x
  1124. } else {
  1125. break;
  1126. };
  1127. match c {
  1128. '/' if self.context != Context::PathSegmentSetter => {
  1129. self.serialization.push(c);
  1130. ends_with_slash = true;
  1131. break;
  1132. }
  1133. '\\' if self.context != Context::PathSegmentSetter
  1134. && scheme_type.is_special() =>
  1135. {
  1136. self.log_violation(SyntaxViolation::Backslash);
  1137. self.serialization.push('/');
  1138. ends_with_slash = true;
  1139. break;
  1140. }
  1141. '?' | '#' if self.context == Context::UrlParser => {
  1142. input = input_before_c;
  1143. break;
  1144. }
  1145. _ => {
  1146. self.check_url_code_point(c, &input);
  1147. if self.context == Context::PathSegmentSetter {
  1148. if scheme_type.is_special() {
  1149. self.serialization
  1150. .extend(utf8_percent_encode(utf8_c, SPECIAL_PATH_SEGMENT));
  1151. } else {
  1152. self.serialization
  1153. .extend(utf8_percent_encode(utf8_c, PATH_SEGMENT));
  1154. }
  1155. } else {
  1156. self.serialization.extend(utf8_percent_encode(utf8_c, PATH));
  1157. }
  1158. }
  1159. }
  1160. }
  1161. // Going from &str to String to &str to please the 1.33.0 borrow checker
  1162. let before_slash_string = if ends_with_slash {
  1163. self.serialization[segment_start..self.serialization.len() - 1].to_owned()
  1164. } else {
  1165. self.serialization[segment_start..self.serialization.len()].to_owned()
  1166. };
  1167. let segment_before_slash: &str = &before_slash_string;
  1168. match segment_before_slash {
  1169. // If buffer is a double-dot path segment, shorten url’s path,
  1170. ".." | "%2e%2e" | "%2e%2E" | "%2E%2e" | "%2E%2E" | "%2e." | "%2E." | ".%2e"
  1171. | ".%2E" => {
  1172. debug_assert!(self.serialization.as_bytes()[segment_start - 1] == b'/');
  1173. self.serialization.truncate(segment_start);
  1174. if self.serialization.ends_with("/")
  1175. && Parser::last_slash_can_be_removed(&self.serialization, path_start)
  1176. {
  1177. self.serialization.pop();
  1178. }
  1179. self.shorten_path(scheme_type, path_start);
  1180. // and then if neither c is U+002F (/), nor url is special and c is U+005C (\), append the empty string to url’s path.
  1181. if ends_with_slash && !self.serialization.ends_with("/") {
  1182. self.serialization.push('/');
  1183. }
  1184. }
  1185. // Otherwise, if buffer is a single-dot path segment and if neither c is U+002F (/),
  1186. // nor url is special and c is U+005C (\), append the empty string to url’s path.
  1187. "." | "%2e" | "%2E" => {
  1188. self.serialization.truncate(segment_start);
  1189. if !self.serialization.ends_with("/") {
  1190. self.serialization.push('/');
  1191. }
  1192. }
  1193. _ => {
  1194. // If url’s scheme is "file", url’s path is empty, and buffer is a Windows drive letter, then
  1195. if scheme_type.is_file() && is_windows_drive_letter(segment_before_slash) {
  1196. // Replace the second code point in buffer with U+003A (:).
  1197. if let Some(c) = segment_before_slash.chars().nth(0) {
  1198. self.serialization.truncate(segment_start);
  1199. self.serialization.push(c);
  1200. self.serialization.push(':');
  1201. if ends_with_slash {
  1202. self.serialization.push('/');
  1203. }
  1204. }
  1205. // If url’s host is neither the empty string nor null,
  1206. // validation error, set url’s host to the empty string.
  1207. if *has_host {
  1208. self.log_violation(SyntaxViolation::FileWithHostAndWindowsDrive);
  1209. *has_host = false; // FIXME account for this in callers
  1210. }
  1211. }
  1212. }
  1213. }
  1214. if !ends_with_slash {
  1215. break;
  1216. }
  1217. }
  1218. if scheme_type.is_file() {
  1219. // while url’s path’s size is greater than 1
  1220. // and url’s path[0] is the empty string,
  1221. // validation error, remove the first item from url’s path.
  1222. //FIXME: log violation
  1223. let path = self.serialization.split_off(path_start);
  1224. self.serialization.push('/');
  1225. self.serialization.push_str(&path.trim_start_matches("/"));
  1226. }
  1227. input
  1228. }
  1229. fn last_slash_can_be_removed(serialization: &String, path_start: usize) -> bool {
  1230. let url_before_segment = &serialization[..serialization.len() - 1];
  1231. if let Some(segment_before_start) = url_before_segment.rfind("/") {
  1232. // Do not remove the root slash
  1233. segment_before_start >= path_start
  1234. // Or a windows drive letter slash
  1235. && !path_starts_with_windows_drive_letter(&serialization[segment_before_start..])
  1236. } else {
  1237. false
  1238. }
  1239. }
  1240. /// https://url.spec.whatwg.org/#shorten-a-urls-path
  1241. fn shorten_path(&mut self, scheme_type: SchemeType, path_start: usize) {
  1242. // If path is empty, then return.
  1243. if self.serialization.len() == path_start {
  1244. return;
  1245. }
  1246. {
  1247. // If url’s scheme is "file", path’s size is 1, and path[0] is a normalized Windows drive letter, then return.
  1248. let segments: Vec<&str> = self.serialization[path_start..]
  1249. .split('/')
  1250. .filter(|s| !s.is_empty())
  1251. .collect();
  1252. if scheme_type.is_file()
  1253. && segments.len() == 1
  1254. && is_normalized_windows_drive_letter(segments[0])
  1255. {
  1256. return;
  1257. }
  1258. }
  1259. // Remove path’s last item.
  1260. self.pop_path(scheme_type, path_start);
  1261. }
  1262. /// https://url.spec.whatwg.org/#pop-a-urls-path
  1263. fn pop_path(&mut self, scheme_type: SchemeType, path_start: usize) {
  1264. if self.serialization.len() > path_start {
  1265. let slash_position = self.serialization[path_start..].rfind('/').unwrap();
  1266. // + 1 since rfind returns the position before the slash.
  1267. let segment_start = path_start + slash_position + 1;
  1268. // Don’t pop a Windows drive letter
  1269. if !(scheme_type.is_file()
  1270. && is_normalized_windows_drive_letter(&self.serialization[segment_start..]))
  1271. {
  1272. self.serialization.truncate(segment_start);
  1273. }
  1274. }
  1275. }
  1276. pub fn parse_cannot_be_a_base_path<'i>(&mut self, mut input: Input<'i>) -> Input<'i> {
  1277. loop {
  1278. let input_before_c = input.clone();
  1279. match input.next_utf8() {
  1280. Some(('?', _)) | Some(('#', _)) if self.context == Context::UrlParser => {
  1281. return input_before_c
  1282. }
  1283. Some((c, utf8_c)) => {
  1284. self.check_url_code_point(c, &input);
  1285. self.serialization
  1286. .extend(utf8_percent_encode(utf8_c, CONTROLS));
  1287. }
  1288. None => return input,
  1289. }
  1290. }
  1291. }
  1292. fn with_query_and_fragment(
  1293. mut self,
  1294. scheme_type: SchemeType,
  1295. scheme_end: u32,
  1296. username_end: u32,
  1297. host_start: u32,
  1298. host_end: u32,
  1299. host: HostInternal,
  1300. port: Option<u16>,
  1301. path_start: u32,
  1302. remaining: Input,
  1303. ) -> ParseResult<Url> {
  1304. let (query_start, fragment_start) =
  1305. self.parse_query_and_fragment(scheme_type, scheme_end, remaining)?;
  1306. Ok(Url {
  1307. serialization: self.serialization,
  1308. scheme_end,
  1309. username_end,
  1310. host_start,
  1311. host_end,
  1312. host,
  1313. port,
  1314. path_start,
  1315. query_start,
  1316. fragment_start,
  1317. })
  1318. }
  1319. /// Return (query_start, fragment_start)
  1320. fn parse_query_and_fragment(
  1321. &mut self,
  1322. scheme_type: SchemeType,
  1323. scheme_end: u32,
  1324. mut input: Input,
  1325. ) -> ParseResult<(Option<u32>, Option<u32>)> {
  1326. let mut query_start = None;
  1327. match input.next() {
  1328. Some('#') => {}
  1329. Some('?') => {
  1330. query_start = Some(to_u32(self.serialization.len())?);
  1331. self.serialization.push('?');
  1332. let remaining = self.parse_query(scheme_type, scheme_end, input);
  1333. if let Some(remaining) = remaining {
  1334. input = remaining
  1335. } else {
  1336. return Ok((query_start, None));
  1337. }
  1338. }
  1339. None => return Ok((None, None)),
  1340. _ => panic!("Programming error. parse_query_and_fragment() called without ? or #"),
  1341. }
  1342. let fragment_start = to_u32(self.serialization.len())?;
  1343. self.serialization.push('#');
  1344. self.parse_fragment(input);
  1345. Ok((query_start, Some(fragment_start)))
  1346. }
  1347. pub fn parse_query<'i>(
  1348. &mut self,
  1349. scheme_type: SchemeType,
  1350. scheme_end: u32,
  1351. mut input: Input<'i>,
  1352. ) -> Option<Input<'i>> {
  1353. let mut query = String::new(); // FIXME: use a streaming decoder instead
  1354. let mut remaining = None;
  1355. while let Some(c) = input.next() {
  1356. if c == '#' && self.context == Context::UrlParser {
  1357. remaining = Some(input);
  1358. break;
  1359. } else {
  1360. self.check_url_code_point(c, &input);
  1361. query.push(c);
  1362. }
  1363. }
  1364. let encoding = match &self.serialization[..scheme_end as usize] {
  1365. "http" | "https" | "file" | "ftp" | "gopher" => self.query_encoding_override,
  1366. _ => None,
  1367. };
  1368. let query_bytes = ::query_encoding::encode(encoding, &query);
  1369. let set = if scheme_type.is_special() {
  1370. SPECIAL_QUERY
  1371. } else {
  1372. QUERY
  1373. };
  1374. self.serialization.extend(percent_encode(&query_bytes, set));
  1375. remaining
  1376. }
  1377. fn fragment_only(mut self, base_url: &Url, mut input: Input) -> ParseResult<Url> {
  1378. let before_fragment = match base_url.fragment_start {
  1379. Some(i) => base_url.slice(..i),
  1380. None => &*base_url.serialization,
  1381. };
  1382. debug_assert!(self.serialization.is_empty());
  1383. self.serialization
  1384. .reserve(before_fragment.len() + input.chars.as_str().len());
  1385. self.serialization.push_str(before_fragment);
  1386. self.serialization.push('#');
  1387. let next = input.next();
  1388. debug_assert!(next == Some('#'));
  1389. self.parse_fragment(input);
  1390. Ok(Url {
  1391. serialization: self.serialization,
  1392. fragment_start: Some(to_u32(before_fragment.len())?),
  1393. ..*base_url
  1394. })
  1395. }
  1396. pub fn parse_fragment(&mut self, mut input: Input) {
  1397. while let Some((c, utf8_c)) = input.next_utf8() {
  1398. if c == '\0' {
  1399. self.log_violation(SyntaxViolation::NullInFragment)
  1400. } else {
  1401. self.check_url_code_point(c, &input);
  1402. self.serialization
  1403. .extend(utf8_percent_encode(utf8_c, FRAGMENT));
  1404. }
  1405. }
  1406. }
  1407. fn check_url_code_point(&self, c: char, input: &Input) {
  1408. if let Some(vfn) = self.violation_fn {
  1409. if c == '%' {
  1410. let mut input = input.clone();
  1411. if !matches!((input.next(), input.next()), (Some(a), Some(b))
  1412. if is_ascii_hex_digit(a) && is_ascii_hex_digit(b))
  1413. {
  1414. vfn(SyntaxViolation::PercentDecode)
  1415. }
  1416. } else if !is_url_code_point(c) {
  1417. vfn(SyntaxViolation::NonUrlCodePoint)
  1418. }
  1419. }
  1420. }
  1421. }
  1422. #[inline]
  1423. fn is_ascii_hex_digit(c: char) -> bool {
  1424. matches!(c, 'a'..='f' | 'A'..='F' | '0'..='9')
  1425. }
  1426. // Non URL code points:
  1427. // U+0000 to U+0020 (space)
  1428. // " # % < > [ \ ] ^ ` { | }
  1429. // U+007F to U+009F
  1430. // surrogates
  1431. // U+FDD0 to U+FDEF
  1432. // Last two of each plane: U+__FFFE to U+__FFFF for __ in 00 to 10 hex
  1433. #[inline]
  1434. fn is_url_code_point(c: char) -> bool {
  1435. matches!(c,
  1436. 'a'..='z' |
  1437. 'A'..='Z' |
  1438. '0'..='9' |
  1439. '!' | '$' | '&' | '\'' | '(' | ')' | '*' | '+' | ',' | '-' |
  1440. '.' | '/' | ':' | ';' | '=' | '?' | '@' | '_' | '~' |
  1441. '\u{A0}'..='\u{D7FF}' | '\u{E000}'..='\u{FDCF}' | '\u{FDF0}'..='\u{FFFD}' |
  1442. '\u{10000}'..='\u{1FFFD}' | '\u{20000}'..='\u{2FFFD}' |
  1443. '\u{30000}'..='\u{3FFFD}' | '\u{40000}'..='\u{4FFFD}' |
  1444. '\u{50000}'..='\u{5FFFD}' | '\u{60000}'..='\u{6FFFD}' |
  1445. '\u{70000}'..='\u{7FFFD}' | '\u{80000}'..='\u{8FFFD}' |
  1446. '\u{90000}'..='\u{9FFFD}' | '\u{A0000}'..='\u{AFFFD}' |
  1447. '\u{B0000}'..='\u{BFFFD}' | '\u{C0000}'..='\u{CFFFD}' |
  1448. '\u{D0000}'..='\u{DFFFD}' | '\u{E1000}'..='\u{EFFFD}' |
  1449. '\u{F0000}'..='\u{FFFFD}' | '\u{100000}'..='\u{10FFFD}')
  1450. }
  1451. /// https://url.spec.whatwg.org/#c0-controls-and-space
  1452. #[inline]
  1453. fn c0_control_or_space(ch: char) -> bool {
  1454. ch <= ' ' // U+0000 to U+0020
  1455. }
  1456. /// https://infra.spec.whatwg.org/#ascii-tab-or-newline
  1457. #[inline]
  1458. fn ascii_tab_or_new_line(ch: char) -> bool {
  1459. matches!(ch, '\t' | '\r' | '\n')
  1460. }
  1461. /// https://url.spec.whatwg.org/#ascii-alpha
  1462. #[inline]
  1463. pub fn ascii_alpha(ch: char) -> bool {
  1464. matches!(ch, 'a'..='z' | 'A'..='Z')
  1465. }
  1466. #[inline]
  1467. pub fn to_u32(i: usize) -> ParseResult<u32> {
  1468. if i <= ::std::u32::MAX as usize {
  1469. Ok(i as u32)
  1470. } else {
  1471. Err(ParseError::Overflow)
  1472. }
  1473. }
  1474. fn is_normalized_windows_drive_letter(segment: &str) -> bool {
  1475. is_windows_drive_letter(segment) && segment.as_bytes()[1] == b':'
  1476. }
  1477. /// Wether the scheme is file:, the path has a single segment, and that segment
  1478. /// is a Windows drive letter
  1479. #[inline]
  1480. pub fn is_windows_drive_letter(segment: &str) -> bool {
  1481. segment.len() == 2 && starts_with_windows_drive_letter(segment)
  1482. }
  1483. /// Wether path starts with a root slash
  1484. /// and a windows drive letter eg: "/c:" or "/a:/"
  1485. fn path_starts_with_windows_drive_letter(s: &str) -> bool {
  1486. if let Some(c) = s.as_bytes().get(0) {
  1487. matches!(c, b'/' | b'\\' | b'?' | b'#') && starts_with_windows_drive_letter(&s[1..])
  1488. } else {
  1489. false
  1490. }
  1491. }
  1492. fn starts_with_windows_drive_letter(s: &str) -> bool {
  1493. s.len() >= 2
  1494. && ascii_alpha(s.as_bytes()[0] as char)
  1495. && matches!(s.as_bytes()[1], b':' | b'|')
  1496. && (s.len() == 2 || matches!(s.as_bytes()[2], b'/' | b'\\' | b'?' | b'#'))
  1497. }
  1498. /// https://url.spec.whatwg.org/#start-with-a-windows-drive-letter
  1499. fn starts_with_windows_drive_letter_segment(input: &Input) -> bool {
  1500. let mut input = input.clone();
  1501. match (input.next(), input.next(), input.next()) {
  1502. // its first two code points are a Windows drive letter
  1503. // its third code point is U+002F (/), U+005C (\), U+003F (?), or U+0023 (#).
  1504. (Some(a), Some(b), Some(c))
  1505. if ascii_alpha(a) && matches!(b, ':' | '|') && matches!(c, '/' | '\\' | '?' | '#') =>
  1506. {
  1507. true
  1508. }
  1509. // its first two code points are a Windows drive letter
  1510. // its length is 2
  1511. (Some(a), Some(b), None) if ascii_alpha(a) && matches!(b, ':' | '|') => true,
  1512. _ => false,
  1513. }
  1514. }