lib.rs 37 KB

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