Просмотр исходного кода

[idna] Preserve leading dots in host

A retry of https://github.com/servo/rust-url/pull/171

This diff changes the behavior of ToASCII step to match the spec and
prevent failures on some cases when a domain name starts with leading
dots (FULL STOPs), as requested in
https://github.com/servo/rust-url/issues/166.

The change in the code results in a few failures for test cases of the
Conformance Testing data provided with UTS #46. But, as the header of
the test data file (IdnaTest.txt) says: "If the file does not indicate
an error, then the implementation must either have an error, or must
have a matching result."

Therefore, failing on those test cases does not break conformance with
UTS #46, and to some level, anticipated.

As mentioned in https://github.com/servo/rust-url/issues/166, a feedback
is submitted for this inconsistency and the test logic can be improved
later if the data file addresses the comments.

Until then, we can throw less errors and maintain passing conformance
tests with this diff.

To keep the side-effects of ignoring errors during test runs as minimum
as possible, I have separated `TooShortForDns` error from
`TooLongForDns`. The `Error` struct has been kept private, so the change
won't affect any library users.

Fix #166
Behnam Esfahbod 9 лет назад
Родитель
Сommit
185495da2d
2 измененных файлов с 18 добавлено и 4 удалено
  1. 11 4
      idna/src/uts46.rs
  2. 7 0
      tests/unit.rs

+ 11 - 4
idna/src/uts46.rs

@@ -239,10 +239,12 @@ fn processing(domain: &str, flags: Flags, errors: &mut Vec<Error>) -> String {
     }
     let normalized: String = mapped.nfc().collect();
     let mut validated = String::new();
+    let mut first = true;
     for label in normalized.split('.') {
-        if validated.len() > 0 {
+        if !first {
             validated.push('.');
         }
+        first = false;
         if label.starts_with("xn--") {
             match punycode::decode_to_string(&label["xn--".len()..]) {
                 Some(decoded_label) => {
@@ -275,6 +277,7 @@ enum Error {
     DissallowedMappedInStd3,
     DissallowedCharacter,
     TooLongForDns,
+    TooShortForDns,
 }
 
 /// Errors recorded during UTS #46 processing.
@@ -288,10 +291,12 @@ pub struct Errors(Vec<Error>);
 pub fn to_ascii(domain: &str, flags: Flags) -> Result<String, Errors> {
     let mut errors = Vec::new();
     let mut result = String::new();
+    let mut first = true;
     for label in processing(domain, flags, &mut errors).split('.') {
-        if result.len() > 0 {
+        if !first {
             result.push('.');
         }
+        first = false;
         if label.is_ascii() {
             result.push_str(label);
         } else {
@@ -307,8 +312,10 @@ pub fn to_ascii(domain: &str, flags: Flags) -> Result<String, Errors> {
 
     if flags.verify_dns_length {
         let domain = if result.ends_with(".") { &result[..result.len()-1]  } else { &*result };
-        if domain.len() < 1 || domain.len() > 253 ||
-                domain.split('.').any(|label| label.len() < 1 || label.len() > 63) {
+        if domain.len() < 1 || domain.split('.').any(|label| label.len() < 1) {
+            errors.push(Error::TooShortForDns)
+        }
+        if domain.len() > 253 || domain.split('.').any(|label| label.len() > 63) {
             errors.push(Error::TooLongForDns)
         }
     }

+ 7 - 0
tests/unit.rs

@@ -374,6 +374,13 @@ fn test_set_host() {
     assert_eq!(url.as_str(), "foobar:/hello");
 }
 
+#[test]
+// https://github.com/servo/rust-url/issues/166
+fn test_leading_dots() {
+    assert_eq!(Host::parse(".org").unwrap(), Host::Domain(".org".to_owned()));
+    assert_eq!(Url::parse("file://./foo").unwrap().domain(), Some("."));
+}
+
 // This is testing that the macro produces buildable code when invoked
 // inside both a module and a function
 #[test]