lib.rs 36 KB

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