url.rs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
  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_id = "url_"]
  9. #![crate_type = "lib"]
  10. #![feature(macro_rules)]
  11. extern crate encoding;
  12. #[cfg(test)]
  13. extern crate serialize;
  14. use std::cmp;
  15. use std::str::from_utf8_lossy;
  16. use encoding::all::UTF_8;
  17. use encoding::types::EncodingRef;
  18. mod parser;
  19. pub mod form_urlencoded;
  20. pub mod punycode;
  21. #[cfg(test)]
  22. mod tests;
  23. #[deriving(Clone, Show)]
  24. pub struct Url {
  25. scheme: String,
  26. scheme_data: SchemeData,
  27. query: Option<String>, // See form_urlencoded::parse_str() to get name/value pairs.
  28. fragment: Option<String>,
  29. }
  30. #[deriving(Clone, Show)]
  31. pub enum SchemeData {
  32. RelativeSchemeData(SchemeRelativeUrl),
  33. OtherSchemeData(String), // data: URLs, mailto: URLs, etc.
  34. }
  35. #[deriving(Clone, Show)]
  36. pub struct SchemeRelativeUrl {
  37. username: String,
  38. password: Option<String>,
  39. host: Host,
  40. port: String,
  41. path: Vec<String>,
  42. }
  43. #[deriving(Clone, Show)]
  44. pub enum Host {
  45. Domain(String),
  46. Ipv6(Ipv6Address)
  47. }
  48. pub struct Ipv6Address {
  49. pieces: [u16, ..8]
  50. }
  51. impl Clone for Ipv6Address {
  52. fn clone(&self) -> Ipv6Address {
  53. Ipv6Address { pieces: self.pieces }
  54. }
  55. }
  56. impl ::std::fmt::Show for Ipv6Address {
  57. fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
  58. self.pieces.as_slice().fmt(formatter)
  59. }
  60. }
  61. macro_rules! is_match(
  62. ($value:expr, $($pattern:pat)|+) => (
  63. match $value { $($pattern)|+ => true, _ => false }
  64. );
  65. )
  66. pub type ParseResult<T> = Result<T, &'static str>;
  67. /// This is called on non-fatal parse errors.
  68. /// The handler can choose to continue or abort parsing by returning Ok() or Err(), respectively.
  69. /// FIXME: make this a by-ref closure when that’s supported.
  70. pub type ErrorHandler = fn(reason: &'static str) -> ParseResult<()>;
  71. fn silent_handler(_reason: &'static str) -> ParseResult<()> {
  72. Ok(())
  73. }
  74. impl Url {
  75. pub fn parse(input: &str, base_url: Option<&Url>)
  76. -> ParseResult<Url> {
  77. parser::parse_url(input, base_url, silent_handler)
  78. }
  79. /// `URLUtils.protocol` setter
  80. pub fn set_scheme(&mut self, input: &str) -> ParseResult<()> {
  81. match parser::parse_scheme(input.as_slice(), /* in_setter = */ true) {
  82. Some((scheme, _)) => {
  83. self.scheme = scheme;
  84. Ok(())
  85. },
  86. None => Err("Invalid scheme"),
  87. }
  88. }
  89. /// `URLUtils.username` setter
  90. pub fn set_username(&mut self, input: &str) -> ParseResult<()> {
  91. match self.scheme_data {
  92. RelativeSchemeData(SchemeRelativeUrl { ref mut username, .. }) => {
  93. username.truncate(0);
  94. utf8_percent_encode(input, UsernameEncodeSet, username);
  95. Ok(())
  96. },
  97. OtherSchemeData(_) => Err("Can not set username on non-relative URL.")
  98. }
  99. }
  100. /// `URLUtils.password` setter
  101. pub fn set_password(&mut self, input: &str) -> ParseResult<()> {
  102. match self.scheme_data {
  103. RelativeSchemeData(SchemeRelativeUrl { ref mut password, .. }) => {
  104. let mut new_password = String::new();
  105. utf8_percent_encode(input, PasswordEncodeSet, &mut new_password);
  106. *password = Some(new_password);
  107. Ok(())
  108. },
  109. OtherSchemeData(_) => Err("Can not set password on non-relative URL.")
  110. }
  111. }
  112. /// `URLUtils.host` setter
  113. pub fn set_host_and_port(&mut self, input: &str) -> ParseResult<()> {
  114. match self.scheme_data {
  115. RelativeSchemeData(SchemeRelativeUrl { ref mut host, ref mut port, .. }) => {
  116. let (new_host, new_port, _) = try!(parser::parse_hostname(
  117. input, self.scheme.as_slice(), silent_handler, /* skip_port = */ false));
  118. *host = new_host;
  119. *port = new_port;
  120. Ok(())
  121. },
  122. OtherSchemeData(_) => Err("Can not set host/port on non-relative URL.")
  123. }
  124. }
  125. /// `URLUtils.hostname` setter
  126. pub fn set_host(&mut self, input: &str) -> ParseResult<()> {
  127. match self.scheme_data {
  128. RelativeSchemeData(SchemeRelativeUrl { ref mut host, .. }) => {
  129. let (new_host, _, _) = try!(parser::parse_hostname(
  130. input, self.scheme.as_slice(), silent_handler, /* skip_port = */ true));
  131. *host = new_host;
  132. Ok(())
  133. },
  134. OtherSchemeData(_) => Err("Can not set host on non-relative URL.")
  135. }
  136. }
  137. /// `URLUtils.port` setter
  138. pub fn set_port(&mut self, input: &str) -> ParseResult<()> {
  139. match self.scheme_data {
  140. RelativeSchemeData(SchemeRelativeUrl { ref mut port, .. }) => {
  141. if self.scheme.as_slice() == "file" {
  142. return Err("Can not set port on file: URL.")
  143. }
  144. let (new_port, _) = try!(parser::parse_port(
  145. input, self.scheme.as_slice(), silent_handler));
  146. *port = new_port;
  147. Ok(())
  148. },
  149. OtherSchemeData(_) => Err("Can not set port on non-relative URL.")
  150. }
  151. }
  152. /// `URLUtils.pathname` setter
  153. pub fn set_path(&mut self, input: &str) -> ParseResult<()> {
  154. match self.scheme_data {
  155. RelativeSchemeData(SchemeRelativeUrl { ref mut path, .. }) => {
  156. let (new_path, _) = try!(parser::parse_path_start(
  157. input, /* full_url = */ false,
  158. self.scheme.as_slice() == "file", silent_handler));
  159. *path = new_path;
  160. Ok(())
  161. },
  162. OtherSchemeData(_) => Err("Can not set path on non-relative URL.")
  163. }
  164. }
  165. /// `URLUtils.search` setter
  166. pub fn set_query(&mut self, input: &str) -> ParseResult<()> {
  167. // FIXME: This is in the spec, but seems superfluous.
  168. match self.scheme_data {
  169. RelativeSchemeData(_) => (),
  170. OtherSchemeData(_) => return Err("Can not set query on non-relative URL.")
  171. }
  172. self.query = if input.is_empty() {
  173. None
  174. } else {
  175. let input = if input.starts_with("?") { input.slice_from(1) } else { input };
  176. let encoding_override = UTF_8 as EncodingRef; // TODO
  177. let (new_query, _) = try!(parser::parse_query(
  178. input, encoding_override, /* full_url = */ false, silent_handler));
  179. Some(new_query)
  180. };
  181. Ok(())
  182. }
  183. /// `URLUtils.hash` setter
  184. pub fn set_fragment(&mut self, input: &str) -> ParseResult<()> {
  185. if self.scheme.as_slice() == "javascript" {
  186. return Err("Can not set fragment on a javascript: URL.")
  187. }
  188. self.fragment = if input.is_empty() {
  189. None
  190. } else {
  191. let input = if input.starts_with("#") { input.slice_from(1) } else { input };
  192. Some(try!(parser::parse_fragment(input, silent_handler)))
  193. };
  194. Ok(())
  195. }
  196. pub fn serialize(&self) -> String {
  197. let mut result = self.serialize_no_fragment();
  198. match self.fragment {
  199. None => (),
  200. Some(ref fragment) => {
  201. result.push_str("#");
  202. result.push_str(fragment.as_slice());
  203. }
  204. }
  205. result
  206. }
  207. pub fn serialize_no_fragment(&self) -> String {
  208. let mut result = self.scheme.clone();
  209. result.push_str(":");
  210. match self.scheme_data {
  211. RelativeSchemeData(SchemeRelativeUrl {
  212. ref username, ref password, ref host, ref port, ref path
  213. }) => {
  214. result.push_str("//");
  215. if !username.is_empty() || password.is_some() {
  216. result.push_str(username.as_slice());
  217. match password {
  218. &None => (),
  219. &Some(ref password) => {
  220. result.push_str(":");
  221. result.push_str(password.as_slice());
  222. }
  223. }
  224. result.push_str("@");
  225. }
  226. result.push_str(host.serialize().as_slice());
  227. if port.len() > 0 {
  228. result.push_str(":");
  229. result.push_str(port.as_slice());
  230. }
  231. if path.len() > 0 {
  232. for path_part in path.iter() {
  233. result.push_str("/");
  234. result.push_str(path_part.as_slice());
  235. }
  236. } else {
  237. result.push_str("/");
  238. }
  239. },
  240. OtherSchemeData(ref data) => result.push_str(data.as_slice()),
  241. }
  242. match self.query {
  243. None => (),
  244. Some(ref query) => {
  245. result.push_str("?");
  246. result.push_str(query.as_slice());
  247. }
  248. }
  249. result
  250. }
  251. }
  252. impl Host {
  253. pub fn parse(input: &str) -> ParseResult<Host> {
  254. if input.len() == 0 {
  255. Err("Empty host")
  256. } else if input.starts_with("[") {
  257. if input.ends_with("]") {
  258. Ipv6Address::parse(input.slice(1, input.len() - 1)).map(Ipv6)
  259. } else {
  260. Err("Invalid Ipv6 address")
  261. }
  262. } else {
  263. let decoded = percent_decode(input.as_bytes());
  264. let domain = from_utf8_lossy(decoded.as_slice());
  265. // TODO: Remove this check and use IDNA "domain to ASCII"
  266. if !domain.as_slice().is_ascii() {
  267. Err("Non-ASCII domains (IDNA) are not supported yet.")
  268. } else if domain.as_slice().find(&[
  269. '\0', '\t', '\n', '\r', ' ', '#', '%', '/', ':', '?', '@', '[', '\\', ']'
  270. ]).is_some() {
  271. Err("Invalid domain character.")
  272. } else {
  273. Ok(Domain(domain.into_string()))
  274. }
  275. }
  276. }
  277. pub fn serialize(&self) -> String {
  278. match *self {
  279. Domain(ref domain) => domain.clone(),
  280. Ipv6(ref address) => {
  281. let mut result = String::from_str("[");
  282. result.push_str(address.serialize().as_slice());
  283. result.push_str("]");
  284. result
  285. }
  286. }
  287. }
  288. }
  289. impl Ipv6Address {
  290. pub fn parse(input: &str) -> ParseResult<Ipv6Address> {
  291. let input = input.as_bytes();
  292. let len = input.len();
  293. let mut is_ip_v4 = false;
  294. let mut pieces = [0, 0, 0, 0, 0, 0, 0, 0];
  295. let mut piece_pointer = 0u;
  296. let mut compress_pointer = None;
  297. let mut i = 0u;
  298. if input[0] == b':' {
  299. if input[1] != b':' {
  300. return Err("Invalid IPv6 address")
  301. }
  302. i = 2;
  303. piece_pointer = 1;
  304. compress_pointer = Some(1u);
  305. }
  306. while i < len {
  307. if piece_pointer == 8 {
  308. return Err("Invalid IPv6 address")
  309. }
  310. if input[i] == b':' {
  311. if compress_pointer.is_some() {
  312. return Err("Invalid IPv6 address")
  313. }
  314. i += 1;
  315. piece_pointer += 1;
  316. compress_pointer = Some(piece_pointer);
  317. continue
  318. }
  319. let start = i;
  320. let end = cmp::min(len, start + 4);
  321. let mut value = 0u16;
  322. while i < end {
  323. match from_hex(input[i]) {
  324. Some(digit) => {
  325. value = value * 0x10 + digit as u16;
  326. i += 1;
  327. },
  328. None => break
  329. }
  330. }
  331. if i < len {
  332. match input[i] {
  333. b'.' => {
  334. if i == start {
  335. return Err("Invalid IPv6 address")
  336. }
  337. i = start;
  338. is_ip_v4 = true;
  339. },
  340. b':' => {
  341. i += 1;
  342. if i == len {
  343. return Err("Invalid IPv6 address")
  344. }
  345. },
  346. _ => return Err("Invalid IPv6 address")
  347. }
  348. }
  349. if is_ip_v4 {
  350. break
  351. }
  352. pieces[piece_pointer] = value;
  353. piece_pointer += 1;
  354. }
  355. if is_ip_v4 {
  356. if piece_pointer > 6 {
  357. return Err("Invalid IPv6 address")
  358. }
  359. let mut dots_seen = 0u;
  360. while i < len {
  361. let mut value = 0u16;
  362. while i < len {
  363. let digit = match input[i] {
  364. c @ b'0' .. b'9' => c - b'0',
  365. _ => break
  366. };
  367. value = value * 10 + digit as u16;
  368. if value == 0 || value > 255 {
  369. return Err("Invalid IPv6 address")
  370. }
  371. }
  372. if dots_seen < 3 && !(i < len && input[i] == b'.') {
  373. return Err("Invalid IPv6 address")
  374. }
  375. pieces[piece_pointer] = pieces[piece_pointer] * 0x100 + value;
  376. if dots_seen == 0 || dots_seen == 2 {
  377. piece_pointer += 1;
  378. }
  379. i += 1;
  380. if dots_seen == 3 && i < len {
  381. return Err("Invalid IPv6 address")
  382. }
  383. dots_seen += 1;
  384. }
  385. }
  386. match compress_pointer {
  387. Some(compress_pointer) => {
  388. let mut swaps = piece_pointer - compress_pointer;
  389. piece_pointer = 7;
  390. while swaps > 0 {
  391. pieces[piece_pointer] = pieces[compress_pointer + swaps - 1];
  392. pieces[compress_pointer + swaps - 1] = 0;
  393. swaps -= 1;
  394. piece_pointer -= 1;
  395. }
  396. }
  397. _ => if piece_pointer != 8 {
  398. return Err("Invalid IPv6 address")
  399. }
  400. }
  401. Ok(Ipv6Address { pieces: pieces })
  402. }
  403. pub fn serialize(&self) -> String {
  404. let mut output = String::new();
  405. let (compress_start, compress_end) = longest_zero_sequence(&self.pieces);
  406. let mut i = 0;
  407. while i < 8 {
  408. if i == compress_start {
  409. output.push_str(":");
  410. if i == 0 {
  411. output.push_str(":");
  412. }
  413. if compress_end < 8 {
  414. i = compress_end;
  415. } else {
  416. break;
  417. }
  418. }
  419. output.push_str(format!("{:X}", self.pieces[i as uint]).as_slice());
  420. if i < 7 {
  421. output.push_str(":");
  422. }
  423. i += 1;
  424. }
  425. output
  426. }
  427. }
  428. fn longest_zero_sequence(pieces: &[u16, ..8]) -> (int, int) {
  429. let mut longest = -1;
  430. let mut longest_length = -1;
  431. let mut start = -1;
  432. macro_rules! finish_sequence(
  433. ($end: expr) => {
  434. if start >= 0 {
  435. let length = $end - start;
  436. if length > longest_length {
  437. longest = start;
  438. longest_length = length;
  439. }
  440. }
  441. };
  442. );
  443. for i in range(0, 8) {
  444. if pieces[i as uint] == 0 {
  445. if start < 0 {
  446. start = i;
  447. }
  448. } else {
  449. finish_sequence!(i);
  450. start = -1;
  451. }
  452. }
  453. finish_sequence!(8);
  454. (longest, longest + longest_length)
  455. }
  456. #[inline]
  457. fn from_hex(byte: u8) -> Option<u8> {
  458. match byte {
  459. b'0' .. b'9' => Some(byte - b'0'), // 0..9
  460. b'A' .. b'F' => Some(byte + 10 - b'A'), // A..F
  461. b'a' .. b'f' => Some(byte + 10 - b'a'), // a..f
  462. _ => None
  463. }
  464. }
  465. #[inline]
  466. fn to_hex_upper(value: u8) -> u8 {
  467. match value {
  468. 0 .. 9 => b'0' + value,
  469. 10 .. 15 => b'A' + value - 10,
  470. _ => fail!()
  471. }
  472. }
  473. enum EncodeSet {
  474. SimpleEncodeSet,
  475. DefaultEncodeSet,
  476. UserInfoEncodeSet,
  477. PasswordEncodeSet,
  478. UsernameEncodeSet
  479. }
  480. #[inline]
  481. fn utf8_percent_encode(input: &str, encode_set: EncodeSet, output: &mut String) {
  482. use Default = self::DefaultEncodeSet;
  483. use UserInfo = self::UserInfoEncodeSet;
  484. use Password = self::PasswordEncodeSet;
  485. use Username = self::UsernameEncodeSet;
  486. for byte in input.bytes() {
  487. if byte < 0x20 || byte > 0x7E || match byte {
  488. b' ' | b'"' | b'#' | b'<' | b'>' | b'?' | b'`'
  489. => is_match!(encode_set, Default | UserInfo | Password | Username),
  490. b'@'
  491. => is_match!(encode_set, UserInfo | Password | Username),
  492. b'/' | b'\\'
  493. => is_match!(encode_set, Password | Username),
  494. b':'
  495. => is_match!(encode_set, Username),
  496. _ => false,
  497. } {
  498. percent_encode_byte(byte, output)
  499. } else {
  500. unsafe { output.push_byte(byte) }
  501. }
  502. }
  503. }
  504. #[inline]
  505. fn percent_encode_byte(byte: u8, output: &mut String) {
  506. unsafe {
  507. output.push_bytes([
  508. b'%', to_hex_upper(byte >> 4), to_hex_upper(byte & 0x0F)
  509. ])
  510. }
  511. }
  512. #[inline]
  513. fn percent_decode(input: &[u8]) -> Vec<u8> {
  514. let mut output = Vec::new();
  515. let mut i = 0u;
  516. while i < input.len() {
  517. let c = input[i];
  518. if c == b'%' && i + 2 < input.len() {
  519. match (from_hex(input[i + 1]), from_hex(input[i + 2])) {
  520. (Some(h), Some(l)) => {
  521. output.push(h * 0x10 + l);
  522. i += 3;
  523. continue
  524. },
  525. _ => (),
  526. }
  527. }
  528. output.push(c);
  529. i += 1;
  530. }
  531. output
  532. }