parser.rs 50 KB

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