parser.rs 46 KB

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