url.rs 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074
  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. #![crate_name = "url_"]
  9. #![crate_type = "dylib"]
  10. #![crate_type = "rlib"]
  11. //! <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>
  12. //! <style>.sidebar { margin-top: 53px }</style>
  13. //!
  14. //! rust-url is an implementation of the [URL Standard](http://url.spec.whatwg.org/)
  15. //! for the [Rust](http://rust-lang.org/) programming language.
  16. //!
  17. //! It builds with [Cargo](http://crates.io/).
  18. //! To use it in your project, add this to your `Cargo.toml` file:
  19. //!
  20. //! ```Cargo
  21. //! [dependencies.url]
  22. //! git = "https://github.com/servo/rust-url"
  23. //! ```
  24. //!
  25. //! This is a replacement of the [`url` crate](http://doc.rust-lang.org/url/index.html)
  26. //! currently distributed with Rust.
  27. //! rust-url’s crate is currently named `url_` with an underscore to avoid a naming conflict,
  28. //! but the intent is to rename it to just `url` when the old crate eventually
  29. //! [goes away](https://github.com/rust-lang/rust/issues/15874).
  30. //! Therefore, it is recommended that you use this crate as follows:
  31. //!
  32. //! ```ignore
  33. //! extern crate url = "url_";
  34. //!
  35. //! use url::{Url, ...};
  36. //! ```
  37. //!
  38. //! … so that, when the renaming is done, you will only need to change this one line.
  39. //!
  40. //! # URL parsing and data structures
  41. //!
  42. //! First, URL parsing may fail for various reasons and therefore returns a `Result`.
  43. //!
  44. //! ```
  45. //! # use url_::Url;
  46. //! assert!(Url::parse("http://[:::1]") == Err("Invalid IPv6 address"))
  47. //! ```
  48. //!
  49. //! Let’s parse a valid URL and look at its components.
  50. //!
  51. //! ```
  52. //! # use url_::{Url, RelativeSchemeData, NonRelativeSchemeData};
  53. //! let issue_list_url = Url::parse(
  54. //! "https://github.com/rust-lang/rust/issues?labels=E-easy&state=open"
  55. //! ).unwrap();
  56. //!
  57. //!
  58. //! assert!(issue_list_url.scheme == "https".to_string());
  59. //! assert!(issue_list_url.domain() == Some("github.com"));
  60. //! assert!(issue_list_url.port() == Some(""));
  61. //! assert!(issue_list_url.path() == Some(&["rust-lang".to_string(),
  62. //! "rust".to_string(),
  63. //! "issues".to_string()]));
  64. //! assert!(issue_list_url.query == Some("labels=E-easy&state=open".to_string()));
  65. //! assert!(issue_list_url.fragment == None);
  66. //! match issue_list_url.scheme_data {
  67. //! RelativeSchemeData(..) => {}, // Expected
  68. //! NonRelativeSchemeData(..) => fail!(),
  69. //! }
  70. //! ```
  71. //!
  72. //! The `scheme`, `query`, and `fragment` are directly fields of the `Url` struct:
  73. //! they apply to all URLs.
  74. //! Every other components has accessors because they only apply to URLs said to be
  75. //! “in a relative scheme”. `https` is a relative scheme, but `data` is not:
  76. //!
  77. //! ```
  78. //! # use url_::{Url, NonRelativeSchemeData};
  79. //! let data_url = Url::parse("data:text/plain,Hello#").unwrap();
  80. //!
  81. //! assert!(data_url.scheme == "data".to_string());
  82. //! assert!(data_url.scheme_data == NonRelativeSchemeData("text/plain,Hello".to_string()));
  83. //! assert!(data_url.non_relative_scheme_data() == Some("text/plain,Hello"));
  84. //! assert!(data_url.query == None);
  85. //! assert!(data_url.fragment == Some("".to_string()));
  86. //! ```
  87. //!
  88. //! # Base URL
  89. //!
  90. //! Many contexts allow URL *references* that can be relative to a *base URL*:
  91. //!
  92. //! ```html
  93. //! <link rel="stylesheet" href="../main.css">
  94. //! ```
  95. //!
  96. //! Since parsed URL are absolute, giving a base is required:
  97. //!
  98. //! ```
  99. //! # use url_::Url;
  100. //! assert!(Url::parse("../main.css") == Err("Relative URL without a base"))
  101. //! ```
  102. //!
  103. //! `UrlParser` is a method-chaining API to provide various optional parameters
  104. //! to URL parsing, including a base URL.
  105. //!
  106. //! ```
  107. //! # use url_::{Url, UrlParser};
  108. //! let this_document = Url::parse("http://servo.github.io/rust-url/url/index.html").unwrap();
  109. //! let css_url = UrlParser::new().base_url(&this_document).parse("../main.css").unwrap();
  110. //! assert!(css_url.serialize() == "http://servo.github.io/rust-url/main.css".to_string());
  111. //! ```
  112. #![feature(macro_rules, default_type_params)]
  113. extern crate encoding;
  114. #[cfg(test)]
  115. extern crate serialize;
  116. use std::cmp;
  117. use std::fmt::{Formatter, FormatError, Show};
  118. use std::hash;
  119. use std::path::Path;
  120. use std::ascii::OwnedStrAsciiExt;
  121. use encoding::EncodingRef;
  122. use encode_sets::{PASSWORD_ENCODE_SET, USERNAME_ENCODE_SET, DEFAULT_ENCODE_SET};
  123. mod encode_sets;
  124. mod parser;
  125. pub mod form_urlencoded;
  126. pub mod punycode;
  127. #[cfg(test)]
  128. mod tests;
  129. #[deriving(PartialEq, Eq, Clone)]
  130. pub struct Url {
  131. pub scheme: String,
  132. pub scheme_data: SchemeData,
  133. pub query: Option<String>, // See form_urlencoded::parse_str() to get name/value pairs.
  134. pub fragment: Option<String>,
  135. }
  136. #[deriving(PartialEq, Eq, Clone)]
  137. pub enum SchemeData {
  138. RelativeSchemeData(RelativeSchemeData),
  139. NonRelativeSchemeData(String), // data: URLs, mailto: URLs, etc.
  140. }
  141. #[deriving(PartialEq, Eq, Clone)]
  142. pub struct RelativeSchemeData {
  143. pub username: String,
  144. pub password: Option<String>,
  145. pub host: Host,
  146. pub port: String,
  147. pub path: Vec<String>,
  148. }
  149. #[deriving(PartialEq, Eq, Clone)]
  150. pub enum Host {
  151. Domain(String),
  152. Ipv6(Ipv6Address)
  153. }
  154. pub struct Ipv6Address {
  155. pub pieces: [u16, ..8]
  156. }
  157. impl Clone for Ipv6Address {
  158. fn clone(&self) -> Ipv6Address {
  159. Ipv6Address { pieces: self.pieces }
  160. }
  161. }
  162. impl Eq for Ipv6Address {}
  163. impl PartialEq for Ipv6Address {
  164. fn eq(&self, other: &Ipv6Address) -> bool {
  165. self.pieces == other.pieces
  166. }
  167. }
  168. impl<S: hash::Writer> hash::Hash<S> for Url {
  169. fn hash(&self, state: &mut S) {
  170. self.serialize().hash(state)
  171. }
  172. }
  173. pub struct UrlParser<'a> {
  174. base_url: Option<&'a Url>,
  175. query_encoding_override: Option<EncodingRef>,
  176. error_handler: ErrorHandler,
  177. scheme_type_mapper: fn(scheme: &str) -> SchemeType,
  178. }
  179. impl<'a> UrlParser<'a> {
  180. #[inline]
  181. pub fn new() -> UrlParser<'a> {
  182. UrlParser {
  183. base_url: None,
  184. query_encoding_override: None,
  185. error_handler: silent_handler,
  186. scheme_type_mapper: whatwg_scheme_type_mapper,
  187. }
  188. }
  189. #[inline]
  190. pub fn base_url<'b>(&'b mut self, value: &'a Url) -> &'b mut UrlParser<'a> {
  191. self.base_url = Some(value);
  192. self
  193. }
  194. #[inline]
  195. pub fn query_encoding_override<'b>(&'b mut self, value: EncodingRef) -> &'b mut UrlParser<'a> {
  196. self.query_encoding_override = Some(value);
  197. self
  198. }
  199. #[inline]
  200. pub fn error_handler<'b>(&'b mut self, value: ErrorHandler) -> &'b mut UrlParser<'a> {
  201. self.error_handler = value;
  202. self
  203. }
  204. #[inline]
  205. pub fn scheme_type_mapper<'b>(&'b mut self, value: fn(scheme: &str) -> SchemeType)
  206. -> &'b mut UrlParser<'a> {
  207. self.scheme_type_mapper = value;
  208. self
  209. }
  210. #[inline]
  211. pub fn parse(&self, input: &str) -> ParseResult<Url> {
  212. parser::parse_url(input, self)
  213. }
  214. #[inline]
  215. fn parse_error(&self, message: &'static str) -> ParseResult<()> {
  216. (self.error_handler)(message)
  217. }
  218. #[inline]
  219. fn get_scheme_type(&self, scheme: &str) -> SchemeType {
  220. (self.scheme_type_mapper)(scheme)
  221. }
  222. }
  223. #[deriving(PartialEq, Eq)]
  224. pub enum SchemeType {
  225. FileLikeRelativeScheme,
  226. RelativeScheme(&'static str), // str is the default port, in ASCII decimal.
  227. NonRelativeScheme,
  228. }
  229. /// http://url.spec.whatwg.org/#relative-scheme
  230. fn whatwg_scheme_type_mapper(scheme: &str) -> SchemeType {
  231. match scheme {
  232. "file" => FileLikeRelativeScheme,
  233. "ftp" => RelativeScheme("21"),
  234. "gopher" => RelativeScheme("70"),
  235. "http" => RelativeScheme("80"),
  236. "https" => RelativeScheme("443"),
  237. "ws" => RelativeScheme("80"),
  238. "wss" => RelativeScheme("443"),
  239. _ => NonRelativeScheme,
  240. }
  241. }
  242. pub type ParseResult<T> = Result<T, &'static str>;
  243. /// This is called on non-fatal parse errors.
  244. /// The handler can choose to continue or abort parsing by returning Ok() or Err(), respectively.
  245. /// FIXME: make this a by-ref closure when that’s supported.
  246. pub type ErrorHandler = fn(reason: &'static str) -> ParseResult<()>;
  247. fn silent_handler(_reason: &'static str) -> ParseResult<()> {
  248. Ok(())
  249. }
  250. impl Url {
  251. #[inline]
  252. pub fn parse(input: &str) -> ParseResult<Url> {
  253. UrlParser::new().parse(input)
  254. }
  255. // FIXME: Figure out what to do on Windows
  256. #[cfg(unix)]
  257. pub fn from_file_path(path: &Path) -> Result<Url, ()> {
  258. let path = try!(encode_file_path(path));
  259. Ok(Url::from_path_common(path))
  260. }
  261. // FIXME: Figure out what to do on Windows
  262. #[cfg(unix)]
  263. pub fn from_directory_path(path: &Path) -> Result<Url, ()> {
  264. let mut path = try!(encode_file_path(path));
  265. // Add an empty path component (i.e. a trailing slash in serialization)
  266. // so that the entire path is used as a base URL.
  267. path.push("".to_string());
  268. Ok(Url::from_path_common(path))
  269. }
  270. fn from_path_common(path: Vec<String>) -> Url {
  271. Url {
  272. scheme: "file".to_string(),
  273. scheme_data: RelativeSchemeData(RelativeSchemeData {
  274. username: "".to_string(),
  275. password: None,
  276. port: "".to_string(),
  277. host: Domain("".to_string()),
  278. path: path,
  279. }),
  280. query: None,
  281. fragment: None,
  282. }
  283. }
  284. #[inline]
  285. pub fn to_file_path(&self) -> Result<Path, ()> {
  286. match self.scheme_data {
  287. RelativeSchemeData(ref scheme_data) => scheme_data.to_file_path(),
  288. NonRelativeSchemeData(..) => Err(()),
  289. }
  290. }
  291. pub fn serialize(&self) -> String {
  292. self.to_string()
  293. }
  294. pub fn serialize_no_fragment(&self) -> String {
  295. UrlNoFragmentFormatter{ url: self }.to_string()
  296. }
  297. #[inline]
  298. pub fn non_relative_scheme_data<'a>(&'a self) -> Option<&'a str> {
  299. match self.scheme_data {
  300. RelativeSchemeData(..) => None,
  301. NonRelativeSchemeData(ref scheme_data) => Some(scheme_data.as_slice()),
  302. }
  303. }
  304. #[inline]
  305. pub fn relative_scheme_data<'a>(&'a self) -> Option<&'a RelativeSchemeData> {
  306. match self.scheme_data {
  307. RelativeSchemeData(ref scheme_data) => Some(scheme_data),
  308. NonRelativeSchemeData(..) => None,
  309. }
  310. }
  311. #[inline]
  312. pub fn username<'a>(&'a self) -> Option<&'a str> {
  313. self.relative_scheme_data().map(|scheme_data| scheme_data.username.as_slice())
  314. }
  315. /// Percent-decode the URL’s username, if any.
  316. ///
  317. /// This is “lossy”: invalid UTF-8 percent-encoded byte sequences
  318. /// will be replaced � U+FFFD, the replacement character.
  319. #[inline]
  320. pub fn lossy_precent_decode_username(&self) -> Option<String> {
  321. self.relative_scheme_data().map(|scheme_data| scheme_data.lossy_precent_decode_username())
  322. }
  323. #[inline]
  324. pub fn password<'a>(&'a self) -> Option<&'a str> {
  325. self.relative_scheme_data().and_then(|scheme_data|
  326. scheme_data.password.as_ref().map(|password| password.as_slice()))
  327. }
  328. /// Percent-decode the URL’s password, if any.
  329. ///
  330. /// This is “lossy”: invalid UTF-8 percent-encoded byte sequences
  331. /// will be replaced � U+FFFD, the replacement character.
  332. #[inline]
  333. pub fn lossy_precent_decode_password(&self) -> Option<String> {
  334. self.relative_scheme_data().and_then(|scheme_data|
  335. scheme_data.lossy_precent_decode_password())
  336. }
  337. #[inline]
  338. pub fn host<'a>(&'a self) -> Option<&'a Host> {
  339. self.relative_scheme_data().map(|scheme_data| &scheme_data.host)
  340. }
  341. #[inline]
  342. pub fn domain<'a>(&'a self) -> Option<&'a str> {
  343. self.relative_scheme_data().and_then(|scheme_data| scheme_data.domain())
  344. }
  345. #[inline]
  346. pub fn port<'a>(&'a self) -> Option<&'a str> {
  347. self.relative_scheme_data().map(|scheme_data| scheme_data.port.as_slice())
  348. }
  349. #[inline]
  350. pub fn path<'a>(&'a self) -> Option<&'a [String]> {
  351. self.relative_scheme_data().map(|scheme_data| scheme_data.path.as_slice())
  352. }
  353. #[inline]
  354. pub fn serialize_host(&self) -> Option<String> {
  355. self.relative_scheme_data().map(|scheme_data| scheme_data.host.serialize())
  356. }
  357. #[inline]
  358. pub fn serialize_path(&self) -> Option<String> {
  359. self.relative_scheme_data().map(|scheme_data| scheme_data.serialize_path())
  360. }
  361. /// Parse the URL’s query string, if any, as `application/x-www-form-urlencoded`
  362. /// and return a vector of (key, value) pairs.
  363. #[inline]
  364. pub fn query_pairs(&self) -> Option<Vec<(String, String)>> {
  365. self.query.as_ref().map(|query| form_urlencoded::parse_str(query.as_slice()))
  366. }
  367. /// Serialize an iterator of (key, value) pairs as `application/x-www-form-urlencoded`
  368. /// and set it as the URL’s query string.
  369. #[inline]
  370. pub fn set_query_from_pairs<'a, I: Iterator<(&'a str, &'a str)>>(&mut self, pairs: I) {
  371. self.query = Some(form_urlencoded::serialize(pairs, None));
  372. }
  373. /// Percent-decode the URL’s query string, if any.
  374. ///
  375. /// This is “lossy”: invalid UTF-8 percent-encoded byte sequences
  376. /// will be replaced � U+FFFD, the replacement character.
  377. #[inline]
  378. pub fn lossy_precent_decode_query(&self) -> Option<String> {
  379. self.query.as_ref().map(|value| lossy_utf8_percent_decode(value.as_bytes()))
  380. }
  381. /// Percent-decode the URL’s fragment identifier, if any.
  382. ///
  383. /// This is “lossy”: invalid UTF-8 percent-encoded byte sequences
  384. /// will be replaced � U+FFFD, the replacement character.
  385. #[inline]
  386. pub fn lossy_precent_decode_fragment(&self) -> Option<String> {
  387. self.fragment.as_ref().map(|value| lossy_utf8_percent_decode(value.as_bytes()))
  388. }
  389. }
  390. impl Show for Url {
  391. fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
  392. try!(UrlNoFragmentFormatter{ url: self }.fmt(formatter));
  393. match self.fragment {
  394. None => (),
  395. Some(ref fragment) => {
  396. try!(formatter.write(b"#"));
  397. try!(formatter.write(fragment.as_bytes()));
  398. }
  399. }
  400. Ok(())
  401. }
  402. }
  403. struct UrlNoFragmentFormatter<'a> {
  404. url: &'a Url
  405. }
  406. impl<'a> Show for UrlNoFragmentFormatter<'a> {
  407. fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
  408. try!(formatter.write(self.url.scheme.as_bytes()));
  409. try!(formatter.write(b":"));
  410. try!(self.url.scheme_data.fmt(formatter));
  411. match self.url.query {
  412. None => (),
  413. Some(ref query) => {
  414. try!(formatter.write(b"?"));
  415. try!(formatter.write(query.as_bytes()));
  416. }
  417. }
  418. Ok(())
  419. }
  420. }
  421. impl Show for SchemeData {
  422. fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
  423. match *self {
  424. RelativeSchemeData(ref scheme_data) => scheme_data.fmt(formatter),
  425. NonRelativeSchemeData(ref scheme_data) => scheme_data.fmt(formatter),
  426. }
  427. }
  428. }
  429. impl RelativeSchemeData {
  430. /// Percent-decode the URL’s username.
  431. ///
  432. /// This is “lossy”: invalid UTF-8 percent-encoded byte sequences
  433. /// will be replaced � U+FFFD, the replacement character.
  434. #[inline]
  435. pub fn lossy_precent_decode_username(&self) -> String {
  436. lossy_utf8_percent_decode(self.username.as_bytes())
  437. }
  438. /// Percent-decode the URL’s password, if any.
  439. ///
  440. /// This is “lossy”: invalid UTF-8 percent-encoded byte sequences
  441. /// will be replaced � U+FFFD, the replacement character.
  442. #[inline]
  443. pub fn lossy_precent_decode_password(&self) -> Option<String> {
  444. self.password.as_ref().map(|value| lossy_utf8_percent_decode(value.as_bytes()))
  445. }
  446. // FIXME: Figure out what to do on Windows.
  447. #[cfg(unix)]
  448. pub fn to_file_path(&self) -> Result<Path, ()> {
  449. // FIXME: Figure out what to do w.r.t host.
  450. match self.domain() {
  451. Some("") | Some("localhost") => {
  452. if self.path.is_empty() {
  453. Ok(Path::new("/"))
  454. } else {
  455. let mut bytes = Vec::new();
  456. for path_part in self.path.iter() {
  457. bytes.push(b'/');
  458. percent_decode_to(path_part.as_bytes(), &mut bytes);
  459. }
  460. let path = Path::new(bytes);
  461. debug_assert!(path.is_absolute(),
  462. "to_file_path() failed to produce an absolute Path")
  463. Ok(path)
  464. }
  465. }
  466. _ => Err(())
  467. }
  468. }
  469. #[inline]
  470. pub fn domain<'a>(&'a self) -> Option<&'a str> {
  471. match self.host {
  472. Domain(ref domain) => Some(domain.as_slice()),
  473. _ => None,
  474. }
  475. }
  476. pub fn serialize_path(&self) -> String {
  477. PathFormatter { path: &self.path }.to_string()
  478. }
  479. }
  480. struct PathFormatter<'a> {
  481. path: &'a Vec<String>
  482. }
  483. impl<'a> Show for PathFormatter<'a> {
  484. fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
  485. if self.path.is_empty() {
  486. formatter.write(b"/")
  487. } else {
  488. for path_part in self.path.iter() {
  489. try!(formatter.write(b"/"));
  490. try!(formatter.write(path_part.as_bytes()));
  491. }
  492. Ok(())
  493. }
  494. }
  495. }
  496. impl Show for RelativeSchemeData {
  497. fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
  498. try!(formatter.write(b"//"));
  499. if !self.username.is_empty() || self.password.is_some() {
  500. try!(formatter.write(self.username.as_bytes()));
  501. match self.password {
  502. None => (),
  503. Some(ref password) => {
  504. try!(formatter.write(b":"));
  505. try!(formatter.write(password.as_bytes()));
  506. }
  507. }
  508. try!(formatter.write(b"@"));
  509. }
  510. try!(self.host.fmt(formatter));
  511. if !self.port.is_empty() {
  512. try!(formatter.write(b":"));
  513. try!(formatter.write(self.port.as_bytes()));
  514. }
  515. PathFormatter { path: &self.path }.fmt(formatter)
  516. }
  517. }
  518. #[allow(dead_code)]
  519. struct UrlUtilsWrapper<'a> {
  520. url: &'a mut Url,
  521. parser: &'a UrlParser<'a>,
  522. }
  523. /// These methods are not meant for use in Rust code,
  524. /// only to help implement the JavaScript URLUtils API: http://url.spec.whatwg.org/#urlutils
  525. trait UrlUtils {
  526. fn set_scheme(&mut self, input: &str) -> ParseResult<()>;
  527. fn set_username(&mut self, input: &str) -> ParseResult<()>;
  528. fn set_password(&mut self, input: &str) -> ParseResult<()>;
  529. fn set_host_and_port(&mut self, input: &str) -> ParseResult<()>;
  530. fn set_host(&mut self, input: &str) -> ParseResult<()>;
  531. fn set_port(&mut self, input: &str) -> ParseResult<()>;
  532. fn set_path(&mut self, input: &str) -> ParseResult<()>;
  533. fn set_query(&mut self, input: &str) -> ParseResult<()>;
  534. fn set_fragment(&mut self, input: &str) -> ParseResult<()>;
  535. }
  536. impl<'a> UrlUtils for UrlUtilsWrapper<'a> {
  537. /// `URLUtils.protocol` setter
  538. fn set_scheme(&mut self, input: &str) -> ParseResult<()> {
  539. match parser::parse_scheme(input.as_slice(), parser::SetterContext) {
  540. Some((scheme, _)) => {
  541. self.url.scheme = scheme;
  542. Ok(())
  543. },
  544. None => Err("Invalid scheme"),
  545. }
  546. }
  547. /// `URLUtils.username` setter
  548. fn set_username(&mut self, input: &str) -> ParseResult<()> {
  549. match self.url.scheme_data {
  550. RelativeSchemeData(RelativeSchemeData { ref mut username, .. }) => {
  551. username.truncate(0);
  552. utf8_percent_encode_to(input, USERNAME_ENCODE_SET, username);
  553. Ok(())
  554. },
  555. NonRelativeSchemeData(_) => Err("Can not set username on non-relative URL.")
  556. }
  557. }
  558. /// `URLUtils.password` setter
  559. fn set_password(&mut self, input: &str) -> ParseResult<()> {
  560. match self.url.scheme_data {
  561. RelativeSchemeData(RelativeSchemeData { ref mut password, .. }) => {
  562. let mut new_password = String::new();
  563. utf8_percent_encode_to(input, PASSWORD_ENCODE_SET, &mut new_password);
  564. *password = Some(new_password);
  565. Ok(())
  566. },
  567. NonRelativeSchemeData(_) => Err("Can not set password on non-relative URL.")
  568. }
  569. }
  570. /// `URLUtils.host` setter
  571. fn set_host_and_port(&mut self, input: &str) -> ParseResult<()> {
  572. match self.url.scheme_data {
  573. RelativeSchemeData(RelativeSchemeData { ref mut host, ref mut port, .. }) => {
  574. let scheme_type = self.parser.get_scheme_type(self.url.scheme.as_slice());
  575. let (new_host, new_port, _) = try!(parser::parse_host(
  576. input, scheme_type, self.parser));
  577. *host = new_host;
  578. *port = new_port;
  579. Ok(())
  580. },
  581. NonRelativeSchemeData(_) => Err("Can not set host/port on non-relative URL.")
  582. }
  583. }
  584. /// `URLUtils.hostname` setter
  585. fn set_host(&mut self, input: &str) -> ParseResult<()> {
  586. match self.url.scheme_data {
  587. RelativeSchemeData(RelativeSchemeData { ref mut host, .. }) => {
  588. let (new_host, _) = try!(parser::parse_hostname(input, self.parser));
  589. *host = new_host;
  590. Ok(())
  591. },
  592. NonRelativeSchemeData(_) => Err("Can not set host on non-relative URL.")
  593. }
  594. }
  595. /// `URLUtils.port` setter
  596. fn set_port(&mut self, input: &str) -> ParseResult<()> {
  597. match self.url.scheme_data {
  598. RelativeSchemeData(RelativeSchemeData { ref mut port, .. }) => {
  599. let scheme_type = self.parser.get_scheme_type(self.url.scheme.as_slice());
  600. if scheme_type == FileLikeRelativeScheme {
  601. return Err("Can not set port on file: URL.")
  602. }
  603. let (new_port, _) = try!(parser::parse_port(input, scheme_type, self.parser));
  604. *port = new_port;
  605. Ok(())
  606. },
  607. NonRelativeSchemeData(_) => Err("Can not set port on non-relative URL.")
  608. }
  609. }
  610. /// `URLUtils.pathname` setter
  611. fn set_path(&mut self, input: &str) -> ParseResult<()> {
  612. match self.url.scheme_data {
  613. RelativeSchemeData(RelativeSchemeData { ref mut path, .. }) => {
  614. let scheme_type = self.parser.get_scheme_type(self.url.scheme.as_slice());
  615. let (new_path, _) = try!(parser::parse_path_start(
  616. input, parser::SetterContext, scheme_type, self.parser));
  617. *path = new_path;
  618. Ok(())
  619. },
  620. NonRelativeSchemeData(_) => Err("Can not set path on non-relative URL.")
  621. }
  622. }
  623. /// `URLUtils.search` setter
  624. fn set_query(&mut self, input: &str) -> ParseResult<()> {
  625. // FIXME: This is in the spec, but seems superfluous.
  626. match self.url.scheme_data {
  627. RelativeSchemeData(_) => (),
  628. NonRelativeSchemeData(_) => return Err("Can not set query on non-relative URL.")
  629. }
  630. self.url.query = if input.is_empty() {
  631. None
  632. } else {
  633. let input = if input.starts_with("?") { input.slice_from(1) } else { input };
  634. let (new_query, _) = try!(parser::parse_query(
  635. input, parser::SetterContext, self.parser));
  636. Some(new_query)
  637. };
  638. Ok(())
  639. }
  640. /// `URLUtils.hash` setter
  641. fn set_fragment(&mut self, input: &str) -> ParseResult<()> {
  642. if self.url.scheme.as_slice() == "javascript" {
  643. return Err("Can not set fragment on a javascript: URL.")
  644. }
  645. self.url.fragment = if input.is_empty() {
  646. None
  647. } else {
  648. let input = if input.starts_with("#") { input.slice_from(1) } else { input };
  649. Some(try!(parser::parse_fragment(input, self.parser)))
  650. };
  651. Ok(())
  652. }
  653. }
  654. impl Host {
  655. pub fn parse(input: &str) -> ParseResult<Host> {
  656. if input.len() == 0 {
  657. Err("Empty host")
  658. } else if input.starts_with("[") {
  659. if input.ends_with("]") {
  660. Ipv6Address::parse(input.slice(1, input.len() - 1)).map(Ipv6)
  661. } else {
  662. Err("Invalid Ipv6 address")
  663. }
  664. } else {
  665. let decoded = percent_decode(input.as_bytes());
  666. let domain = String::from_utf8_lossy(decoded.as_slice());
  667. // TODO: Remove this check and use IDNA "domain to ASCII"
  668. if !domain.as_slice().is_ascii() {
  669. Err("Non-ASCII domains (IDNA) are not supported yet.")
  670. } else if domain.as_slice().find(&[
  671. '\0', '\t', '\n', '\r', ' ', '#', '%', '/', ':', '?', '@', '[', '\\', ']'
  672. ]).is_some() {
  673. Err("Invalid domain character.")
  674. } else {
  675. Ok(Domain(domain.into_string().into_ascii_lower()))
  676. }
  677. }
  678. }
  679. pub fn serialize(&self) -> String {
  680. self.to_string()
  681. }
  682. }
  683. impl Show for Host {
  684. fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
  685. match *self {
  686. Domain(ref domain) => domain.fmt(formatter),
  687. Ipv6(ref address) => {
  688. try!(formatter.write(b"["));
  689. try!(address.fmt(formatter));
  690. formatter.write(b"]")
  691. }
  692. }
  693. }
  694. }
  695. impl Ipv6Address {
  696. pub fn parse(input: &str) -> ParseResult<Ipv6Address> {
  697. let input = input.as_bytes();
  698. let len = input.len();
  699. let mut is_ip_v4 = false;
  700. let mut pieces = [0, 0, 0, 0, 0, 0, 0, 0];
  701. let mut piece_pointer = 0u;
  702. let mut compress_pointer = None;
  703. let mut i = 0u;
  704. if input[0] == b':' {
  705. if input[1] != b':' {
  706. return Err("Invalid IPv6 address")
  707. }
  708. i = 2;
  709. piece_pointer = 1;
  710. compress_pointer = Some(1u);
  711. }
  712. while i < len {
  713. if piece_pointer == 8 {
  714. return Err("Invalid IPv6 address")
  715. }
  716. if input[i] == b':' {
  717. if compress_pointer.is_some() {
  718. return Err("Invalid IPv6 address")
  719. }
  720. i += 1;
  721. piece_pointer += 1;
  722. compress_pointer = Some(piece_pointer);
  723. continue
  724. }
  725. let start = i;
  726. let end = cmp::min(len, start + 4);
  727. let mut value = 0u16;
  728. while i < end {
  729. match from_hex(input[i]) {
  730. Some(digit) => {
  731. value = value * 0x10 + digit as u16;
  732. i += 1;
  733. },
  734. None => break
  735. }
  736. }
  737. if i < len {
  738. match input[i] {
  739. b'.' => {
  740. if i == start {
  741. return Err("Invalid IPv6 address")
  742. }
  743. i = start;
  744. is_ip_v4 = true;
  745. },
  746. b':' => {
  747. i += 1;
  748. if i == len {
  749. return Err("Invalid IPv6 address")
  750. }
  751. },
  752. _ => return Err("Invalid IPv6 address")
  753. }
  754. }
  755. if is_ip_v4 {
  756. break
  757. }
  758. pieces[piece_pointer] = value;
  759. piece_pointer += 1;
  760. }
  761. if is_ip_v4 {
  762. if piece_pointer > 6 {
  763. return Err("Invalid IPv6 address")
  764. }
  765. let mut dots_seen = 0u;
  766. while i < len {
  767. let mut value = 0u16;
  768. while i < len {
  769. let digit = match input[i] {
  770. c @ b'0' .. b'9' => c - b'0',
  771. _ => break
  772. };
  773. value = value * 10 + digit as u16;
  774. if value == 0 || value > 255 {
  775. return Err("Invalid IPv6 address")
  776. }
  777. }
  778. if dots_seen < 3 && !(i < len && input[i] == b'.') {
  779. return Err("Invalid IPv6 address")
  780. }
  781. pieces[piece_pointer] = pieces[piece_pointer] * 0x100 + value;
  782. if dots_seen == 0 || dots_seen == 2 {
  783. piece_pointer += 1;
  784. }
  785. i += 1;
  786. if dots_seen == 3 && i < len {
  787. return Err("Invalid IPv6 address")
  788. }
  789. dots_seen += 1;
  790. }
  791. }
  792. match compress_pointer {
  793. Some(compress_pointer) => {
  794. let mut swaps = piece_pointer - compress_pointer;
  795. piece_pointer = 7;
  796. while swaps > 0 {
  797. pieces[piece_pointer] = pieces[compress_pointer + swaps - 1];
  798. pieces[compress_pointer + swaps - 1] = 0;
  799. swaps -= 1;
  800. piece_pointer -= 1;
  801. }
  802. }
  803. _ => if piece_pointer != 8 {
  804. return Err("Invalid IPv6 address")
  805. }
  806. }
  807. Ok(Ipv6Address { pieces: pieces })
  808. }
  809. pub fn serialize(&self) -> String {
  810. self.to_string()
  811. }
  812. }
  813. impl Show for Ipv6Address {
  814. fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
  815. let (compress_start, compress_end) = longest_zero_sequence(&self.pieces);
  816. let mut i = 0;
  817. while i < 8 {
  818. if i == compress_start {
  819. try!(formatter.write(b":"));
  820. if i == 0 {
  821. try!(formatter.write(b":"));
  822. }
  823. if compress_end < 8 {
  824. i = compress_end;
  825. } else {
  826. break;
  827. }
  828. }
  829. try!(write!(formatter, "{:X}", self.pieces[i as uint]));
  830. if i < 7 {
  831. try!(formatter.write(b":"));
  832. }
  833. i += 1;
  834. }
  835. Ok(())
  836. }
  837. }
  838. fn longest_zero_sequence(pieces: &[u16, ..8]) -> (int, int) {
  839. let mut longest = -1;
  840. let mut longest_length = -1;
  841. let mut start = -1;
  842. macro_rules! finish_sequence(
  843. ($end: expr) => {
  844. if start >= 0 {
  845. let length = $end - start;
  846. if length > longest_length {
  847. longest = start;
  848. longest_length = length;
  849. }
  850. }
  851. };
  852. );
  853. for i in range(0, 8) {
  854. if pieces[i as uint] == 0 {
  855. if start < 0 {
  856. start = i;
  857. }
  858. } else {
  859. finish_sequence!(i);
  860. start = -1;
  861. }
  862. }
  863. finish_sequence!(8);
  864. (longest, longest + longest_length)
  865. }
  866. #[inline]
  867. fn from_hex(byte: u8) -> Option<u8> {
  868. match byte {
  869. b'0' .. b'9' => Some(byte - b'0'), // 0..9
  870. b'A' .. b'F' => Some(byte + 10 - b'A'), // A..F
  871. b'a' .. b'f' => Some(byte + 10 - b'a'), // a..f
  872. _ => None
  873. }
  874. }
  875. pub struct EncodeSet {
  876. map: &'static [&'static str, ..256],
  877. }
  878. pub static SIMPLE_ENCODE_SET: EncodeSet = EncodeSet { map: &encode_sets::SIMPLE };
  879. pub static QUERY_ENCODE_SET: EncodeSet = EncodeSet { map: &encode_sets::QUERY };
  880. pub static DEFAULT_ENCODE_SET: EncodeSet = EncodeSet { map: &encode_sets::DEFAULT };
  881. pub static USERINFO_ENCODE_SET: EncodeSet = EncodeSet { map: &encode_sets::USERINFO };
  882. pub static PASSWORD_ENCODE_SET: EncodeSet = EncodeSet { map: &encode_sets::PASSWORD };
  883. pub static USERNAME_ENCODE_SET: EncodeSet = EncodeSet { map: &encode_sets::USERNAME };
  884. pub static FORM_URLENCODED_ENCODE_SET: EncodeSet = EncodeSet { map: &encode_sets::FORM_URLENCODED };
  885. #[inline]
  886. pub fn percent_encode_to(input: &[u8], encode_set: EncodeSet, output: &mut String) {
  887. for &byte in input.iter() {
  888. output.push_str(encode_set.map[byte as uint])
  889. }
  890. }
  891. /// Percent-encode the given bytes.
  892. ///
  893. /// The returned string. is within the ASCII range.
  894. #[inline]
  895. pub fn percent_encode(input: &[u8], encode_set: EncodeSet) -> String {
  896. let mut output = String::new();
  897. percent_encode_to(input, encode_set, &mut output);
  898. output
  899. }
  900. #[inline]
  901. pub fn utf8_percent_encode_to(input: &str, encode_set: EncodeSet, output: &mut String) {
  902. percent_encode_to(input.as_bytes(), encode_set, output)
  903. }
  904. #[inline]
  905. pub fn utf8_percent_encode(input: &str, encode_set: EncodeSet) -> String {
  906. let mut output = String::new();
  907. utf8_percent_encode_to(input, encode_set, &mut output);
  908. output
  909. }
  910. pub fn percent_decode_to(input: &[u8], output: &mut Vec<u8>) {
  911. let mut i = 0u;
  912. while i < input.len() {
  913. let c = input[i];
  914. if c == b'%' && i + 2 < input.len() {
  915. match (from_hex(input[i + 1]), from_hex(input[i + 2])) {
  916. (Some(h), Some(l)) => {
  917. output.push(h * 0x10 + l);
  918. i += 3;
  919. continue
  920. },
  921. _ => (),
  922. }
  923. }
  924. output.push(c);
  925. i += 1;
  926. }
  927. }
  928. #[inline]
  929. pub fn percent_decode(input: &[u8]) -> Vec<u8> {
  930. let mut output = Vec::new();
  931. percent_decode_to(input, &mut output);
  932. output
  933. }
  934. #[inline]
  935. pub fn lossy_utf8_percent_decode(input: &[u8]) -> String {
  936. String::from_utf8_lossy(percent_decode(input).as_slice()).into_string()
  937. }
  938. // FIXME: Figure out what to do on Windows
  939. #[cfg(unix)]
  940. fn encode_file_path(path: &Path) -> Result<Vec<String>, ()> {
  941. if !path.is_absolute() {
  942. return Err(())
  943. }
  944. Ok(path.components().map(|c| percent_encode(c, DEFAULT_ENCODE_SET)).collect())
  945. }