| OLD | NEW | 
|---|
| 1 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file | 
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a | 
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. | 
| 4 | 4 | 
| 5 library uriTest; | 5 library uriTest; | 
| 6 | 6 | 
| 7 import "package:expect/expect.dart"; | 7 import "package:expect/expect.dart"; | 
| 8 import 'dart:convert'; | 8 import 'dart:convert'; | 
| 9 | 9 | 
| 10 testUri(String uriText, bool isAbsolute) { | 10 testUri(String uriText, bool isAbsolute) { | 
| 11   var uri = Uri.parse(uriText); | 11   var uri = Uri.parse(uriText); | 
| 12 | 12 | 
| 13   // Test that parsing a substring works the same as parsing the string. |  | 
| 14   String wrapper = "://@[]:/%?#"; |  | 
| 15   var embeddedUri = Uri.parse( |  | 
| 16        "$wrapper$uri$wrapper", wrapper.length, uriText.length + wrapper.length); |  | 
| 17 |  | 
| 18   Expect.equals(uri, embeddedUri); |  | 
| 19   Expect.equals(isAbsolute, uri.isAbsolute); | 13   Expect.equals(isAbsolute, uri.isAbsolute); | 
| 20   Expect.stringEquals(uriText, uri.toString()); | 14   Expect.stringEquals(uriText, uri.toString()); | 
| 21 | 15 | 
| 22   // Test equals and hashCode members. | 16   // Test equals and hashCode members. | 
| 23   var uri2 = Uri.parse(uriText); | 17   var uri2 = Uri.parse(uriText); | 
| 24   Expect.equals(uri, uri2); | 18   Expect.equals(uri, uri2); | 
| 25   Expect.equals(uri.hashCode, uri2.hashCode); | 19   Expect.equals(uri.hashCode, uri2.hashCode); | 
| 26 | 20 | 
| 27   // Test that removeFragment doesn't change anything else. | 21   // Test that removeFragment doesn't change anything else. | 
| 28   if (uri.hasFragment) { | 22   if (uri.hasFragment) { | 
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 81   } else { | 75   } else { | 
| 82     Expect.throws(() => Uri.encodeQueryComponent(orig, encoding: ASCII), | 76     Expect.throws(() => Uri.encodeQueryComponent(orig, encoding: ASCII), | 
| 83                   (e) => e is ArgumentError); | 77                   (e) => e is ArgumentError); | 
| 84   } | 78   } | 
| 85 } | 79 } | 
| 86 | 80 | 
| 87 testUriPerRFCs() { | 81 testUriPerRFCs() { | 
| 88   final urisSample = "http://a/b/c/d;p?q"; | 82   final urisSample = "http://a/b/c/d;p?q"; | 
| 89   Uri base = Uri.parse(urisSample); | 83   Uri base = Uri.parse(urisSample); | 
| 90   testResolve(expect, relative) { | 84   testResolve(expect, relative) { | 
| 91     String name = "$base << $relative"; | 85     Expect.stringEquals(expect, base.resolve(relative).toString()); | 
| 92     Expect.stringEquals(expect, base.resolve(relative).toString(), name); |  | 
| 93   } | 86   } | 
| 94 | 87 | 
| 95   // From RFC 3986. | 88   // From RFC 3986. | 
| 96   testResolve("g:h",                   "g:h"); | 89   testResolve("g:h",                   "g:h"); | 
| 97   testResolve("http://a/b/c/g",        "g"); | 90   testResolve("http://a/b/c/g",        "g"); | 
| 98   testResolve("http://a/b/c/g",        "./g"); | 91   testResolve("http://a/b/c/g",        "./g"); | 
| 99   testResolve("http://a/b/c/g/",       "g/"); | 92   testResolve("http://a/b/c/g/",       "g/"); | 
| 100   testResolve("http://a/g",            "/g"); | 93   testResolve("http://a/g",            "/g"); | 
| 101   testResolve("http://g",              "//g"); | 94   testResolve("http://g",              "//g"); | 
| 102   testResolve("http://a/b/c/d;p?y",    "?y"); | 95   testResolve("http://a/b/c/d;p?y",    "?y"); | 
| (...skipping 29 matching lines...) Expand all  Loading... | 
| 132   testResolve("http://a/b/c/y",        "g;x=1/../y"); | 125   testResolve("http://a/b/c/y",        "g;x=1/../y"); | 
| 133   testResolve("http://a/b/c/g?y/./x",  "g?y/./x"); | 126   testResolve("http://a/b/c/g?y/./x",  "g?y/./x"); | 
| 134   testResolve("http://a/b/c/g?y/../x", "g?y/../x"); | 127   testResolve("http://a/b/c/g?y/../x", "g?y/../x"); | 
| 135   testResolve("http://a/b/c/g#s/./x",  "g#s/./x"); | 128   testResolve("http://a/b/c/g#s/./x",  "g#s/./x"); | 
| 136   testResolve("http://a/b/c/g#s/../x", "g#s/../x"); | 129   testResolve("http://a/b/c/g#s/../x", "g#s/../x"); | 
| 137   testResolve("http:g",                "http:g"); | 130   testResolve("http:g",                "http:g"); | 
| 138 | 131 | 
| 139   // Additional tests (not from RFC 3986). | 132   // Additional tests (not from RFC 3986). | 
| 140   testResolve("http://a/b/g;p/h;s",    "../g;p/h;s"); | 133   testResolve("http://a/b/g;p/h;s",    "../g;p/h;s"); | 
| 141 | 134 | 
| 142   base = Uri.parse("s:a/b"); |  | 
| 143   testResolve("s:a/c", "c"); |  | 
| 144   testResolve("s:/c", "../c"); |  | 
| 145 |  | 
| 146   base = Uri.parse("S:a/b"); |  | 
| 147   testResolve("s:a/c", "c"); |  | 
| 148   testResolve("s:/c", "../c"); |  | 
| 149 |  | 
| 150   base = Uri.parse("s:foo"); |  | 
| 151   testResolve("s:bar", "bar"); |  | 
| 152   testResolve("s:bar", "../bar"); |  | 
| 153 |  | 
| 154   base = Uri.parse("S:foo"); |  | 
| 155   testResolve("s:bar", "bar"); |  | 
| 156   testResolve("s:bar", "../bar"); |  | 
| 157 |  | 
| 158   // Special-case (deliberate non-RFC behavior). |  | 
| 159   base = Uri.parse("foo/bar"); |  | 
| 160   testResolve("foo/baz", "baz"); |  | 
| 161   testResolve("baz", "../baz"); |  | 
| 162 |  | 
| 163   base = Uri.parse("s:/foo"); |  | 
| 164   testResolve("s:/bar", "bar"); |  | 
| 165   testResolve("s:/bar", "../bar"); |  | 
| 166 |  | 
| 167   base = Uri.parse("S:/foo"); |  | 
| 168   testResolve("s:/bar", "bar"); |  | 
| 169   testResolve("s:/bar", "../bar"); |  | 
| 170 |  | 
| 171   // Test non-URI base (no scheme, no authority, relative path). | 135   // Test non-URI base (no scheme, no authority, relative path). | 
| 172   base = Uri.parse("a/b/c?_#_"); | 136   base = Uri.parse("a/b/c?_#_"); | 
| 173   testResolve("a/b/g?q#f", "g?q#f"); | 137   testResolve("a/b/g?q#f", "g?q#f"); | 
| 174   testResolve("./", "../.."); | 138   testResolve("./", "../.."); | 
| 175   testResolve("../", "../../.."); | 139   testResolve("../", "../../.."); | 
| 176   testResolve("a/b/", "."); | 140   testResolve("a/b/", "."); | 
| 177   testResolve("c", "../../c");  // Deliberate non-RFC behavior. | 141   testResolve("c", "../../c"); | 
| 178   base = Uri.parse("../../a/b/c?_#_");  // Initial ".." in base url. | 142   base = Uri.parse("../../a/b/c?_#_");  // Initial ".." in base url. | 
| 179   testResolve("../../a/d", "../d"); | 143   testResolve("../../a/d", "../d"); | 
| 180   testResolve("../../../d", "../../../d"); | 144   testResolve("../../../d", "../../../d"); | 
| 181 | 145 | 
| 182   base = Uri.parse("s://h/p?q#f");  // A simple base. | 146   base = Uri.parse("s:a/b"); | 
| 183   // Simple references: | 147   testResolve("s:/c", "../c"); | 
| 184   testResolve("s2://h2/P?Q#F", "s2://h2/P?Q#F"); |  | 
| 185   testResolve("s://h2/P?Q#F", "//h2/P?Q#F"); |  | 
| 186   testResolve("s://h/P?Q#F", "/P?Q#F"); |  | 
| 187   testResolve("s://h/p?Q#F", "?Q#F"); |  | 
| 188   testResolve("s://h/p?q#F", "#F"); |  | 
| 189   testResolve("s://h/p?q", ""); |  | 
| 190   // Non-simple references: |  | 
| 191   testResolve("s2://I@h2/P?Q#F%20", "s2://I@h2/P?Q#F%20"); |  | 
| 192   testResolve("s://I@h2/P?Q#F%20", "//I@h2/P?Q#F%20"); |  | 
| 193   testResolve("s://h2/P?Q#F%20", "//h2/P?Q#F%20"); |  | 
| 194   testResolve("s://h/P?Q#F%20", "/P?Q#F%20"); |  | 
| 195   testResolve("s://h/p?Q#F%20", "?Q#F%20"); |  | 
| 196   testResolve("s://h/p?q#F%20", "#F%20"); |  | 
| 197 |  | 
| 198   base = Uri.parse("s://h/p1/p2/p3");  // A simple base with a path. |  | 
| 199   testResolve("s://h/p1/p2/", "."); |  | 
| 200   testResolve("s://h/p1/p2/", "./"); |  | 
| 201   testResolve("s://h/p1/", ".."); |  | 
| 202   testResolve("s://h/p1/", "../"); |  | 
| 203   testResolve("s://h/", "../.."); |  | 
| 204   testResolve("s://h/", "../../"); |  | 
| 205   testResolve("s://h/p1/%20", "../%20"); |  | 
| 206   testResolve("s://h/", "../../../.."); |  | 
| 207   testResolve("s://h/", "../../../../"); |  | 
| 208 |  | 
| 209   base = Uri.parse("s://h/p?q#f%20");  // A non-simpe base. |  | 
| 210   // Simple references: |  | 
| 211   testResolve("s2://h2/P?Q#F", "s2://h2/P?Q#F"); |  | 
| 212   testResolve("s://h2/P?Q#F", "//h2/P?Q#F"); |  | 
| 213   testResolve("s://h/P?Q#F", "/P?Q#F"); |  | 
| 214   testResolve("s://h/p?Q#F", "?Q#F"); |  | 
| 215   testResolve("s://h/p?q#F", "#F"); |  | 
| 216   testResolve("s://h/p?q", ""); |  | 
| 217   // Non-simple references: |  | 
| 218   testResolve("s2://I@h2/P?Q#F%20", "s2://I@h2/P?Q#F%20"); |  | 
| 219   testResolve("s://I@h2/P?Q#F%20", "//I@h2/P?Q#F%20"); |  | 
| 220   testResolve("s://h2/P?Q#F%20", "//h2/P?Q#F%20"); |  | 
| 221   testResolve("s://h/P?Q#F%20", "/P?Q#F%20"); |  | 
| 222   testResolve("s://h/p?Q#F%20", "?Q#F%20"); |  | 
| 223   testResolve("s://h/p?q#F%20", "#F%20"); |  | 
| 224 |  | 
| 225   base = Uri.parse("S://h/p1/p2/p3");  // A non-simple base with a path. |  | 
| 226   testResolve("s://h/p1/p2/", "."); |  | 
| 227   testResolve("s://h/p1/p2/", "./"); |  | 
| 228   testResolve("s://h/p1/", ".."); |  | 
| 229   testResolve("s://h/p1/", "../"); |  | 
| 230   testResolve("s://h/", "../.."); |  | 
| 231   testResolve("s://h/", "../../"); |  | 
| 232   testResolve("s://h/p1/%20", "../%20"); |  | 
| 233   testResolve("s://h/", "../../../.."); |  | 
| 234   testResolve("s://h/", "../../../../"); |  | 
| 235 |  | 
| 236   base = Uri.parse("../../../");  // A simple relative path. |  | 
| 237   testResolve("../../../a", "a"); |  | 
| 238   testResolve("../../../../a", "../a"); |  | 
| 239   testResolve("../../../a%20", "a%20"); |  | 
| 240   testResolve("../../../../a%20", "../a%20"); |  | 
| 241 |  | 
| 242   // Tests covering the branches of the merge algorithm in RFC 3986 |  | 
| 243   // with both simple and complex base URIs. |  | 
| 244   for (var b in ["s://a/pa/pb?q#f", "s://a/pa/pb?q#f%20"]) { |  | 
| 245     var origBase = Uri.parse(b); |  | 
| 246     base = origBase; |  | 
| 247 |  | 
| 248     // if defined(R.scheme) then ... |  | 
| 249     testResolve("s2://a2/p2?q2#f2", "s2://a2/p2?q2#f2"); |  | 
| 250     // else, if defined(R.authority) then ... |  | 
| 251     testResolve("s://a2/p2?q2#f2", "//a2/p2?q2#f2"); |  | 
| 252     testResolve("s://a2/?q2#f2", "//a2/../?q2#f2"); |  | 
| 253     testResolve("s://a2?q2#f2", "//a2?q2#f2"); |  | 
| 254     testResolve("s://a2#f2", "//a2#f2"); |  | 
| 255     testResolve("s://a2", "//a2"); |  | 
| 256     // else, if (R.path == "") then ... |  | 
| 257     //   if defined(R.query) then |  | 
| 258     testResolve("s://a/pa/pb?q2#f2", "?q2#f2"); |  | 
| 259     testResolve("s://a/pa/pb?q2", "?q2"); |  | 
| 260     //   else |  | 
| 261     testResolve("s://a/pa/pb?q#f2", "#f2"); |  | 
| 262     testResolve("s://a/pa/pb?q", ""); |  | 
| 263     // else, if (R.path starts-with "/") then ... |  | 
| 264     testResolve("s://a/p2?q2#f2", "/p2?q2#f2"); |  | 
| 265     testResolve("s://a/?q2#f2", "/?q2#f2"); |  | 
| 266     testResolve("s://a/#f2", "/#f2"); |  | 
| 267     testResolve("s://a/", "/"); |  | 
| 268     testResolve("s://a/", "/../"); |  | 
| 269     // else ... T.path = merge(Base.path, R.path) |  | 
| 270     // ... remove-dot-fragments(T.path) ... |  | 
| 271     // (Cover the merge function and the remove-dot-fragments functions too). |  | 
| 272 |  | 
| 273     // If base has authority and empty path ... |  | 
| 274     var emptyPathBase = Uri.parse(b.replaceFirst("/pa/pb", "")); |  | 
| 275     base = emptyPathBase; |  | 
| 276     testResolve("s://a/p2?q2#f2", "p2?q2#f2"); |  | 
| 277     testResolve("s://a/p2#f2", "p2#f2"); |  | 
| 278     testResolve("s://a/p2", "p2"); |  | 
| 279 |  | 
| 280     base = origBase; |  | 
| 281     // otherwise |  | 
| 282     // (Cover both no authority and non-empty path and both). |  | 
| 283     var noAuthEmptyPathBase = Uri.parse(b.replaceFirst("//a/pa/pb", "")); |  | 
| 284     var noAuthAbsPathBase = Uri.parse(b.replaceFirst("//a", "")); |  | 
| 285     var noAuthRelPathBase = Uri.parse(b.replaceFirst("//a/", "")); |  | 
| 286     var noAuthRelSinglePathBase = Uri.parse(b.replaceFirst("//a/pa/", "")); |  | 
| 287 |  | 
| 288     testResolve("s://a/pa/p2?q2#f2", "p2?q2#f2"); |  | 
| 289     testResolve("s://a/pa/p2#f2", "p2#f2"); |  | 
| 290     testResolve("s://a/pa/p2", "p2"); |  | 
| 291 |  | 
| 292     base = noAuthEmptyPathBase; |  | 
| 293     testResolve("s:p2?q2#f2", "p2?q2#f2"); |  | 
| 294     testResolve("s:p2#f2", "p2#f2"); |  | 
| 295     testResolve("s:p2", "p2"); |  | 
| 296 |  | 
| 297     base = noAuthAbsPathBase; |  | 
| 298     testResolve("s:/pa/p2?q2#f2", "p2?q2#f2"); |  | 
| 299     testResolve("s:/pa/p2#f2", "p2#f2"); |  | 
| 300     testResolve("s:/pa/p2", "p2"); |  | 
| 301 |  | 
| 302     base = noAuthRelPathBase; |  | 
| 303     testResolve("s:pa/p2?q2#f2", "p2?q2#f2"); |  | 
| 304     testResolve("s:pa/p2#f2", "p2#f2"); |  | 
| 305     testResolve("s:pa/p2", "p2"); |  | 
| 306 |  | 
| 307     base = noAuthRelSinglePathBase; |  | 
| 308     testResolve("s:p2?q2#f2", "p2?q2#f2"); |  | 
| 309     testResolve("s:p2#f2", "p2#f2"); |  | 
| 310     testResolve("s:p2", "p2"); |  | 
| 311 |  | 
| 312     // Then remove dot segments. |  | 
| 313 |  | 
| 314     // A. if input buffer starts with "../" or "./". |  | 
| 315     // This only happens if base has only a single (may be empty) segment and |  | 
| 316     // no slash. |  | 
| 317     base = emptyPathBase; |  | 
| 318     testResolve("s://a/p2", "../p2"); |  | 
| 319     testResolve("s://a/", "../"); |  | 
| 320     testResolve("s://a/", ".."); |  | 
| 321     testResolve("s://a/p2", "./p2"); |  | 
| 322     testResolve("s://a/", "./"); |  | 
| 323     testResolve("s://a/", "."); |  | 
| 324     testResolve("s://a/p2", "../../p2"); |  | 
| 325     testResolve("s://a/p2", "../../././p2"); |  | 
| 326 |  | 
| 327     base = noAuthRelSinglePathBase; |  | 
| 328     testResolve("s:p2", "../p2"); |  | 
| 329     testResolve("s:", "../"); |  | 
| 330     testResolve("s:", ".."); |  | 
| 331     testResolve("s:p2", "./p2"); |  | 
| 332     testResolve("s:", "./"); |  | 
| 333     testResolve("s:", "."); |  | 
| 334     testResolve("s:p2", "../../p2"); |  | 
| 335     testResolve("s:p2", "../../././p2"); |  | 
| 336 |  | 
| 337     // B. if input buffer starts with "/./" or is "/.". replace with "/". |  | 
| 338     // (The URI implementation removes the "." path segments when parsing, |  | 
| 339     // so this case isn't handled by merge). |  | 
| 340     base = origBase; |  | 
| 341     testResolve("s://a/pa/p2", "./p2"); |  | 
| 342 |  | 
| 343     // C. if input buffer starts with "/../" or is "/..", replace with "/" |  | 
| 344     // and remove preceeding segment. |  | 
| 345     testResolve("s://a/p2", "../p2"); |  | 
| 346     var longPathBase = Uri.parse(b.replaceFirst("/pb", "/pb/pc/pd")); |  | 
| 347     base = longPathBase; |  | 
| 348     testResolve("s://a/pa/pb/p2", "../p2"); |  | 
| 349     testResolve("s://a/pa/p2", "../../p2"); |  | 
| 350     testResolve("s://a/p2", "../../../p2"); |  | 
| 351     testResolve("s://a/p2", "../../../../p2"); |  | 
| 352     var noAuthRelLongPathBase = |  | 
| 353         Uri.parse(b.replaceFirst("//a/pa/pb", "pa/pb/pc/pd")); |  | 
| 354     base = noAuthRelLongPathBase; |  | 
| 355     testResolve("s:pa/pb/p2", "../p2"); |  | 
| 356     testResolve("s:pa/p2", "../../p2"); |  | 
| 357     testResolve("s:/p2", "../../../p2"); |  | 
| 358     testResolve("s:/p2", "../../../../p2"); |  | 
| 359 |  | 
| 360     // D. if the input buffer contains only ".." or ".", remove it. |  | 
| 361     base = noAuthEmptyPathBase; |  | 
| 362     testResolve("s:", ".."); |  | 
| 363     testResolve("s:", "."); |  | 
| 364     base = noAuthRelSinglePathBase; |  | 
| 365     testResolve("s:", ".."); |  | 
| 366     testResolve("s:", "."); |  | 
| 367   } |  | 
| 368 } | 148 } | 
| 369 | 149 | 
| 370 void testResolvePath(String expected, String path) { | 150 void testResolvePath(String expected, String path) { | 
| 371   Expect.equals(expected, | 151   Expect.equals(expected, | 
| 372                 new Uri(path: '/').resolveUri(new Uri(path: path)).path); | 152                 new Uri(path: '/').resolveUri(new Uri(path: path)).path); | 
| 373   Expect.equals( | 153   Expect.equals( | 
| 374       "http://localhost$expected", | 154       "http://localhost$expected", | 
| 375       Uri.parse("http://localhost").resolveUri(new Uri(path: path)).toString()); | 155       Uri.parse("http://localhost").resolveUri(new Uri(path: path)).toString()); | 
| 376 } | 156 } | 
| 377 | 157 | 
| (...skipping 333 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 711   Expect.stringEquals("/a/b/c/", | 491   Expect.stringEquals("/a/b/c/", | 
| 712                       new Uri( | 492                       new Uri( | 
| 713                           scheme: null, | 493                           scheme: null, | 
| 714                           userInfo: null, | 494                           userInfo: null, | 
| 715                           host: null, | 495                           host: null, | 
| 716                           port: 0, | 496                           port: 0, | 
| 717                           path: "/a/b/c/", | 497                           path: "/a/b/c/", | 
| 718                           query: null, | 498                           query: null, | 
| 719                           fragment: null).toString()); | 499                           fragment: null).toString()); | 
| 720   Expect.stringEquals("file:///", Uri.parse("file:").toString()); | 500   Expect.stringEquals("file:///", Uri.parse("file:").toString()); | 
| 721   Expect.stringEquals("file:///", Uri.parse("file:/").toString()); |  | 
| 722   Expect.stringEquals("file:///", Uri.parse("file:").toString()); |  | 
| 723   Expect.stringEquals("file:///foo", Uri.parse("file:foo").toString()); |  | 
| 724   Expect.stringEquals("file:///foo", Uri.parse("file:/foo").toString()); |  | 
| 725   Expect.stringEquals("file://foo/", Uri.parse("file://foo").toString()); |  | 
| 726 | 501 | 
| 727   testResolvePath("/a/g", "/a/b/c/./../../g"); | 502   testResolvePath("/a/g", "/a/b/c/./../../g"); | 
| 728   testResolvePath("/a/g", "/a/b/c/./../../g"); | 503   testResolvePath("/a/g", "/a/b/c/./../../g"); | 
| 729   testResolvePath("/mid/6", "mid/content=5/../6"); | 504   testResolvePath("/mid/6", "mid/content=5/../6"); | 
| 730   testResolvePath("/a/b/e", "a/b/c/d/../../e"); | 505   testResolvePath("/a/b/e", "a/b/c/d/../../e"); | 
| 731   testResolvePath("/a/b/e", "../a/b/c/d/../../e"); | 506   testResolvePath("/a/b/e", "../a/b/c/d/../../e"); | 
| 732   testResolvePath("/a/b/e", "./a/b/c/d/../../e"); | 507   testResolvePath("/a/b/e", "./a/b/c/d/../../e"); | 
| 733   testResolvePath("/a/b/e", "../a/b/./c/d/../../e"); | 508   testResolvePath("/a/b/e", "../a/b/./c/d/../../e"); | 
| 734   testResolvePath("/a/b/e", "./a/b/./c/d/../../e"); | 509   testResolvePath("/a/b/e", "./a/b/./c/d/../../e"); | 
| 735   testResolvePath("/a/b/e/", "./a/b/./c/d/../../e/."); | 510   testResolvePath("/a/b/e/", "./a/b/./c/d/../../e/."); | 
| (...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 863 String dump(Uri uri) { | 638 String dump(Uri uri) { | 
| 864   return "URI: $uri\n" | 639   return "URI: $uri\n" | 
| 865       "  Scheme:    ${uri.scheme} #${uri.scheme.length}\n" | 640       "  Scheme:    ${uri.scheme} #${uri.scheme.length}\n" | 
| 866       "  User-info: ${uri.userInfo} #${uri.userInfo.length}\n" | 641       "  User-info: ${uri.userInfo} #${uri.userInfo.length}\n" | 
| 867       "  Host:      ${uri.host} #${uri.host.length}\n" | 642       "  Host:      ${uri.host} #${uri.host.length}\n" | 
| 868       "  Port:      ${uri.port}\n" | 643       "  Port:      ${uri.port}\n" | 
| 869       "  Path:      ${uri.path} #${uri.path.length}\n" | 644       "  Path:      ${uri.path} #${uri.path.length}\n" | 
| 870       "  Query:     ${uri.query} #${uri.query.length}\n" | 645       "  Query:     ${uri.query} #${uri.query.length}\n" | 
| 871       "  Fragment:  ${uri.fragment} #${uri.fragment.length}\n"; | 646       "  Fragment:  ${uri.fragment} #${uri.fragment.length}\n"; | 
| 872 } | 647 } | 
| OLD | NEW | 
|---|