url.rs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  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#0.1"]
  9. #![crate_type = "lib"]
  10. #![feature(macro_rules)]
  11. extern crate encoding;
  12. #[cfg(test)]
  13. extern crate serialize;
  14. use std::str;
  15. use std::cmp;
  16. use std::num::ToStrRadix;
  17. use encoding::Encoding;
  18. use encoding::all::UTF_8;
  19. mod parser;
  20. pub mod form_urlencoded;
  21. pub mod punycode;
  22. #[cfg(test)]
  23. mod tests;
  24. #[deriving(Clone)]
  25. pub struct Url {
  26. scheme: ~str,
  27. scheme_data: SchemeData,
  28. query: Option<~str>, // See form_urlencoded::parse_str() to get name/value pairs.
  29. fragment: Option<~str>,
  30. }
  31. #[deriving(Clone)]
  32. pub enum SchemeData {
  33. RelativeSchemeData(SchemeRelativeUrl),
  34. OtherSchemeData(~str), // data: URLs, mailto: URLs, etc.
  35. }
  36. #[deriving(Clone)]
  37. pub struct SchemeRelativeUrl {
  38. userinfo: Option<UserInfo>,
  39. host: Host,
  40. port: ~str,
  41. path: ~[~str],
  42. }
  43. #[deriving(Clone)]
  44. pub struct UserInfo {
  45. username: ~str,
  46. password: Option<~str>,
  47. }
  48. #[deriving(Clone)]
  49. pub enum Host {
  50. Domain(~[~str]), // Can only be empty in the file scheme
  51. Ipv6(Ipv6Address)
  52. }
  53. pub struct Ipv6Address {
  54. pieces: [u16, ..8]
  55. }
  56. impl Clone for Ipv6Address {
  57. fn clone(&self) -> Ipv6Address {
  58. Ipv6Address { pieces: self.pieces }
  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. impl Url {
  68. pub fn parse(input: &str, base_url: Option<&Url>) -> ParseResult<Url> {
  69. parser::parse_url(input, base_url)
  70. }
  71. pub fn serialize(&self) -> ~str {
  72. let mut result = self.serialize_no_fragment();
  73. match self.fragment {
  74. None => (),
  75. Some(ref fragment) => {
  76. result.push_str("#");
  77. result.push_str(fragment.as_slice());
  78. }
  79. }
  80. result
  81. }
  82. pub fn serialize_no_fragment(&self) -> ~str {
  83. let mut result = self.scheme.to_owned();
  84. result.push_str(":");
  85. match self.scheme_data {
  86. RelativeSchemeData(SchemeRelativeUrl {
  87. ref userinfo, ref host, ref port, ref path
  88. }) => {
  89. result.push_str("//");
  90. match userinfo {
  91. &None => (),
  92. &Some(UserInfo { ref username, ref password })
  93. => if username.len() > 0 || password.is_some() {
  94. result.push_str(username.as_slice());
  95. match password {
  96. &None => (),
  97. &Some(ref password) => {
  98. result.push_str(":");
  99. result.push_str(password.as_slice());
  100. }
  101. }
  102. result.push_str("@");
  103. }
  104. }
  105. result.push_str(host.serialize());
  106. if port.len() > 0 {
  107. result.push_str(":");
  108. result.push_str(port.as_slice());
  109. }
  110. if path.len() > 0 {
  111. for path_part in path.iter() {
  112. result.push_str("/");
  113. result.push_str(path_part.as_slice());
  114. }
  115. } else {
  116. result.push_str("/");
  117. }
  118. },
  119. OtherSchemeData(ref data) => result.push_str(data.as_slice()),
  120. }
  121. match self.query {
  122. None => (),
  123. Some(ref query) => {
  124. result.push_str("?");
  125. result.push_str(query.as_slice());
  126. }
  127. }
  128. result
  129. }
  130. }
  131. impl Host {
  132. pub fn parse(input: &str) -> ParseResult<Host> {
  133. if input.len() == 0 {
  134. Err("Empty host")
  135. } else if input[0] == '[' as u8 {
  136. if input[input.len() - 1] == ']' as u8 {
  137. Ipv6Address::parse(input.slice(1, input.len() - 1)).map(Ipv6)
  138. } else {
  139. Err("Invalid Ipv6 address")
  140. }
  141. } else {
  142. let mut percent_encoded = ~"";
  143. utf8_percent_encode(input, SimpleEncodeSet, &mut percent_encoded);
  144. let bytes = percent_decode(percent_encoded.as_bytes());
  145. let decoded = UTF_8.decode(bytes, encoding::DecodeReplace).unwrap();
  146. let mut labels = ~[];
  147. for label in decoded.split(&['.', '\u3002', '\uFF0E', '\uFF61']) {
  148. // TODO: Remove this check and use IDNA "domain to ASCII"
  149. // TODO: switch to .map(domain_label_to_ascii).collect() then.
  150. if label.is_ascii() {
  151. labels.push(label.to_owned())
  152. } else {
  153. return Err("Non-ASCII domains (IDNA) are not supported yet.")
  154. }
  155. }
  156. Ok(Domain(labels))
  157. }
  158. }
  159. pub fn serialize(&self) -> ~str {
  160. match *self {
  161. Domain(ref labels) => labels.connect("."),
  162. Ipv6(ref address) => {
  163. let mut result = ~"[";
  164. result.push_str(address.serialize());
  165. result.push_str("]");
  166. result
  167. }
  168. }
  169. }
  170. }
  171. impl Ipv6Address {
  172. pub fn parse(input: &str) -> ParseResult<Ipv6Address> {
  173. let len = input.len();
  174. let mut is_ip_v4 = false;
  175. let mut pieces = [0, 0, 0, 0, 0, 0, 0, 0];
  176. let mut piece_pointer = 0u;
  177. let mut compress_pointer = None;
  178. let mut i = 0u;
  179. if input[0] == ':' as u8 {
  180. if input[1] != ':' as u8 {
  181. return Err("Invalid IPv6 address")
  182. }
  183. i = 2;
  184. piece_pointer = 1;
  185. compress_pointer = Some(1u);
  186. }
  187. while i < len {
  188. if piece_pointer == 8 {
  189. return Err("Invalid IPv6 address")
  190. }
  191. if input[i] == ':' as u8 {
  192. if compress_pointer.is_some() {
  193. return Err("Invalid IPv6 address")
  194. }
  195. i += 1;
  196. piece_pointer += 1;
  197. compress_pointer = Some(piece_pointer);
  198. continue
  199. }
  200. let start = i;
  201. let end = cmp::min(len, start + 4);
  202. let mut value = 0u16;
  203. while i < end {
  204. match from_hex(input[i]) {
  205. Some(digit) => {
  206. value = value * 0x10 + digit as u16;
  207. i += 1;
  208. },
  209. None => break
  210. }
  211. }
  212. if i < len {
  213. match input[i] as char {
  214. '.' => {
  215. if i == start {
  216. return Err("Invalid IPv6 address")
  217. }
  218. i = start;
  219. is_ip_v4 = true;
  220. },
  221. ':' => {
  222. i += 1;
  223. if i == len {
  224. return Err("Invalid IPv6 address")
  225. }
  226. },
  227. _ => return Err("Invalid IPv6 address")
  228. }
  229. }
  230. if is_ip_v4 {
  231. break
  232. }
  233. pieces[piece_pointer] = value;
  234. piece_pointer += 1;
  235. }
  236. if is_ip_v4 {
  237. if piece_pointer > 6 {
  238. return Err("Invalid IPv6 address")
  239. }
  240. let mut dots_seen = 0u;
  241. while i < len {
  242. let mut value = 0u16;
  243. while i < len {
  244. let digit = match input[i] {
  245. c @ 0x30 .. 0x39 => c - 0x30, // 0..9
  246. _ => break
  247. };
  248. value = value * 10 + digit as u16;
  249. if value > 255 {
  250. return Err("Invalid IPv6 address")
  251. }
  252. }
  253. if dots_seen < 3 && !(i < len && input[i] == '.' as u8) {
  254. return Err("Invalid IPv6 address")
  255. }
  256. pieces[piece_pointer] = pieces[piece_pointer] * 0x100 + value;
  257. if dots_seen == 0 || dots_seen == 2 {
  258. piece_pointer += 1;
  259. }
  260. i += 1;
  261. if dots_seen == 3 && i < len {
  262. return Err("Invalid IPv6 address")
  263. }
  264. dots_seen += 1;
  265. }
  266. }
  267. match compress_pointer {
  268. Some(compress_pointer) => {
  269. let mut swaps = piece_pointer - compress_pointer;
  270. piece_pointer = 7;
  271. while swaps > 0 {
  272. pieces[piece_pointer] = pieces[compress_pointer + swaps - 1];
  273. pieces[compress_pointer + swaps - 1] = 0;
  274. swaps -= 1;
  275. piece_pointer -= 1;
  276. }
  277. }
  278. _ => if piece_pointer != 8 {
  279. return Err("Invalid IPv6 address")
  280. }
  281. }
  282. Ok(Ipv6Address { pieces: pieces })
  283. }
  284. pub fn serialize(&self) -> ~str {
  285. let mut output = ~"";
  286. let (compress_start, compress_end) = longest_zero_sequence(&self.pieces);
  287. let mut i = 0;
  288. while i < 8 {
  289. if i == compress_start {
  290. output.push_str(":");
  291. if i == 0 {
  292. output.push_str(":");
  293. }
  294. if compress_end < 8 {
  295. i = compress_end;
  296. } else {
  297. break;
  298. }
  299. }
  300. output.push_str(self.pieces[i].to_str_radix(16));
  301. if i < 7 {
  302. output.push_str(":");
  303. }
  304. i += 1;
  305. }
  306. output
  307. }
  308. }
  309. fn longest_zero_sequence(pieces: &[u16, ..8]) -> (int, int) {
  310. let mut longest = -1;
  311. let mut longest_length = -1;
  312. let mut start = -1;
  313. macro_rules! finish_sequence(
  314. ($end: expr) => {
  315. if start >= 0 {
  316. let length = $end - start;
  317. if length > longest_length {
  318. longest = start;
  319. longest_length = length;
  320. }
  321. }
  322. };
  323. );
  324. for i in range(0, 8) {
  325. if pieces[i] == 0 {
  326. if start < 0 {
  327. start = i;
  328. }
  329. } else {
  330. finish_sequence!(i);
  331. start = -1;
  332. }
  333. }
  334. finish_sequence!(8);
  335. (longest, longest + longest_length)
  336. }
  337. #[inline]
  338. fn from_hex(byte: u8) -> Option<u8> {
  339. match byte {
  340. 0x30 .. 0x39 => Some(byte - 0x30), // 0..9
  341. 0x41 .. 0x46 => Some(byte + 10 - 0x41), // A..F
  342. 0x61 .. 0x66 => Some(byte + 10 - 0x61), // a..f
  343. _ => None
  344. }
  345. }
  346. #[inline]
  347. fn to_hex_upper(value: u8) -> u8 {
  348. match value {
  349. 0 .. 9 => value + 0x30,
  350. 10 .. 15 => value - 10 + 0x41,
  351. _ => fail!()
  352. }
  353. }
  354. enum EncodeSet {
  355. SimpleEncodeSet,
  356. DefaultEncodeSet,
  357. UserInfoEncodeSet,
  358. PasswordEncodeSet,
  359. UsernameEncodeSet
  360. }
  361. #[inline]
  362. fn utf8_percent_encode(input: &str, encode_set: EncodeSet, output: &mut ~str) {
  363. use Default = self::DefaultEncodeSet;
  364. use UserInfo = self::UserInfoEncodeSet;
  365. use Password = self::PasswordEncodeSet;
  366. use Username = self::UsernameEncodeSet;
  367. for byte in input.bytes() {
  368. if byte < 0x20 || byte > 0x7E || match byte as char {
  369. ' ' | '"' | '#' | '<' | '>' | '?' | '`'
  370. => is_match!(encode_set, Default | UserInfo | Password | Username),
  371. '@'
  372. => is_match!(encode_set, UserInfo | Password | Username),
  373. '/' | '\\'
  374. => is_match!(encode_set, Password | Username),
  375. ':'
  376. => is_match!(encode_set, Username),
  377. _ => false,
  378. } {
  379. percent_encode_byte(byte, output)
  380. } else {
  381. unsafe { str::raw::push_byte(output, byte) }
  382. }
  383. }
  384. }
  385. #[inline]
  386. fn percent_encode_byte(byte: u8, output: &mut ~str) {
  387. unsafe {
  388. str::raw::push_bytes(output, [
  389. '%' as u8, to_hex_upper(byte >> 4), to_hex_upper(byte & 0x0F)
  390. ])
  391. }
  392. }
  393. #[inline]
  394. fn percent_decode(input: &[u8]) -> ~[u8] {
  395. let mut output = ~[];
  396. let mut i = 0u;
  397. while i < input.len() {
  398. let c = input[i];
  399. if c == ('%' as u8) && i + 2 < input.len() {
  400. match (from_hex(input[i + 1]), from_hex(input[i + 2])) {
  401. (Some(h), Some(l)) => {
  402. output.push(h * 0x10 + l);
  403. i += 3;
  404. continue
  405. },
  406. _ => (),
  407. }
  408. }
  409. output.push(c);
  410. i += 1;
  411. }
  412. output
  413. }