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