Simon Sapin 12 år sedan
förälder
incheckning
af590112c2
3 ändrade filer med 76 tillägg och 70 borttagningar
  1. 36 34
      tests.rs
  2. 35 31
      url.rs
  3. 5 5
      urltestdata.txt

+ 36 - 34
tests.rs

@@ -27,46 +27,47 @@ fn test_url_parsing() {
             query: expected_query,
             query: expected_query,
             fragment: expected_fragment
             fragment: expected_fragment
         } = test;
         } = test;
-        let base = URL::parse(base, None).unwrap();
-        let url = URL::parse(input, Some(base));
+        let base = match URL::parse(base, None) {
+            Ok(base) => base,
+            Err(message) => fail!("Error parsing base {:?}: {}", base, message)
+        };
+        let url = URL::parse(input, Some(&base));
         if expected_scheme.is_none() {
         if expected_scheme.is_none() {
-            assert!(url.is_none(), "Expected a parse error");
+            assert!(url.is_err(), "Expected a parse error for URL {:?}", input);
             continue
             continue
         }
         }
-        let URL {
-            scheme: scheme,
-            scheme_data: scheme_data,
-            query: query,
-            fragment: fragment
-        } = url.unwrap();
+        let URL { scheme, scheme_data, query, fragment } = match url {
+            Ok(url) => url,
+            Err(message) => fail!("Error parsing URL {:?}: {}", input, message)
+        };
 
 
         assert_eq!(Some(scheme.as_str_ascii().to_owned()), expected_scheme);
         assert_eq!(Some(scheme.as_str_ascii().to_owned()), expected_scheme);
         match scheme_data {
         match scheme_data {
-            RelativeSchemeData(SchemeRelativeURL {
-                userinfo: userinfo, host: host, port: port, path: path
-            }) => {
+            RelativeSchemeData(SchemeRelativeURL { userinfo, host, port, path }) => {
                 let (username, password) = match userinfo {
                 let (username, password) = match userinfo {
-                    Some(UserInfo { username: username, password: password })
-                    => (Some(username.as_str_ascii().to_owned()), password.map(|p| p.as_str_ascii().to_owned())),
-                    _ => (None, None),
+                    None => (~"", None),
+                    Some(UserInfo { username, password }) => (
+                        username.as_str_ascii().to_owned(),
+                        password.map(|p| p.as_str_ascii().to_owned())),
                 };
                 };
                 assert_eq!(username, expected_username);
                 assert_eq!(username, expected_username);
                 assert_eq!(password, expected_password);
                 assert_eq!(password, expected_password);
                 let host = host.serialize();
                 let host = host.serialize();
-                assert_eq!(Some(host.as_str_ascii().to_owned()), expected_host)
-                assert_eq!(Some(port.as_str_ascii().to_owned()), expected_port);
-                assert_eq!(Some(path.map(|p| p.as_str_ascii().to_owned()).connect("/")), expected_path);
+                assert_eq!(host.as_str_ascii().to_owned(), expected_host)
+                assert_eq!(port.as_str_ascii().to_owned(), expected_port);
+                assert_eq!(Some("/" + path.map(|p| p.as_str_ascii().to_owned()).connect("/")),
+                           expected_path);
             },
             },
             OtherSchemeData(scheme_data) => {
             OtherSchemeData(scheme_data) => {
                 assert_eq!(Some(scheme_data.as_str_ascii().to_owned()), expected_path);
                 assert_eq!(Some(scheme_data.as_str_ascii().to_owned()), expected_path);
-                assert_eq!(None, expected_username);
+                assert_eq!(~"", expected_username);
                 assert_eq!(None, expected_password);
                 assert_eq!(None, expected_password);
-                assert_eq!(None, expected_host);
-                assert_eq!(None, expected_port);
+                assert_eq!(~"", expected_host);
+                assert_eq!(~"", expected_port);
             },
             },
         }
         }
-        assert_eq!(query.map(|p| p.as_str_ascii().to_owned()), expected_query);
-        assert_eq!(fragment.map(|p| p.as_str_ascii().to_owned()), expected_fragment);
+        assert_eq!(query.map(|p| "?" + p.as_str_ascii().to_owned()), expected_query);
+        assert_eq!(fragment.map(|p| "#" + p.as_str_ascii().to_owned()), expected_fragment);
     }
     }
 }
 }
 
 
