Przeglądaj źródła

Restore custom IPv6 serialization.

The one in libstd differs.
Simon Sapin 10 lat temu
rodzic
commit
ff91328be9
2 zmienionych plików z 73 dodań i 2 usunięć
  1. 61 1
      src/host.rs
  2. 12 1
      tests/format.rs

+ 61 - 1
src/host.rs

@@ -77,11 +77,70 @@ impl fmt::Display for Host {
         match *self {
             Host::Domain(ref domain) => domain.fmt(f),
             Host::Ipv4(ref addr) => addr.fmt(f),
-            Host::Ipv6(ref addr) => write!(f, "[{}]", addr),
+            Host::Ipv6(ref addr) => {
+                try!(f.write_str("["));
+                try!(write_ipv6(addr, f));
+                f.write_str("]")
+            }
+        }
+    }
+}
+
+fn write_ipv6(addr: &Ipv6Addr, f: &mut Formatter) -> fmt::Result {
+    let segments = addr.segments();
+    let (compress_start, compress_end) = longest_zero_sequence(&segments);
+    let mut i = 0;
+    while i < 8 {
+        if i == compress_start {
+            try!(f.write_str(":"));
+            if i == 0 {
+                try!(f.write_str(":"));
+            }
+            if compress_end < 8 {
+                i = compress_end;
+            } else {
+                break;
+            }
+        }
+        try!(write!(f, "{:x}", segments[i as usize]));
+        if i < 7 {
+            try!(f.write_str(":"));
         }
+        i += 1;
     }
+    Ok(())
 }
 
+fn longest_zero_sequence(pieces: &[u16; 8]) -> (isize, isize) {
+    let mut longest = -1;
+    let mut longest_length = -1;
+    let mut start = -1;
+    macro_rules! finish_sequence(
+        ($end: expr) => {
+            if start >= 0 {
+                let length = $end - start;
+                if length > longest_length {
+                    longest = start;
+                    longest_length = length;
+                }
+            }
+        };
+    );
+    for i in 0..8 {
+        if pieces[i as usize] == 0 {
+            if start < 0 {
+                start = i;
+            }
+        } else {
+            finish_sequence!(i);
+            start = -1;
+        }
+    }
+    finish_sequence!(8);
+    (longest, longest + longest_length)
+}
+
+
 fn parse_ipv4number(mut input: &str) -> ParseResult<u32> {
     let mut r = 10;
     if input.starts_with("0x") || input.starts_with("0X") {
@@ -123,6 +182,7 @@ fn parse_ipv4addr(input: &str) -> ParseResult<Option<Ipv4Addr>> {
         }
     }
     let mut ipv4 = numbers.pop().expect("a non-empty list of numbers");
+    // Equivalent to: ipv4 >= 256 ** (4 − numbers.len())
     if ipv4 > u32::max_value() >> (8 * numbers.len() as u32)  {
         return Err(ParseError::InvalidIpv4Address);
     }

+ 12 - 1
tests/format.rs

@@ -1,6 +1,6 @@
 extern crate url;
 
-use url::Url;
+use url::{Url, Host};
 use url::format::{PathFormatter, UserInfoFormatter};
 
 #[test]
@@ -18,6 +18,17 @@ fn path_formatting() {
     }
 }
 
+#[test]
+fn host() {
+    // libstd’s `Display for Ipv6Addr` serializes 0:0:0:0:0:0:_:_ and 0:0:0:0:0:ffff:_:_
+    // using IPv4-like syntax, as suggested in https://tools.ietf.org/html/rfc5952#section-4
+    // but https://url.spec.whatwg.org/#concept-ipv6-serializer specifies not to.
+
+    // Not [::0.0.0.2] / [::ffff:0.0.0.2]
+    assert_eq!(Host::parse("[0::2]").unwrap().to_string(), "[::2]");
+    assert_eq!(Host::parse("[0::ffff:0:2]").unwrap().to_string(), "[::ffff:0:2]");
+}
+
 #[test]
 fn userinfo_formatting() {
     // Test data as (username, password, result) tuples.