فهرست منبع

Add host (and IPv6) serialization. (But without IDNA)

Simon Sapin 12 سال پیش
والد
کامیت
0883357026
1فایلهای تغییر یافته به همراه181 افزوده شده و 19 حذف شده
  1. 181 19
      url.rs

+ 181 - 19
url.rs

@@ -38,7 +38,12 @@ pub struct UserInfo {
 
 pub enum Host {
     Domain(~[~str]),
-    IPv6Address([u16, ..8])
+    IPv6(IPv6Address)
+}
+
+
+pub struct IPv6Address {
+    pieces: [u16, ..8]
 }
 
 
@@ -49,38 +54,195 @@ pub fn parse_url(input: &str, base_url: Option<ParsedURL>)
     None
 }
 
+pub type ParseResult<T> = Result<T, &'static str>;
+
 
 impl Host {
+    pub fn parse(input: &str) -> ParseResult<Host> {
+        if input.len() == 0 {
+            Err("Empty host")
+        } else if input[0] == '[' as u8 {
+            if input[input.len()] == ']' as u8 {
+                match IPv6Address::parse(input.slice(1, input.len() - 1)) {
+                    Some(address) => Ok(IPv6(address)),
+                    None => Err("Invalid IPv6 address"),
+                }
+            } else {
+                Err("Invalid IPv6 address")
+            }
+        } else {
+            // TODO: percent-decoding + UTF-8
+            Ok(Domain(input.split_iter(&['.', '\u3002', '\uFF0E', '\uFF61'])
+                           .map(domain_label_to_ascii).collect()))
+        }
+    }
+
     pub fn serialize(&self) -> ~str {
         match *self {
             Domain(ref labels) => labels.connect("."),
-            IPv6Address(ref fields) => {
-                let mut output = ~"[";
-                let (compress_start, compress_end) = longest_zero_sequence(fields);
-                let mut i = 0;
-                while i < 8 {
-                    if i == compress_start {
-                        output.push_str(if i == 0 { "::" } else { ":" });
-                        if compress_end < 8 {
-                            i = compress_end;
-                        } else {
-                            break;
+            IPv6(ref address) => format!("[{}]", address.serialize()),
+        }
+    }
+}
+
+
+pub fn domain_label_to_ascii(label: &str) -> ~str {
+    // TODO: IDNA2003 ToASCII algorithm with the AllowUnassigned flag set
+    // and the version of Unicode used being the most recent version
+    // rather than Unicode 3.2.
+    // http://tools.ietf.org/html/rfc3490#section-4.1
+    label.to_owned()
+}
+
+
+macro_rules! matches(
+    ($value: expr, ($pattern: pat)|+) => {
+        match $value {
+            $($pattern)|+ => true,
+            _ => false,
+        }
+    };
+)
+
+
+impl IPv6Address {
+    pub fn parse(input: &str) -> Option<IPv6Address> {
+        let len = input.len();
+        let mut is_ip_v4 = false;
+        let mut pieces = [0, 0, 0, 0, 0, 0, 0, 0];
+        let mut piece_pointer = 0u;
+        let mut compress_pointer = None;
+        let mut i = 0u;
+        if input[0] == ':' as u8 {
+            if input[1] != ':' as u8 {
+                return None
+            }
+            i = 2;
+            piece_pointer = 1;
+            compress_pointer = Some(1u);
+        }
+
+        while i < len {
+            if piece_pointer == 8 {
+                return None
+            }
+            if input[i] == ':' as u8 {
+                if compress_pointer.is_some() {
+                    return None
+                }
+                piece_pointer += 1;
+                compress_pointer = Some(piece_pointer);
+                continue
+            }
+            let start = i;
+            let end = len.min(&(start + 4));
+            let mut value = 0u16;
+            while i < end {
+                let digit = match input[i] {
+                    c @ 0x30 .. 0x39 => c - 0x30,  // 0..9
+                    c @ 0x41 .. 0x46 => c + 10 - 0x41,  // A..F
+                    c @ 0x61 .. 0x66 => c + 10 - 0x61,  // a..f
+                    0x2E => {  // .
+                        if i == start {
+                            return None
                         }
+                        i = start;
+                        is_ip_v4 = true;
+                        break
+                    },
+                    0x3A => { // :
+                        i += 1;
+                        if i == len {
+                            return None
+                        }
+                        break
                     }
-                    output.push_str(fields[i].to_str_radix(16));
-                    if i < 7 {
-                        output.push_str(":");
+                    _ => return None
+                };
+                value = value * 0x10 + digit as u16;
+                i += 1;
+            }
+            if is_ip_v4 {
+                break
+            }
+            pieces[piece_pointer] = value;
+            piece_pointer += 1;
+        }
+
+        if is_ip_v4 {
+            if piece_pointer > 6 {
+                return None
+            }
+            let mut dots_seen = 0u;
+            while i < len {
+                let mut value = 0u16;
+                while i < len {
+                    let digit = match input[i] {
+                        c @ 0x30 .. 0x39 => c - 0x30,  // 0..9
+                        _ => break
+                    };
+                    value = value * 10 + digit as u16;
+                    if value > 255 {
+                        return None
                     }
                 }
-                output.push_str("]");
-                output
+                if dots_seen < 3 && !(i < len && input[i] == '.' as u8) {
+                    return None
+                }
+                pieces[piece_pointer] = pieces[piece_pointer] * 0x100 + value;
+                if dots_seen == 0 || dots_seen == 2 {
+                    piece_pointer += 1;
+                }
+                i += 1;
+                if dots_seen == 3 && i < len {
+                    return None
+                }
+                dots_seen += 1;
+            }
+        }
+
+        match compress_pointer {
+            Some(compress_pointer) => {
+                let mut swaps = piece_pointer - compress_pointer;
+                piece_pointer = 7;
+                while swaps > 0 {
+                    pieces[piece_pointer] = pieces[compress_pointer + swaps - 1];
+                    pieces[compress_pointer + swaps - 1] = 0;
+                    swaps -= 1;
+                    piece_pointer -= 1;
+                }
+            }
+            _ => if piece_pointer != 8 {
+                return None
+            }
+        }
+        Some(IPv6Address { pieces: pieces })
+    }
+
+    pub fn serialize(&self) -> ~str {
+        let mut output = ~"";
+        let (compress_start, compress_end) = longest_zero_sequence(&self.pieces);
+        let mut i = 0;
+        while i < 8 {
+            if i == compress_start {
+                output.push_str(if i == 0 { "::" } else { ":" });
+                if compress_end < 8 {
+                    i = compress_end;
+                } else {
+                    break;
+                }
+            }
+            output.push_str(self.pieces[i].to_str_radix(16));
+            if i < 7 {
+                output.push_str(":");
             }
         }
+        output
     }
 }
 
 
-fn longest_zero_sequence(fields: &[u16, ..8]) -> (int, int) {
+fn longest_zero_sequence(pieces: &[u16, ..8]) -> (int, int) {
     let mut longest = -1;
     let mut longest_length = -1;
     let mut start = -1;
@@ -96,7 +258,7 @@ fn longest_zero_sequence(fields: &[u16, ..8]) -> (int, int) {
         };
     );
     for i in range(0, 8) {
-        if fields[i] == 0 {
+        if pieces[i] == 0 {
             if start < 0 {
                 start = i;
             }