lib.rs 39 KB

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