url.rs 13 KB

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