lib.rs 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012
  1. // Copyright 2013-2015 Simon Sapin.
  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. /*!
  9. <a href="https://github.com/servo/rust-url"><img style="position: absolute; top: 0; left: 0; border: 0;" src="../github.png" alt="Fork me on GitHub"></a>
  10. <style>.sidebar { margin-top: 53px }</style>
  11. rust-url is an implementation of the [URL Standard](http://url.spec.whatwg.org/)
  12. for the [Rust](http://rust-lang.org/) programming language.
  13. It builds with [Cargo](http://crates.io/).
  14. To use it in your project, add this to your `Cargo.toml` file:
  15. ```Cargo
  16. [dependencies.url]
  17. git = "https://github.com/servo/rust-url"
  18. ```
  19. Supporting encodings other than UTF-8 in query strings is an optional feature
  20. that requires [rust-encoding](https://github.com/lifthrasiir/rust-encoding)
  21. and is off by default.
  22. You can enable it with
  23. [Cargo’s *features* mechanism](http://doc.crates.io/manifest.html#the-[features]-section):
  24. ```Cargo
  25. [dependencies.url]
  26. git = "https://github.com/servo/rust-url"
  27. features = ["query_encoding"]
  28. ```
  29. … or by passing `--cfg 'feature="query_encoding"'` to rustc.
  30. # URL parsing and data structures
  31. First, URL parsing may fail for various reasons and therefore returns a `Result`.
  32. ```
  33. use url::{Url, ParseError};
  34. assert!(Url::parse("http://[:::1]") == Err(ParseError::InvalidIpv6Address))
  35. ```
  36. Let’s parse a valid URL and look at its components.
  37. ```
  38. use url::{Url, SchemeData};
  39. let issue_list_url = Url::parse(
  40. "https://github.com/rust-lang/rust/issues?labels=E-easy&state=open"
  41. ).unwrap();
  42. assert!(issue_list_url.scheme == "https".to_string());
  43. assert!(issue_list_url.domain() == Some("github.com"));
  44. assert!(issue_list_url.port() == None);
  45. assert!(issue_list_url.path() == Some(&["rust-lang".to_string(),
  46. "rust".to_string(),
  47. "issues".to_string()][..]));
  48. assert!(issue_list_url.query == Some("labels=E-easy&state=open".to_string()));
  49. assert!(issue_list_url.fragment == None);
  50. match issue_list_url.scheme_data {
  51. SchemeData::Relative(..) => {}, // Expected
  52. SchemeData::NonRelative(..) => panic!(),
  53. }
  54. ```
  55. The `scheme`, `query`, and `fragment` are directly fields of the `Url` struct:
  56. they apply to all URLs.
  57. Every other components has accessors because they only apply to URLs said to be
  58. “in a relative scheme”. `https` is a relative scheme, but `data` is not:
  59. ```
  60. use url::{Url, SchemeData};
  61. let data_url = Url::parse("data:text/plain,Hello#").unwrap();
  62. assert!(data_url.scheme == "data".to_string());
  63. assert!(data_url.scheme_data == SchemeData::NonRelative("text/plain,Hello".to_string()));
  64. assert!(data_url.non_relative_scheme_data() == Some("text/plain,Hello"));
  65. assert!(data_url.query == None);
  66. assert!(data_url.fragment == Some("".to_string()));
  67. ```
  68. # Base URL
  69. Many contexts allow URL *references* that can be relative to a *base URL*:
  70. ```html
  71. <link rel="stylesheet" href="../main.css">
  72. ```
  73. Since parsed URL are absolute, giving a base is required:
  74. ```
  75. use url::{Url, ParseError};
  76. assert!(Url::parse("../main.css") == Err(ParseError::RelativeUrlWithoutBase))
  77. ```
  78. `UrlParser` is a method-chaining API to provide various optional parameters
  79. to URL parsing, including a base URL.
  80. ```
  81. use url::{Url, UrlParser};
  82. let this_document = Url::parse("http://servo.github.io/rust-url/url/index.html").unwrap();
  83. let css_url = UrlParser::new().base_url(&this_document).parse("../main.css").unwrap();
  84. assert!(css_url.serialize() == "http://servo.github.io/rust-url/main.css".to_string());
  85. ```
  86. */
  87. extern crate rustc_serialize;
  88. #[macro_use]
  89. extern crate matches;
  90. use std::fmt::{self, Formatter};
  91. use std::hash;
  92. use std::path::{Path, PathBuf};
  93. pub use host::{Host, Ipv6Address};
  94. pub use parser::{ErrorHandler, ParseResult, ParseError};
  95. #[deprecated = "Moved to the `percent_encoding` module"]
  96. pub use percent_encoding::{
  97. percent_decode, percent_decode_to, percent_encode, percent_encode_to,
  98. utf8_percent_encode, utf8_percent_encode_to, lossy_utf8_percent_decode,
  99. SIMPLE_ENCODE_SET, QUERY_ENCODE_SET, DEFAULT_ENCODE_SET, USERINFO_ENCODE_SET,
  100. PASSWORD_ENCODE_SET, USERNAME_ENCODE_SET, FORM_URLENCODED_ENCODE_SET, EncodeSet,
  101. };
  102. use format::{PathFormatter, UserInfoFormatter, UrlNoFragmentFormatter};
  103. use encoding::EncodingOverride;
  104. mod encoding;
  105. mod host;
  106. mod parser;
  107. pub mod urlutils;
  108. pub mod percent_encoding;
  109. pub mod form_urlencoded;
  110. pub mod punycode;
  111. pub mod format;
  112. #[cfg(test)]
  113. mod tests;
  114. /// The parsed representation of an absolute URL.
  115. #[derive(PartialEq, Eq, Clone, Debug)]
  116. pub struct Url {
  117. /// The scheme (a.k.a. protocol) of the URL, in ASCII lower case.
  118. pub scheme: String,
  119. /// The components of the URL whose representation depends on where the scheme is *relative*.
  120. pub scheme_data: SchemeData,
  121. /// The query string of the URL.
  122. ///
  123. /// `None` if the `?` delimiter character was not part of the parsed input,
  124. /// otherwise a possibly empty, pecent-encoded string.
  125. ///
  126. /// Percent encoded strings are within the ASCII range.
  127. ///
  128. /// See also the `query_pairs`, `set_query_from_pairs`,
  129. /// and `lossy_percent_decode_query` methods.
  130. pub query: Option<String>,
  131. /// The fragment identifier of the URL.
  132. ///
  133. /// `None` if the `#` delimiter character was not part of the parsed input,
  134. /// otherwise a possibly empty, pecent-encoded string.
  135. ///
  136. /// Percent encoded strings are within the ASCII range.
  137. ///
  138. /// See also the `lossy_percent_decode_fragment` method.
  139. pub fragment: Option<String>,
  140. }
  141. /// The components of the URL whose representation depends on where the scheme is *relative*.
  142. #[derive(PartialEq, Eq, Clone, Debug)]
  143. pub enum SchemeData {
  144. /// Components for URLs in a *relative* scheme such as HTTP.
  145. Relative(RelativeSchemeData),
  146. /// No further structure is assumed for *non-relative* schemes such as `data` and `mailto`.
  147. ///
  148. /// This is a single percent-encoded string, whose interpretation depends on the scheme.
  149. ///
  150. /// Percent encoded strings are within the ASCII range.
  151. NonRelative(String),
  152. }
  153. /// Components for URLs in a *relative* scheme such as HTTP.
  154. #[derive(PartialEq, Eq, Clone, Debug)]
  155. pub struct RelativeSchemeData {
  156. /// The username of the URL, as a possibly empty, pecent-encoded string.
  157. ///
  158. /// Percent encoded strings are within the ASCII range.
  159. ///
  160. /// See also the `lossy_percent_decode_username` method.
  161. pub username: String,
  162. /// The password of the URL.
  163. ///
  164. /// `None` if the `:` delimiter character was not part of the parsed input,
  165. /// otherwise a possibly empty, pecent-encoded string.
  166. ///
  167. /// Percent encoded strings are within the ASCII range.
  168. ///
  169. /// See also the `lossy_percent_decode_password` method.
  170. pub password: Option<String>,
  171. /// The host of the URL, either a domain name or an IPv4 address
  172. pub host: Host,
  173. /// The port number of the URL.
  174. /// `None` for file-like schemes, or to indicate the default port number.
  175. pub port: Option<u16>,
  176. /// The default port number for the URL’s scheme.
  177. /// `None` for file-like schemes.
  178. pub default_port: Option<u16>,
  179. /// The path of the URL, as vector of pecent-encoded strings.
  180. ///
  181. /// Percent encoded strings are within the ASCII range.
  182. ///
  183. /// See also the `serialize_path` method and,
  184. /// for URLs in the `file` scheme, the `to_file_path` method.
  185. pub path: Vec<String>,
  186. }
  187. impl hash::Hash for Url {
  188. fn hash<H: hash::Hasher>(&self, state: &mut H) {
  189. self.serialize().hash(state)
  190. }
  191. }
  192. /// A set of optional parameters for URL parsing.
  193. pub struct UrlParser<'a> {
  194. base_url: Option<&'a Url>,
  195. query_encoding_override: EncodingOverride,
  196. error_handler: ErrorHandler,
  197. scheme_type_mapper: fn(scheme: &str) -> SchemeType,
  198. }
  199. /// A method-chaining API to provide a set of optional parameters for URL parsing.
  200. impl<'a> UrlParser<'a> {
  201. /// Return a new UrlParser with default parameters.
  202. #[inline]
  203. pub fn new() -> UrlParser<'a> {
  204. fn silent_handler(_reason: ParseError) -> ParseResult<()> { Ok(()) }
  205. UrlParser {
  206. base_url: None,
  207. query_encoding_override: EncodingOverride::utf8(),
  208. error_handler: silent_handler,
  209. scheme_type_mapper: whatwg_scheme_type_mapper,
  210. }
  211. }
  212. /// Set the base URL used for resolving relative URL references, and return the `UrlParser`.
  213. /// The default is no base URL, so that relative URLs references fail to parse.
  214. #[inline]
  215. pub fn base_url<'b>(&'b mut self, value: &'a Url) -> &'b mut UrlParser<'a> {
  216. self.base_url = Some(value);
  217. self
  218. }
  219. /// Set the character encoding the query string is encoded as before percent-encoding,
  220. /// and return the `UrlParser`.
  221. ///
  222. /// This legacy quirk is only relevant to HTML.
  223. ///
  224. /// This method is only available if the `query_encoding` Cargo feature is enabled.
  225. #[cfg(feature = "query_encoding")]
  226. #[inline]
  227. pub fn query_encoding_override<'b>(&'b mut self, value: encoding::EncodingRef)
  228. -> &'b mut UrlParser<'a> {
  229. self.query_encoding_override = EncodingOverride::from_encoding(value);
  230. self
  231. }
  232. /// Set an error handler for non-fatal parse errors, and return the `UrlParser`.
  233. ///
  234. /// Non-fatal parse errors are normally ignored by the parser,
  235. /// but indicate violations of authoring requirements.
  236. /// An error handler can be used, for example, to log these errors in the console
  237. /// of a browser’s developer tools.
  238. ///
  239. /// The error handler can choose to make the error fatal by returning `Err(..)`
  240. #[inline]
  241. pub fn error_handler<'b>(&'b mut self, value: ErrorHandler) -> &'b mut UrlParser<'a> {
  242. self.error_handler = value;
  243. self
  244. }
  245. /// Set a *scheme type mapper*, and return the `UrlParser`.
  246. ///
  247. /// The URL parser behaves differently based on the `SchemeType` of the URL.
  248. /// See the documentation for `SchemeType` for more details.
  249. /// A *scheme type mapper* returns a `SchemeType`
  250. /// based on the scheme as an ASCII lower case string,
  251. /// as found in the `scheme` field of an `Url` struct.
  252. ///
  253. /// The default scheme type mapper is as follows:
  254. ///
  255. /// ```ignore
  256. /// fn whatwg_scheme_type_mapper(scheme: &str) -> SchemeType {
  257. /// match scheme {
  258. /// "file" => SchemeType::FileLike,
  259. /// "ftp" => SchemeType::Relative(21),
  260. /// "gopher" => SchemeType::Relative(70),
  261. /// "http" => SchemeType::Relative(80),
  262. /// "https" => SchemeType::Relative(443),
  263. /// "ws" => SchemeType::Relative(80),
  264. /// "wss" => SchemeType::Relative(443),
  265. /// _ => NonRelative,
  266. /// }
  267. /// }
  268. /// ```
  269. ///
  270. /// Note that unknown schemes default to non-relative.
  271. /// Overriding the scheme type mapper can allow, for example,
  272. /// parsing URLs in the `git` or `irc` scheme as relative.
  273. #[inline]
  274. pub fn scheme_type_mapper<'b>(&'b mut self, value: fn(scheme: &str) -> SchemeType)
  275. -> &'b mut UrlParser<'a> {
  276. self.scheme_type_mapper = value;
  277. self
  278. }
  279. /// Parse `input` as an URL, with all the parameters previously set in the `UrlParser`.
  280. #[inline]
  281. pub fn parse(&self, input: &str) -> ParseResult<Url> {
  282. parser::parse_url(input, self)
  283. }
  284. /// Parse `input` as a “standalone” URL path,
  285. /// with an optional query string and fragment identifier.
  286. ///
  287. /// This is typically found in the start line of an HTTP header.
  288. ///
  289. /// Note that while the start line has no fragment identifier in the HTTP RFC,
  290. /// servers typically parse it and ignore it
  291. /// (rather than having it be part of the path or query string.)
  292. ///
  293. /// On success, return `(path, query_string, fragment_identifier)`
  294. #[inline]
  295. pub fn parse_path(&self, input: &str)
  296. -> ParseResult<(Vec<String>, Option<String>, Option<String>)> {
  297. parser::parse_standalone_path(input, self)
  298. }
  299. }
  300. /// Parse `input` as a “standalone” URL path,
  301. /// with an optional query string and fragment identifier.
  302. ///
  303. /// This is typically found in the start line of an HTTP header.
  304. ///
  305. /// Note that while the start line has no fragment identifier in the HTTP RFC,
  306. /// servers typically parse it and ignore it
  307. /// (rather than having it be part of the path or query string.)
  308. ///
  309. /// On success, return `(path, query_string, fragment_identifier)`
  310. ///
  311. /// ```rust
  312. /// let (path, query, fragment) = url::parse_path("/foo/bar/../baz?q=42").unwrap();
  313. /// assert_eq!(path, vec!["foo".to_string(), "baz".to_string()]);
  314. /// assert_eq!(query, Some("q=42".to_string()));
  315. /// assert_eq!(fragment, None);
  316. /// ```
  317. #[inline]
  318. pub fn parse_path(input: &str)
  319. -> ParseResult<(Vec<String>, Option<String>, Option<String>)> {
  320. UrlParser::new().parse_path(input)
  321. }
  322. /// Private convenience methods for use in parser.rs
  323. impl<'a> UrlParser<'a> {
  324. #[inline]
  325. fn parse_error(&self, error: ParseError) -> ParseResult<()> {
  326. (self.error_handler)(error)
  327. }
  328. #[inline]
  329. fn get_scheme_type(&self, scheme: &str) -> SchemeType {
  330. (self.scheme_type_mapper)(scheme)
  331. }
  332. }
  333. /// Determines the behavior of the URL parser for a given scheme.
  334. #[derive(PartialEq, Eq, Copy, Debug, Clone)]
  335. pub enum SchemeType {
  336. /// Indicate that the scheme is *non-relative*.
  337. ///
  338. /// The *scheme data* of the URL
  339. /// (everything other than the scheme, query string, and fragment identifier)
  340. /// is parsed as a single percent-encoded string of which no structure is assumed.
  341. /// That string may need to be parsed further, per a scheme-specific format.
  342. NonRelative,
  343. /// Indicate that the scheme is *relative*, and what the default port number is.
  344. ///
  345. /// The *scheme data* is structured as
  346. /// *username*, *password*, *host*, *port number*, and *path*.
  347. /// Relative URL references are supported, if a base URL was given.
  348. /// The string value indicates the default port number as a string of ASCII digits,
  349. /// or the empty string to indicate no default port number.
  350. Relative(u16),
  351. /// Indicate a *relative* scheme similar to the *file* scheme.
  352. ///
  353. /// For example, you might want to have distinct `git+file` and `hg+file` URL schemes.
  354. ///
  355. /// This is like `Relative` except the host can be empty, there is no port number,
  356. /// and path parsing has (platform-independent) quirks to support Windows filenames.
  357. FileLike,
  358. }
  359. impl SchemeType {
  360. pub fn default_port(&self) -> Option<u16> {
  361. match self {
  362. &SchemeType::Relative(default_port) => Some(default_port),
  363. _ => None,
  364. }
  365. }
  366. pub fn same_as(&self, other: SchemeType) -> bool {
  367. match (self, other) {
  368. (&SchemeType::NonRelative, SchemeType::NonRelative) => true,
  369. (&SchemeType::Relative(_), SchemeType::Relative(_)) => true,
  370. (&SchemeType::FileLike, SchemeType::FileLike) => true,
  371. _ => false
  372. }
  373. }
  374. }
  375. /// http://url.spec.whatwg.org/#relative-scheme
  376. pub fn whatwg_scheme_type_mapper(scheme: &str) -> SchemeType {
  377. match scheme {
  378. "file" => SchemeType::FileLike,
  379. "ftp" => SchemeType::Relative(21),
  380. "gopher" => SchemeType::Relative(70),
  381. "http" => SchemeType::Relative(80),
  382. "https" => SchemeType::Relative(443),
  383. "ws" => SchemeType::Relative(80),
  384. "wss" => SchemeType::Relative(443),
  385. _ => SchemeType::NonRelative,
  386. }
  387. }
  388. impl Url {
  389. /// Parse an URL with the default `UrlParser` parameters.
  390. ///
  391. /// In particular, relative URL references are parse errors since no base URL is provided.
  392. #[inline]
  393. pub fn parse(input: &str) -> ParseResult<Url> {
  394. UrlParser::new().parse(input)
  395. }
  396. /// Convert a file name as `std::path::Path` into an URL in the `file` scheme.
  397. ///
  398. /// This returns `Err` if the given path is not absolute
  399. /// or, with a Windows path, if the prefix is not a disk prefix (e.g. `C:`).
  400. pub fn from_file_path<P: AsRef<Path>>(path: P) -> Result<Url, ()> {
  401. let path = try!(path_to_file_url_path(path.as_ref()));
  402. Ok(Url::from_path_common(path))
  403. }
  404. /// Convert a directory name as `std::path::Path` into an URL in the `file` scheme.
  405. ///
  406. /// This returns `Err` if the given path is not absolute
  407. /// or, with a Windows path, if the prefix is not a disk prefix (e.g. `C:`).
  408. ///
  409. /// Compared to `from_file_path`, this adds an empty component to the path
  410. /// (or, in terms of URL syntax, adds a trailing slash)
  411. /// so that the entire path is considered when using this URL as a base URL.
  412. ///
  413. /// For example:
  414. ///
  415. /// * `"index.html"` parsed with `Url::from_directory_path(Path::new("/var/www"))`
  416. /// as the base URL is `file:///var/www/index.html`
  417. /// * `"index.html"` parsed with `Url::from_file_path(Path::new("/var/www/"))`
  418. /// as the base URL is `file:///var/index.html`, which might not be what was intended.
  419. ///
  420. /// (Note that `Path::new` removes any trailing slash.)
  421. pub fn from_directory_path<P: AsRef<Path>>(path: P) -> Result<Url, ()> {
  422. let mut path = try!(path_to_file_url_path(path.as_ref()));
  423. // Add an empty path component (i.e. a trailing slash in serialization)
  424. // so that the entire path is used as a base URL.
  425. path.push("".to_string());
  426. Ok(Url::from_path_common(path))
  427. }
  428. fn from_path_common(path: Vec<String>) -> Url {
  429. Url {
  430. scheme: "file".to_string(),
  431. scheme_data: SchemeData::Relative(RelativeSchemeData {
  432. username: "".to_string(),
  433. password: None,
  434. port: None,
  435. default_port: None,
  436. host: Host::Domain("".to_string()),
  437. path: path,
  438. }),
  439. query: None,
  440. fragment: None,
  441. }
  442. }
  443. /// Assuming the URL is in the `file` scheme or similar,
  444. /// convert its path to an absolute `std::path::Path`.
  445. ///
  446. /// **Note:** This does not actually check the URL’s `scheme`,
  447. /// and may give nonsensical results for other schemes.
  448. /// It is the user’s responsibility to check the URL’s scheme before calling this.
  449. ///
  450. /// The return type (when `Ok()`) is generic and can be either `std::path::posix::Path`
  451. /// or `std::path::windows::Path`.
  452. /// (Use `std::path::Path` to pick one of them depending on the local system.)
  453. /// If the compiler can not infer the desired type from context, you may have to specifiy it:
  454. ///
  455. /// ```ignore
  456. /// let path = url.to_file_path::<std::path::posix::Path>();
  457. /// ```
  458. ///
  459. /// Returns `Err` if the host is neither empty nor `"localhost"`,
  460. /// or if `Path::new_opt()` returns `None`.
  461. /// (That is, if the percent-decoded path contains a NUL byte or,
  462. /// for a Windows path, is not UTF-8.)
  463. #[inline]
  464. pub fn to_file_path(&self) -> Result<PathBuf, ()> {
  465. match self.scheme_data {
  466. SchemeData::Relative(ref scheme_data) => scheme_data.to_file_path(),
  467. SchemeData::NonRelative(..) => Err(()),
  468. }
  469. }
  470. /// Return the serialization of this URL as a string.
  471. pub fn serialize(&self) -> String {
  472. self.to_string()
  473. }
  474. /// Return the serialization of this URL, without the fragment identifier, as a string
  475. pub fn serialize_no_fragment(&self) -> String {
  476. UrlNoFragmentFormatter{ url: self }.to_string()
  477. }
  478. /// If the URL is *non-relative*, return the string scheme data.
  479. #[inline]
  480. pub fn non_relative_scheme_data<'a>(&'a self) -> Option<&'a str> {
  481. match self.scheme_data {
  482. SchemeData::Relative(..) => None,
  483. SchemeData::NonRelative(ref scheme_data) => Some(scheme_data),
  484. }
  485. }
  486. /// If the URL is *non-relative*, return a mutable reference to the string scheme data.
  487. #[inline]
  488. pub fn non_relative_scheme_data_mut<'a>(&'a mut self) -> Option<&'a mut String> {
  489. match self.scheme_data {
  490. SchemeData::Relative(..) => None,
  491. SchemeData::NonRelative(ref mut scheme_data) => Some(scheme_data),
  492. }
  493. }
  494. /// If the URL is in a *relative scheme*, return the structured scheme data.
  495. #[inline]
  496. pub fn relative_scheme_data<'a>(&'a self) -> Option<&'a RelativeSchemeData> {
  497. match self.scheme_data {
  498. SchemeData::Relative(ref scheme_data) => Some(scheme_data),
  499. SchemeData::NonRelative(..) => None,
  500. }
  501. }
  502. /// If the URL is in a *relative scheme*,
  503. /// return a mutable reference to the structured scheme data.
  504. #[inline]
  505. pub fn relative_scheme_data_mut<'a>(&'a mut self) -> Option<&'a mut RelativeSchemeData> {
  506. match self.scheme_data {
  507. SchemeData::Relative(ref mut scheme_data) => Some(scheme_data),
  508. SchemeData::NonRelative(..) => None,
  509. }
  510. }
  511. /// If the URL is in a *relative scheme*, return its username.
  512. #[inline]
  513. pub fn username<'a>(&'a self) -> Option<&'a str> {
  514. self.relative_scheme_data().map(|scheme_data| &*scheme_data.username)
  515. }
  516. /// If the URL is in a *relative scheme*, return a mutable reference to its username.
  517. #[inline]
  518. pub fn username_mut<'a>(&'a mut self) -> Option<&'a mut String> {
  519. self.relative_scheme_data_mut().map(|scheme_data| &mut scheme_data.username)
  520. }
  521. /// Percent-decode the URL’s username, if any.
  522. ///
  523. /// This is “lossy”: invalid UTF-8 percent-encoded byte sequences
  524. /// will be replaced � U+FFFD, the replacement character.
  525. #[inline]
  526. pub fn lossy_percent_decode_username(&self) -> Option<String> {
  527. self.relative_scheme_data().map(|scheme_data| scheme_data.lossy_percent_decode_username())
  528. }
  529. /// If the URL is in a *relative scheme*, return its password, if any.
  530. #[inline]
  531. pub fn password<'a>(&'a self) -> Option<&'a str> {
  532. self.relative_scheme_data().and_then(|scheme_data|
  533. scheme_data.password.as_ref().map(|password| &**password))
  534. }
  535. /// If the URL is in a *relative scheme*, return a mutable reference to its password, if any.
  536. #[inline]
  537. pub fn password_mut<'a>(&'a mut self) -> Option<&'a mut String> {
  538. self.relative_scheme_data_mut().and_then(|scheme_data| scheme_data.password.as_mut())
  539. }
  540. /// Percent-decode the URL’s password, if any.
  541. ///
  542. /// This is “lossy”: invalid UTF-8 percent-encoded byte sequences
  543. /// will be replaced � U+FFFD, the replacement character.
  544. #[inline]
  545. pub fn lossy_percent_decode_password(&self) -> Option<String> {
  546. self.relative_scheme_data().and_then(|scheme_data|
  547. scheme_data.lossy_percent_decode_password())
  548. }
  549. /// Serialize the URL's username and password, if any.
  550. ///
  551. /// Format: "<username>:<password>@"
  552. #[inline]
  553. pub fn serialize_userinfo<'a>(&'a mut self) -> Option<String> {
  554. self.relative_scheme_data().map(|scheme_data| scheme_data.serialize_userinfo())
  555. }
  556. /// If the URL is in a *relative scheme*, return its structured host.
  557. #[inline]
  558. pub fn host<'a>(&'a self) -> Option<&'a Host> {
  559. self.relative_scheme_data().map(|scheme_data| &scheme_data.host)
  560. }
  561. /// If the URL is in a *relative scheme*, return a mutable reference to its structured host.
  562. #[inline]
  563. pub fn host_mut<'a>(&'a mut self) -> Option<&'a mut Host> {
  564. self.relative_scheme_data_mut().map(|scheme_data| &mut scheme_data.host)
  565. }
  566. /// If the URL is in a *relative scheme* and its host is a domain,
  567. /// return the domain as a string.
  568. #[inline]
  569. pub fn domain<'a>(&'a self) -> Option<&'a str> {
  570. self.relative_scheme_data().and_then(|scheme_data| scheme_data.domain())
  571. }
  572. /// If the URL is in a *relative scheme* and its host is a domain,
  573. /// return a mutable reference to the domain string.
  574. #[inline]
  575. pub fn domain_mut<'a>(&'a mut self) -> Option<&'a mut String> {
  576. self.relative_scheme_data_mut().and_then(|scheme_data| scheme_data.domain_mut())
  577. }
  578. /// If the URL is in a *relative scheme*, serialize its host as a string.
  579. ///
  580. /// A domain a returned as-is, an IPv6 address between [] square brackets.
  581. #[inline]
  582. pub fn serialize_host(&self) -> Option<String> {
  583. self.relative_scheme_data().map(|scheme_data| scheme_data.host.serialize())
  584. }
  585. /// If the URL is in a *relative scheme* and has a port number, return it.
  586. #[inline]
  587. pub fn port<'a>(&'a self) -> Option<u16> {
  588. self.relative_scheme_data().and_then(|scheme_data| scheme_data.port)
  589. }
  590. /// If the URL is in a *relative scheme*, return a mutable reference to its port.
  591. #[inline]
  592. pub fn port_mut<'a>(&'a mut self) -> Option<&'a mut Option<u16>> {
  593. self.relative_scheme_data_mut().map(|scheme_data| &mut scheme_data.port)
  594. }
  595. /// If the URL is in a *relative scheme* that is not a file-like,
  596. /// return its port number, even if it is the default.
  597. #[inline]
  598. pub fn port_or_default(&self) -> Option<u16> {
  599. self.relative_scheme_data().and_then(|scheme_data| scheme_data.port_or_default())
  600. }
  601. /// If the URL is in a *relative scheme*, return its path components.
  602. #[inline]
  603. pub fn path<'a>(&'a self) -> Option<&'a [String]> {
  604. self.relative_scheme_data().map(|scheme_data| &*scheme_data.path)
  605. }
  606. /// If the URL is in a *relative scheme*, return a mutable reference to its path components.
  607. #[inline]
  608. pub fn path_mut<'a>(&'a mut self) -> Option<&'a mut Vec<String>> {
  609. self.relative_scheme_data_mut().map(|scheme_data| &mut scheme_data.path)
  610. }
  611. /// If the URL is in a *relative scheme*, serialize its path as a string.
  612. ///
  613. /// The returned string starts with a "/" slash, and components are separated by slashes.
  614. /// A trailing slash represents an empty last component.
  615. #[inline]
  616. pub fn serialize_path(&self) -> Option<String> {
  617. self.relative_scheme_data().map(|scheme_data| scheme_data.serialize_path())
  618. }
  619. /// Parse the URL’s query string, if any, as `application/x-www-form-urlencoded`
  620. /// and return a vector of (key, value) pairs.
  621. #[inline]
  622. pub fn query_pairs(&self) -> Option<Vec<(String, String)>> {
  623. self.query.as_ref().map(|query| form_urlencoded::parse(query.as_bytes()))
  624. }
  625. /// Serialize an iterator of (key, value) pairs as `application/x-www-form-urlencoded`
  626. /// and set it as the URL’s query string.
  627. #[inline]
  628. pub fn set_query_from_pairs<'a, I: Iterator<Item = (&'a str, &'a str)>>(&mut self, pairs: I) {
  629. self.query = Some(form_urlencoded::serialize(pairs));
  630. }
  631. /// Percent-decode the URL’s query string, if any.
  632. ///
  633. /// This is “lossy”: invalid UTF-8 percent-encoded byte sequences
  634. /// will be replaced � U+FFFD, the replacement character.
  635. #[inline]
  636. pub fn lossy_percent_decode_query(&self) -> Option<String> {
  637. self.query.as_ref().map(|value| lossy_utf8_percent_decode(value.as_bytes()))
  638. }
  639. /// Percent-decode the URL’s fragment identifier, if any.
  640. ///
  641. /// This is “lossy”: invalid UTF-8 percent-encoded byte sequences
  642. /// will be replaced � U+FFFD, the replacement character.
  643. #[inline]
  644. pub fn lossy_percent_decode_fragment(&self) -> Option<String> {
  645. self.fragment.as_ref().map(|value| lossy_utf8_percent_decode(value.as_bytes()))
  646. }
  647. }
  648. impl rustc_serialize::Encodable for Url {
  649. fn encode<S: rustc_serialize::Encoder>(&self, encoder: &mut S) -> Result<(), S::Error> {
  650. encoder.emit_str(&self.to_string())
  651. }
  652. }
  653. impl rustc_serialize::Decodable for Url {
  654. fn decode<D: rustc_serialize::Decoder>(decoder: &mut D) -> Result<Url, D::Error> {
  655. Url::parse(&*try!(decoder.read_str())).map_err(|error| {
  656. decoder.error(&format!("URL parsing error: {}", error))
  657. })
  658. }
  659. }
  660. impl fmt::Display for Url {
  661. fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
  662. try!(UrlNoFragmentFormatter{ url: self }.fmt(formatter));
  663. if let Some(ref fragment) = self.fragment {
  664. try!(formatter.write_str("#"));
  665. try!(formatter.write_str(fragment));
  666. }
  667. Ok(())
  668. }
  669. }
  670. impl fmt::Display for SchemeData {
  671. fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
  672. match *self {
  673. SchemeData::Relative(ref scheme_data) => scheme_data.fmt(formatter),
  674. SchemeData::NonRelative(ref scheme_data) => scheme_data.fmt(formatter),
  675. }
  676. }
  677. }
  678. impl RelativeSchemeData {
  679. /// Percent-decode the URL’s username.
  680. ///
  681. /// This is “lossy”: invalid UTF-8 percent-encoded byte sequences
  682. /// will be replaced � U+FFFD, the replacement character.
  683. #[inline]
  684. pub fn lossy_percent_decode_username(&self) -> String {
  685. lossy_utf8_percent_decode(self.username.as_bytes())
  686. }
  687. /// Percent-decode the URL’s password, if any.
  688. ///
  689. /// This is “lossy”: invalid UTF-8 percent-encoded byte sequences
  690. /// will be replaced � U+FFFD, the replacement character.
  691. #[inline]
  692. pub fn lossy_percent_decode_password(&self) -> Option<String> {
  693. self.password.as_ref().map(|value| lossy_utf8_percent_decode(value.as_bytes()))
  694. }
  695. /// Assuming the URL is in the `file` scheme or similar,
  696. /// convert its path to an absolute `std::path::Path`.
  697. ///
  698. /// **Note:** This does not actually check the URL’s `scheme`,
  699. /// and may give nonsensical results for other schemes.
  700. /// It is the user’s responsibility to check the URL’s scheme before calling this.
  701. ///
  702. /// The return type (when `Ok()`) is generic and can be either `std::path::posix::Path`
  703. /// or `std::path::windows::Path`.
  704. /// (Use `std::path::Path` to pick one of them depending on the local system.)
  705. /// If the compiler can not infer the desired type from context, you may have to specifiy it:
  706. ///
  707. /// ```ignore
  708. /// let path = url.to_file_path::<std::path::posix::Path>();
  709. /// ```
  710. ///
  711. /// Returns `Err` if the host is neither empty nor `"localhost"`,
  712. /// or if `Path::new_opt()` returns `None`.
  713. /// (That is, if the percent-decoded path contains a NUL byte or,
  714. /// for a Windows path, is not UTF-8.)
  715. #[inline]
  716. pub fn to_file_path(&self) -> Result<PathBuf, ()> {
  717. // FIXME: Figure out what to do w.r.t host.
  718. if !matches!(self.domain(), Some("") | Some("localhost")) {
  719. return Err(())
  720. }
  721. file_url_path_to_pathbuf(&self.path)
  722. }
  723. /// If the host is a domain, return the domain as a string.
  724. #[inline]
  725. pub fn domain<'a>(&'a self) -> Option<&'a str> {
  726. match self.host {
  727. Host::Domain(ref domain) => Some(domain),
  728. _ => None,
  729. }
  730. }
  731. /// If the host is a domain, return a mutable reference to the domain string.
  732. #[inline]
  733. pub fn domain_mut<'a>(&'a mut self) -> Option<&'a mut String> {
  734. match self.host {
  735. Host::Domain(ref mut domain) => Some(domain),
  736. _ => None,
  737. }
  738. }
  739. /// Return the port number of the URL, even if it is the default.
  740. /// Return `None` for file-like URLs.
  741. #[inline]
  742. pub fn port_or_default(&self) -> Option<u16> {
  743. self.port.or(self.default_port)
  744. }
  745. /// Serialize the path as a string.
  746. ///
  747. /// The returned string starts with a "/" slash, and components are separated by slashes.
  748. /// A trailing slash represents an empty last component.
  749. pub fn serialize_path(&self) -> String {
  750. PathFormatter {
  751. path: &self.path
  752. }.to_string()
  753. }
  754. /// Serialize the userinfo as a string.
  755. ///
  756. /// Format: "<username>:<password>@".
  757. pub fn serialize_userinfo(&self) -> String {
  758. UserInfoFormatter {
  759. username: &self.username,
  760. password: self.password.as_ref().map(|s| &**s)
  761. }.to_string()
  762. }
  763. }
  764. impl fmt::Display for RelativeSchemeData {
  765. fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
  766. // Write the scheme-trailing double slashes.
  767. try!(formatter.write_str("//"));
  768. // Write the user info.
  769. try!(UserInfoFormatter {
  770. username: &self.username,
  771. password: self.password.as_ref().map(|s| &**s)
  772. }.fmt(formatter));
  773. // Write the host.
  774. try!(self.host.fmt(formatter));
  775. // Write the port.
  776. match self.port {
  777. Some(port) => {
  778. try!(write!(formatter, ":{}", port));
  779. },
  780. None => {}
  781. }
  782. // Write the path.
  783. PathFormatter {
  784. path: &self.path
  785. }.fmt(formatter)
  786. }
  787. }
  788. #[cfg(unix)]
  789. fn path_to_file_url_path(path: &Path) -> Result<Vec<String>, ()> {
  790. use std::os::unix::prelude::OsStrExt;
  791. if !path.is_absolute() {
  792. return Err(())
  793. }
  794. // skip the root component
  795. Ok(path.components().skip(1).map(|c| {
  796. percent_encode(c.as_os_str().as_bytes(), DEFAULT_ENCODE_SET)
  797. }).collect())
  798. }
  799. #[cfg(windows)]
  800. fn path_to_file_url_path(path: &Path) -> Result<Vec<String>, ()> {
  801. use std::path::{Prefix, Component};
  802. if !path.is_absolute() {
  803. return Err(())
  804. }
  805. let mut components = path.components();
  806. let disk = match components.next() {
  807. Some(Component::Prefix(ref p)) => match p.kind() {
  808. Prefix::Disk(byte) => byte,
  809. _ => return Err(()),
  810. },
  811. // FIXME: do something with UNC and other prefixes?
  812. _ => return Err(())
  813. };
  814. // Start with the prefix, e.g. "C:"
  815. let mut path = vec![format!("{}:", disk as char)];
  816. for component in components {
  817. if component == Component::RootDir { continue }
  818. // FIXME: somehow work with non-unicode?
  819. let part = match component.as_os_str().to_str() {
  820. Some(s) => s,
  821. None => return Err(()),
  822. };
  823. path.push(percent_encode(part.as_bytes(), DEFAULT_ENCODE_SET));
  824. }
  825. Ok(path)
  826. }
  827. #[cfg(unix)]
  828. fn file_url_path_to_pathbuf(path: &[String]) -> Result<PathBuf, ()> {
  829. use std::ffi::OsStr;
  830. use std::os::unix::prelude::OsStrExt;
  831. use std::path::PathBuf;
  832. if path.is_empty() {
  833. return Ok(PathBuf::from("/"))
  834. }
  835. let mut bytes = Vec::new();
  836. for path_part in path.iter() {
  837. bytes.push(b'/');
  838. percent_decode_to(path_part.as_bytes(), &mut bytes);
  839. }
  840. let os_str = OsStr::from_bytes(&bytes);
  841. let path = PathBuf::from(os_str);
  842. debug_assert!(path.is_absolute(),
  843. "to_file_path() failed to produce an absolute Path");
  844. Ok(path)
  845. }
  846. #[cfg(windows)]
  847. fn file_url_path_to_pathbuf(path: &[String]) -> Result<PathBuf, ()> {
  848. if path.is_empty() {
  849. return Err(())
  850. }
  851. let prefix = &*path[0];
  852. if prefix.len() != 2 || !parser::starts_with_ascii_alpha(prefix)
  853. || prefix.as_bytes()[1] != b':' {
  854. return Err(())
  855. }
  856. let mut string = prefix.to_string();
  857. for path_part in path[1..].iter() {
  858. string.push('\\');
  859. // Currently non-unicode windows paths cannot be represented
  860. match String::from_utf8(percent_decode(path_part.as_bytes())) {
  861. Ok(s) => string.push_str(&s),
  862. Err(..) => return Err(()),
  863. }
  864. }
  865. let path = PathBuf::from(string);
  866. debug_assert!(path.is_absolute(),
  867. "to_file_path() failed to produce an absolute Path");
  868. Ok(path)
  869. }