@@ -74,10 +75,10 @@ struct Test {
     input: ~str,
     input: ~str,
     base: ~str,
     base: ~str,
     scheme: Option<~str>,
     scheme: Option<~str>,
-    username: Option<~str>,
+    username: ~str,
     password: Option<~str>,
     password: Option<~str>,
-    host: Option<~str>,
-    port: Option<~str>,
+    host: ~str,
+    port: ~str,
     path: Option<~str>,
     path: Option<~str>,
     query: Option<~str>,
     query: Option<~str>,
     fragment: Option<~str>,
     fragment: Option<~str>,
@@ -93,31 +94,32 @@ fn parse_test_data(input: &str) -> ~[Test] {
         let input = unescape(pieces.shift());
         let input = unescape(pieces.shift());
         let mut test = Test {
         let mut test = Test {
             input: input,
             input: input,
-            base: if pieces.is_empty() {
+            base: if pieces.is_empty() || pieces[0] == "" {
                 tests[tests.len() - 1].base.to_owned()
                 tests[tests.len() - 1].base.to_owned()
             } else {
             } else {
                 unescape(pieces.shift())
                 unescape(pieces.shift())
             },
             },
             scheme: None,
             scheme: None,
-            username: None,
+            username: ~"",
             password: None,
             password: None,
-            host: None,
-            port: None,
+            host: ~"",
+            port: ~"",
             path: None,
             path: None,
             query: None,
             query: None,
             fragment: None,
             fragment: None,
         };
         };
         for piece in pieces.move_iter() {
         for piece in pieces.move_iter() {
-            if piece != "" || piece[0] == ('#' as u8) {
+            if piece == "" || piece[0] == ('#' as u8) {
                 continue
                 continue
             }
             }
             let colon = piece.find(':').unwrap();
             let colon = piece.find(':').unwrap();
-            let value = piece.slice_from(colon + 1).to_owned();
+            let value = unescape(piece.slice_from(colon + 1));
             match piece.slice_to(colon) {
             match piece.slice_to(colon) {
                 "s" => test.scheme = Some(value),
                 "s" => test.scheme = Some(value),
-                "u" => test.username = Some(value),
+                "u" => test.username = value,
                 "pass" => test.password = Some(value),
                 "pass" => test.password = Some(value),
-                "h" => test.host = Some(value),
+                "h" => test.host = value,
+                "port" => test.port = value,
                 "p" => test.path = Some(value),
                 "p" => test.path = Some(value),
                 "q" => test.query = Some(value),
                 "q" => test.query = Some(value),
                 "f" => test.fragment = Some(value),
                 "f" => test.fragment = Some(value),

+ 35 - 31
url.rs

@@ -76,6 +76,13 @@ impl Clone for IPv6Address {
 }
 }
 
 
 
 
+macro_rules! is_match(
+    ($value:expr, $($pattern:pat)|+) => (
+        match $value { $($pattern)|+ => true, _ => false }
+    );
+)
+
+
 pub type ParseResult<T> = Result<T, &'static str>;
 pub type ParseResult<T> = Result<T, &'static str>;
 
 
 
 
@@ -152,7 +159,7 @@ impl Host {
         if input.len() == 0 {
         if input.len() == 0 {
             Err("Empty host")
             Err("Empty host")
         } else if input[0] == '[' as u8 {
         } else if input[0] == '[' as u8 {
-            if input[input.len()] == ']' as u8 {
+            if input[input.len() - 1] == ']' as u8 {
                 match IPv6Address::parse(input.slice(1, input.len() - 1)) {
                 match IPv6Address::parse(input.slice(1, input.len() - 1)) {
                     Some(address) => Ok(IPv6(address)),
                     Some(address) => Ok(IPv6(address)),
                     None => Err("Invalid IPv6 address"),
                     None => Err("Invalid IPv6 address"),
@@ -193,16 +200,6 @@ impl Host {
 }
 }
 
 
 
 
-macro_rules! matches(
-    ($value: expr, ($pattern: pat)|+) => {
-        match $value {
-            $($pattern)|+ => true,
-            _ => false,
-        }
-    };
-)
-
-
 impl IPv6Address {
 impl IPv6Address {
     pub fn parse(input: &str) -> Option<IPv6Address> {
     pub fn parse(input: &str) -> Option<IPv6Address> {
         let len = input.len();
         let len = input.len();
@@ -228,6 +225,7 @@ impl IPv6Address {
                 if compress_pointer.is_some() {
                 if compress_pointer.is_some() {
                     return None
                     return None
                 }
                 }
+                i += 1;
                 piece_pointer += 1;
                 piece_pointer += 1;
                 compress_pointer = Some(piece_pointer);
                 compress_pointer = Some(piece_pointer);
                 continue
                 continue
@@ -241,24 +239,25 @@ impl IPv6Address {
                         value = value * 0x10 + digit as u16;
                         value = value * 0x10 + digit as u16;
                         i += 1;
                         i += 1;
                     },
                     },
-                    None => {
-                        if input[i] == 0x2E {  // .
-                            if i == start {
-                                return None
-                            }
-                            i = start;
-                            is_ip_v4 = true;
-                            break
+                    None => break
+                }
+            }
+            if i < len {
+                match input[i] as char {
+                    '.' => {
+                        if i == start {
+                            return None
                         }
                         }
-                        if input[i]  == 0x3A { // :
-                            i += 1;
-                            if i == len {
-                                return None
-                            }
-                            break
+                        i = start;
+                        is_ip_v4 = true;
+                    },
+                    ':' => {
+                        i += 1;
+                        if i == len {
+                            return None
                         }
                         }
-                        return None
-                    }
+                    },
+                    _ => return None
                 }
                 }
             }
             }
             if is_ip_v4 {
             if is_ip_v4 {
@@ -340,6 +339,7 @@ impl IPv6Address {
             if i < 7 {
             if i < 7 {
                 output.push(':'.to_ascii());
                 output.push(':'.to_ascii());
             }
             }
+            i += 1;
         }
         }
         output
         output
     }
     }
@@ -400,6 +400,7 @@ fn to_hex_upper(value: u8) -> Ascii {
 enum EncodeSet {
 enum EncodeSet {
     SimpleEncodeSet,
     SimpleEncodeSet,
     DefaultEncodeSet,
     DefaultEncodeSet,
+    UserInfoEncodeSet,
     PasswordEncodeSet,
     PasswordEncodeSet,
     UsernameEncodeSet
     UsernameEncodeSet
 }
 }
@@ -408,16 +409,19 @@ enum EncodeSet {
 #[inline]
 #[inline]
 fn utf8_percent_encode(input: &str, encode_set: EncodeSet, output: &mut ~[Ascii]) {
 fn utf8_percent_encode(input: &str, encode_set: EncodeSet, output: &mut ~[Ascii]) {
     use Default = self::DefaultEncodeSet;
     use Default = self::DefaultEncodeSet;
+    use UserInfo = self::UserInfoEncodeSet;
     use Password = self::PasswordEncodeSet;
     use Password = self::PasswordEncodeSet;
     use Username = self::UsernameEncodeSet;
     use Username = self::UsernameEncodeSet;
     for &byte in input.as_bytes().iter() {
     for &byte in input.as_bytes().iter() {
         if byte < 0x20 || byte > 0x7E || match byte as char {
         if byte < 0x20 || byte > 0x7E || match byte as char {
             ' ' | '"' | '#' | '<' | '>' | '?' | '`'
             ' ' | '"' | '#' | '<' | '>' | '?' | '`'
-            => match encode_set { Default | Password | Username => true, _ => false },
-            '/' | '@' | '\\'
-            => match encode_set { Password | Username => true, _ => false },
+            => is_match!(encode_set, Default | UserInfo | Password | Username),
+            '@'
+            => is_match!(encode_set, UserInfo | Password | Username),
+            '/' | '\\'
+            => is_match!(encode_set, Password | Username),
             ':'
             ':'
-            => match encode_set { Username => true, _ => false },
+            => is_match!(encode_set, Username),
             _ => false,
             _ => false,
         } {
         } {
             percent_encode_byte(byte, output)
             percent_encode_byte(byte, output)

+ 5 - 5
urltestdata.txt

@@ -10,7 +10,7 @@ http://user:pass@foo:21/bar;par?b#c  s:http u:user pass:pass h:foo port:21 p:/ba
 http:foo.com  s:http h:example.org p:/foo/foo.com
 http:foo.com  s:http h:example.org p:/foo/foo.com
 \t\s\s\s:foo.com\s\s\s\n  s:http h:example.org p:/foo/:foo.com
 \t\s\s\s:foo.com\s\s\s\n  s:http h:example.org p:/foo/:foo.com
 \sfoo.com\s\s  s:http h:example.org p:/foo/foo.com
 \sfoo.com\s\s  s:http h:example.org p:/foo/foo.com
-a:\t\sfoo.com  s:a p:%20foo.com
+a:\t\sfoo.com  s:a p:\sfoo.com
 http://f:21/\sb\s?\sd\s#\se\s  s:http h:f port:21 p:/%20b%20 q:?%20d%20 f:#\se
 http://f:21/\sb\s?\sd\s#\se\s  s:http h:f port:21 p:/%20b%20 q:?%20d%20 f:#\se
 http://f:/c  s:http h:f p:/c
 http://f:/c  s:http h:f p:/c
 http://f:0/c  s:http h:f port:0 p:/c
 http://f:0/c  s:http h:f port:0 p:/c
@@ -45,7 +45,7 @@ foo://  s:foo p://
 http://a:b@c:29/d  s:http u:a pass:b h:c port:29 p:/d
 http://a:b@c:29/d  s:http u:a pass:b h:c port:29 p:/d
 http::@c:29  s:http h:example.org p:/foo/:@c:29
 http::@c:29  s:http h:example.org p:/foo/:@c:29
 http://&a:foo(b]c@d:2/  s:http u:&a pass:foo(b]c h:d port:2 p:/
 http://&a:foo(b]c@d:2/  s:http u:&a pass:foo(b]c h:d port:2 p:/
-http://::@c@d:2  s:http pass::c%40 h:d port:2 p:/
+http://::@c@d:2  s:http u: pass::%40c h:d port:2 p:/
 http://foo.com:b@d/  s:http u:foo.com pass:b h:d p:/
 http://foo.com:b@d/  s:http u:foo.com pass:b h:d p:/
 http://foo.com/\\@  s:http h:foo.com p://@
 http://foo.com/\\@  s:http h:foo.com p://@
 http:\\\\foo.com\\  s:http h:foo.com p:/
 http:\\\\foo.com\\  s:http h:foo.com p:/
@@ -161,10 +161,10 @@ http://example.com/\u202E/foo/\u202D/bar  s:http h:example.com p:/%E2%80%AE/foo/
 
 
 # Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/script-tests/relative.js
 # Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/script-tests/relative.js
 http://www.google.com/foo?bar=baz# about:blank s:http h:www.google.com p:/foo q:?bar=baz f:#
 http://www.google.com/foo?bar=baz# about:blank s:http h:www.google.com p:/foo q:?bar=baz f:#
-http://www.google.com/foo?bar=baz#\s\u00BB  s:http h:www.google.com p:/foo q:?bar=baz f:#\s\u00BB
-http://[www.google.com]/  s:http h:[www.google.com] p:/
+http://www.google.com/foo?bar=baz#\s\u00BB  s:http h:www.google.com p:/foo q:?bar=baz f:#\s%C2%BB
+http://[www.google.com]/
 http://www.google.com  s:http h:www.google.com p:/
 http://www.google.com  s:http h:www.google.com p:/
-http://192.0x00A80001  s:http h:192.0x00a80001 p:/
+http://192.0x00A80001  s:http h:192.0x00A80001 p:/
 http://www/foo%2Ehtml  s:http h:www p:/foo%2Ehtml
 http://www/foo%2Ehtml  s:http h:www p:/foo%2Ehtml
 http://www/foo/%2E/html  s:http h:www p:/foo/html
 http://www/foo/%2E/html  s:http h:www p:/foo/html
 http://user:pass@/
 http://user:pass@/