Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(274)

Side by Side Diff: sdk/lib/core/uri.dart

Issue 1224263009: Do "path normalization" when creating a URI. (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | tests/corelib/uri_normalize_path_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 part of dart.core; 5 part of dart.core;
6 6
7 /** 7 /**
8 * A parsed URI, such as a URL. 8 * A parsed URI, such as a URL.
9 * 9 *
10 * **See also:** 10 * **See also:**
(...skipping 363 matching lines...) Expand 10 before | Expand all | Expand 10 after
374 char = uri.codeUnitAt(index); 374 char = uri.codeUnitAt(index);
375 if (char == _QUESTION || char == _NUMBER_SIGN) { 375 if (char == _QUESTION || char == _NUMBER_SIGN) {
376 break; 376 break;
377 } 377 }
378 char = EOI; 378 char = EOI;
379 } 379 }
380 state = NOT_IN_PATH; 380 state = NOT_IN_PATH;
381 } 381 }
382 382
383 assert(state == NOT_IN_PATH); 383 assert(state == NOT_IN_PATH);
384 bool isFile = (scheme == "file"); 384 bool hasAuthority = host != null;
floitsch 2015/07/17 13:03:20 nit: (host != null)
Lasse Reichstein Nielsen 2015/07/17 18:40:22 Done.
385 bool ensureLeadingSlash = host != null; 385 path = _makePath(uri, pathStart, index, null, scheme, hasAuthority);
386 path = _makePath(uri, pathStart, index, null, ensureLeadingSlash, isFile);
387 386
388 if (char == _QUESTION) { 387 if (char == _QUESTION) {
389 int numberSignIndex = -1; 388 int numberSignIndex = -1;
390 for (int i = index + 1; i < end; i++) { 389 for (int i = index + 1; i < end; i++) {
391 if (uri.codeUnitAt(i) == _NUMBER_SIGN) { 390 if (uri.codeUnitAt(i) == _NUMBER_SIGN) {
392 numberSignIndex = i; 391 numberSignIndex = i;
393 break; 392 break;
394 } 393 }
395 } 394 }
396 if (numberSignIndex < 0) { 395 if (numberSignIndex < 0) {
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
504 // Special case this constructor for backwards compatibility. 503 // Special case this constructor for backwards compatibility.
505 if (query == "") query = null; 504 if (query == "") query = null;
506 query = _makeQuery(query, 0, _stringOrNullLength(query), queryParameters); 505 query = _makeQuery(query, 0, _stringOrNullLength(query), queryParameters);
507 fragment = _makeFragment(fragment, 0, _stringOrNullLength(fragment)); 506 fragment = _makeFragment(fragment, 0, _stringOrNullLength(fragment));
508 port = _makePort(port, scheme); 507 port = _makePort(port, scheme);
509 bool isFile = (scheme == "file"); 508 bool isFile = (scheme == "file");
510 if (host == null && 509 if (host == null &&
511 (userInfo.isNotEmpty || port != null || isFile)) { 510 (userInfo.isNotEmpty || port != null || isFile)) {
512 host = ""; 511 host = "";
513 } 512 }
514 bool ensureLeadingSlash = host != null; 513 bool hasAuthority = host != null;
floitsch 2015/07/17 13:03:20 ditto.
515 path = _makePath(path, 0, _stringOrNullLength(path), pathSegments, 514 path = _makePath(path, 0, _stringOrNullLength(path), pathSegments,
516 ensureLeadingSlash, isFile); 515 scheme, hasAuthority);
516 if (scheme.isEmpty && host == null && !path.startsWith('/')) {
floitsch 2015/07/17 13:03:20 ditto.
Lasse Reichstein Nielsen 2015/07/17 18:40:23 Not here, no. Reading expressions between && is no
517 path = _normalizeRelativePath(path);
518 } else {
519 path = _removeDotSegments(path);
520 }
517 return new Uri._internal(scheme, userInfo, host, port, 521 return new Uri._internal(scheme, userInfo, host, port,
518 path, query, fragment); 522 path, query, fragment);
519 } 523 }
520 524
521 /** 525 /**
522 * Creates a new `http` URI from authority, path and query. 526 * Creates a new `http` URI from authority, path and query.
523 * 527 *
524 * Examples: 528 * Examples:
525 * 529 *
526 * ``` 530 * ```
(...skipping 419 matching lines...) Expand 10 before | Expand all | Expand 10 after
946 } 950 }
947 } 951 }
948 if (host != null) { 952 if (host != null) {
949 host = _makeHost(host, 0, host.length, false); 953 host = _makeHost(host, 0, host.length, false);
950 } else if (this.hasAuthority) { 954 } else if (this.hasAuthority) {
951 host = this.host; 955 host = this.host;
952 } else if (userInfo.isNotEmpty || port != null || isFile) { 956 } else if (userInfo.isNotEmpty || port != null || isFile) {
953 host = ""; 957 host = "";
954 } 958 }
955 959
956 bool ensureLeadingSlash = (host != null); 960 bool hasAuthority = host != null;
floitsch 2015/07/17 13:03:20 we seem to disagree :) I like the parenthesis.
Lasse Reichstein Nielsen 2015/07/17 18:40:23 We do disagree. Parentheses are added to change o
957 if (path != null || pathSegments != null) { 961 if (path != null || pathSegments != null) {
958 path = _makePath(path, 0, _stringOrNullLength(path), pathSegments, 962 path = _makePath(path, 0, _stringOrNullLength(path), pathSegments,
959 ensureLeadingSlash, isFile); 963 scheme, hasAuthority);
960 } else { 964 } else {
961 path = this.path; 965 path = this.path;
962 if ((isFile || (ensureLeadingSlash && !path.isEmpty)) && 966 if ((isFile || (hasAuthority && !path.isEmpty)) &&
963 !path.startsWith('/')) { 967 !path.startsWith('/')) {
964 path = "/$path"; 968 path = "/" + path;
965 } 969 }
966 } 970 }
967 971
968 if (query != null || queryParameters != null) { 972 if (query != null || queryParameters != null) {
969 query = _makeQuery(query, 0, _stringOrNullLength(query), queryParameters); 973 query = _makeQuery(query, 0, _stringOrNullLength(query), queryParameters);
970 } else if (this.hasQuery) { 974 } else if (this.hasQuery) {
971 query = this.query; 975 query = this.query;
972 } 976 }
973 977
974 if (fragment != null) { 978 if (fragment != null) {
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
1018 * calls that would mutate it. 1022 * calls that would mutate it.
1019 */ 1023 */
1020 Map<String, String> get queryParameters { 1024 Map<String, String> get queryParameters {
1021 if (_queryParameters == null) { 1025 if (_queryParameters == null) {
1022 _queryParameters = new UnmodifiableMapView(splitQueryString(query)); 1026 _queryParameters = new UnmodifiableMapView(splitQueryString(query));
1023 } 1027 }
1024 return _queryParameters; 1028 return _queryParameters;
1025 } 1029 }
1026 1030
1027 /** 1031 /**
1028 * Returns an URI where the path has been normalized. 1032 * Returns an URI where the path has been normalized.
floitsch 2015/07/17 13:03:20 a URI
Lasse Reichstein Nielsen 2015/07/17 18:40:22 Done.
1029 * 1033 *
1030 * A normalized path does not contain `.` segments or non-leading `..` 1034 * A normalized path does not contain `.` segments or non-leading `..`
1031 * segments. 1035 * segments.
1032 * Only a relative path may contain leading `..` segments, 1036 * Only a relative path with no scheme or authority may contain
1037 * leading `..` segments,
1033 * a path that starts with `/` will also drop any leading `..` segments. 1038 * a path that starts with `/` will also drop any leading `..` segments.
1034 * 1039 *
1035 * This uses the same normalization strategy as [resolveUri], as specified by 1040 * This uses the same normalization strategy as `new Uri().resolve(this)`.
1036 * RFC 3986.
1037 * 1041 *
1038 * Does not change any part of the URI except the path. 1042 * Does not change any part of the URI except the path.
1039 */ 1043 */
1040 Uri normalizePath() { 1044 Uri normalizePath() {
1041 String path = _removeDotSegments(_path); 1045 String path = _normalizePath(_path, scheme, hasAuthority);
1042 if (identical(path, _path)) return this; 1046 if (identical(path, _path)) return this;
1043 return this.replace(path: path); 1047 return this.replace(path: path);
1044 } 1048 }
1045 1049
1046 static int _makePort(int port, String scheme) { 1050 static int _makePort(int port, String scheme) {
1047 // Perform scheme specific normalization. 1051 // Perform scheme specific normalization.
1048 if (port != null && port == _defaultPort(scheme)) return null; 1052 if (port != null && port == _defaultPort(scheme)) return null;
1049 return port; 1053 return port;
1050 } 1054 }
1051 1055
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after
1171 * Validates scheme characters and does case-normalization. 1175 * Validates scheme characters and does case-normalization.
1172 * 1176 *
1173 * Schemes are converted to lower case. They cannot contain escapes. 1177 * Schemes are converted to lower case. They cannot contain escapes.
1174 */ 1178 */
1175 static String _makeScheme(String scheme, int start, int end) { 1179 static String _makeScheme(String scheme, int start, int end) {
1176 if (start == end) return ""; 1180 if (start == end) return "";
1177 final int firstCodeUnit = scheme.codeUnitAt(start); 1181 final int firstCodeUnit = scheme.codeUnitAt(start);
1178 if (!_isAlphabeticCharacter(firstCodeUnit)) { 1182 if (!_isAlphabeticCharacter(firstCodeUnit)) {
1179 _fail(scheme, start, "Scheme not starting with alphabetic character"); 1183 _fail(scheme, start, "Scheme not starting with alphabetic character");
1180 } 1184 }
1181 bool allLowercase = firstCodeUnit >= _LOWER_CASE_A; 1185 int minChar = firstCodeUnit;
1182 for (int i = start; i < end; i++) { 1186 for (int i = start; i < end; i++) {
1183 final int codeUnit = scheme.codeUnitAt(i); 1187 final int codeUnit = scheme.codeUnitAt(i);
1184 if (!_isSchemeCharacter(codeUnit)) { 1188 if (!_isSchemeCharacter(codeUnit)) {
1185 _fail(scheme, i, "Illegal scheme character"); 1189 _fail(scheme, i, "Illegal scheme character");
1186 } 1190 }
1187 if (codeUnit < _LOWER_CASE_A || codeUnit > _LOWER_CASE_Z) { 1191 if (codeUnit < minChar) minChar = codeUnit;
1188 allLowercase = false;
1189 }
1190 } 1192 }
1193 // All valid scheme characters are greater than or equal to _UPPER_CASE_A.
1194 // If they are above _UPPER_CASE_Z, the scheme is already lower-case.
floitsch 2015/07/17 13:03:20 maybe add assert(_UPPER_CASE_Z < _LOWER_CASE_A);
Lasse Reichstein Nielsen 2015/07/17 18:40:23 I'll revert this, I had missed that there are vali
1191 scheme = scheme.substring(start, end); 1195 scheme = scheme.substring(start, end);
1196 bool allLowercase = minChar > _UPPER_CASE_Z;
floitsch 2015/07/17 13:03:20 why not minChar >= _LOWER_CASE_A ?
Lasse Reichstein Nielsen 2015/07/17 18:40:23 Because we allow two non-letter characters, one of
1192 if (!allLowercase) scheme = scheme.toLowerCase(); 1197 if (!allLowercase) scheme = scheme.toLowerCase();
1193 return scheme; 1198 return scheme;
1194 } 1199 }
1195 1200
1196 static String _makeUserInfo(String userInfo, int start, int end) { 1201 static String _makeUserInfo(String userInfo, int start, int end) {
1197 if (userInfo == null) return ""; 1202 if (userInfo == null) return "";
1198 return _normalize(userInfo, start, end, _userinfoTable); 1203 return _normalize(userInfo, start, end, _userinfoTable);
1199 } 1204 }
1200 1205
1201 static String _makePath(String path, int start, int end, 1206 static String _makePath(String path, int start, int end,
1202 Iterable<String> pathSegments, 1207 Iterable<String> pathSegments,
1203 bool ensureLeadingSlash, 1208 String scheme,
1204 bool isFile) { 1209 bool hasAuthority) {
1210 bool isFile = (scheme == "file");
1211 bool ensureLeadingSlash = isFile || hasAuthority;
1205 if (path == null && pathSegments == null) return isFile ? "/" : ""; 1212 if (path == null && pathSegments == null) return isFile ? "/" : "";
1206 if (path != null && pathSegments != null) { 1213 if (path != null && pathSegments != null) {
1207 throw new ArgumentError('Both path and pathSegments specified'); 1214 throw new ArgumentError('Both path and pathSegments specified');
1208 } 1215 }
1209 var result; 1216 var result;
1210 if (path != null) { 1217 if (path != null) {
1211 result = _normalize(path, start, end, _pathCharOrSlashTable); 1218 result = _normalize(path, start, end, _pathCharOrSlashTable);
1212 } else { 1219 } else {
1213 result = pathSegments.map((s) => _uriEncode(_pathCharTable, s)).join("/"); 1220 result = pathSegments.map((s) => _uriEncode(_pathCharTable, s)).join("/");
1214 } 1221 }
1215 if (result.isEmpty) { 1222 if (result.isEmpty) {
1216 if (isFile) return "/"; 1223 if (isFile) return "/";
1217 } else if ((isFile || ensureLeadingSlash) && 1224 } else if (ensureLeadingSlash && !result.startsWith('/')) {
1218 result.codeUnitAt(0) != _SLASH) { 1225 result = "/" + result;
1219 return "/$result";
1220 } 1226 }
1227 result = _normalizePath(result, scheme, hasAuthority);
1221 return result; 1228 return result;
1222 } 1229 }
1223 1230
1231 /// Performs path normalization (remove dot segments) on a path.
1232 ///
1233 /// If the URI has neither scheme nor authority, it's considered a
1234 /// "pure path" and normalization won't remove leading ".." segments.
1235 /// Otherwise it follows the RFC 3986 "remove dot segments" algorithm.
1236 static String _normalizePath(String path, String scheme, bool hasAuthority) {
1237 if (scheme.isEmpty && !hasAuthority && !path.startsWith('/')) {
1238 return _normalizeRelativePath(path);
1239 }
1240 return _removeDotSegments(path);
1241 }
1242
1224 static String _makeQuery(String query, int start, int end, 1243 static String _makeQuery(String query, int start, int end,
1225 Map<String, String> queryParameters) { 1244 Map<String, String> queryParameters) {
1226 if (query == null && queryParameters == null) return null; 1245 if (query == null && queryParameters == null) return null;
1227 if (query != null && queryParameters != null) { 1246 if (query != null && queryParameters != null) {
1228 throw new ArgumentError('Both query and queryParameters specified'); 1247 throw new ArgumentError('Both query and queryParameters specified');
1229 } 1248 }
1230 if (query != null) return _normalize(query, start, end, _queryCharTable); 1249 if (query != null) return _normalize(query, start, end, _queryCharTable);
1231 1250
1232 var result = new StringBuffer(); 1251 var result = new StringBuffer();
1233 var first = true; 1252 var first = true;
(...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after
1422 static bool _isGeneralDelimiter(int ch) { 1441 static bool _isGeneralDelimiter(int ch) {
1423 return ch <= _RIGHT_BRACKET && 1442 return ch <= _RIGHT_BRACKET &&
1424 ((_genDelimitersTable[ch >> 4] & (1 << (ch & 0x0f))) != 0); 1443 ((_genDelimitersTable[ch >> 4] & (1 << (ch & 0x0f))) != 0);
1425 } 1444 }
1426 1445
1427 /** 1446 /**
1428 * Returns whether the URI is absolute. 1447 * Returns whether the URI is absolute.
1429 */ 1448 */
1430 bool get isAbsolute => scheme != "" && fragment == ""; 1449 bool get isAbsolute => scheme != "" && fragment == "";
1431 1450
1432 String _merge(String base, String reference) { 1451 String _mergePaths(String base, String reference) {
1433 if (base.isEmpty) return "/$reference";
1434 // Optimize for the case: absolute base, reference beginning with "../". 1452 // Optimize for the case: absolute base, reference beginning with "../".
1435 int backCount = 0; 1453 int backCount = 0;
1436 int refStart = 0; 1454 int refStart = 0;
1437 // Count number of "../" at beginning of reference. 1455 // Count number of "../" at beginning of reference.
1438 while (reference.startsWith("../", refStart)) { 1456 while (reference.startsWith("../", refStart)) {
1439 refStart += 3; 1457 refStart += 3;
1440 backCount++; 1458 backCount++;
1441 } 1459 }
1442 1460
1443 // Drop last segment - everything after last '/' of base. 1461 // Drop last segment - everything after last '/' of base.
(...skipping 12 matching lines...) Expand all
1456 (delta == 2 || base.codeUnitAt(newEnd + 2) == _DOT)) { 1474 (delta == 2 || base.codeUnitAt(newEnd + 2) == _DOT)) {
1457 break; 1475 break;
1458 } 1476 }
1459 baseEnd = newEnd; 1477 baseEnd = newEnd;
1460 backCount--; 1478 backCount--;
1461 } 1479 }
1462 return base.replaceRange(baseEnd + 1, null, 1480 return base.replaceRange(baseEnd + 1, null,
1463 reference.substring(refStart - 3 * backCount)); 1481 reference.substring(refStart - 3 * backCount));
1464 } 1482 }
1465 1483
1466 bool _hasDotSegments(String path) { 1484 /// Make a guess at whether a path contains a `..` or `.` segments.
floitsch 2015/07/17 13:03:20 segment.
Lasse Reichstein Nielsen 2015/07/17 18:40:22 Done.
1485 ///
1486 /// This is a primitive test that can cause false positives.
1487 /// It's only used to avoid a more expensive operation in the case where
1488 /// it's not necessary.
1489 static bool _hasDotSegments(String path) {
floitsch 2015/07/17 13:03:20 _maybeHasDotSegments ?
Lasse Reichstein Nielsen 2015/07/17 18:40:23 _mayContainDotSegments
1467 if (path.length > 0 && path.codeUnitAt(0) == _DOT) return true; 1490 if (path.length > 0 && path.codeUnitAt(0) == _DOT) return true;
1468 int index = path.indexOf("/."); 1491 int index = path.indexOf("/.");
1469 return index != -1; 1492 return index != -1;
1470 } 1493 }
1471 1494
1472 String _removeDotSegments(String path) { 1495 /// Removes '.' and '..' segments from a path.
1496 ///
1497 /// Follows the RFC 2986 "remove dot segments" algorithm.
1498 /// This algorithm is only used on paths of URIs with a scheme,
1499 /// and it treats the path as if it is absolute (leading '..' are removed).
1500 static String _removeDotSegments(String path) {
1473 if (!_hasDotSegments(path)) return path; 1501 if (!_hasDotSegments(path)) return path;
1474 List<String> output = []; 1502 List<String> output = [];
1475 bool appendSlash = false; 1503 bool appendSlash = false;
1476 for (String segment in path.split("/")) { 1504 for (String segment in path.split("/")) {
1477 appendSlash = false; 1505 appendSlash = false;
1478 if (segment == "..") { 1506 if (segment == "..") {
1479 if (!output.isEmpty && 1507 if (output.isNotEmpty) {
1480 ((output.length != 1) || (output[0] != ""))) output.removeLast(); 1508 output.removeLast();
1509 if (output.isEmpty) {
1510 output.add("");
1511 }
1512 }
1481 appendSlash = true; 1513 appendSlash = true;
1482 } else if ("." == segment) { 1514 } else if ("." == segment) {
1483 appendSlash = true; 1515 appendSlash = true;
1484 } else { 1516 } else {
1485 output.add(segment); 1517 output.add(segment);
1486 } 1518 }
1487 } 1519 }
1488 if (appendSlash) output.add(""); 1520 if (appendSlash) output.add("");
1489 return output.join("/"); 1521 return output.join("/");
1490 } 1522 }
1491 1523
1524 /// Removes all `.` segments and any non-leading `..` segments.
1525 ///
1526 /// Removing the ".." from a "bar/foo/.." sequence results in "bar/"
1527 /// (trailing "/"). If the entire path is removed (because it contains as
1528 /// many ".." segments as real segments), the result is "./".
1529 /// This is different from an empty string, which represents "no path",
1530 /// when you resolve it against a base URI with a path with a non-empty
1531 /// final segment.
1532 static String _normalizeRelativePath(String path) {
1533 assert(!path.startsWith('/')); // Only get called for relative paths.
1534 if (!_hasDotSegments(path)) return path; // Ensures path non-empty below.
floitsch 2015/07/17 13:03:20 If _hasDotSegments return false for the empty stri
Lasse Reichstein Nielsen 2015/07/17 18:40:23 Well, it's pretty certain that an empty path won't
1535 List<String> output = [];
1536 bool appendSlash = false;
1537 for (String segment in path.split("/")) {
1538 appendSlash = false;
1539 if (".." == segment) {
1540 if (!output.isEmpty && output.last != "..") {
1541 output.removeLast();
1542 appendSlash = true;
1543 } else {
1544 output.add("..");
1545 }
1546 } else if ("." == segment) {
1547 appendSlash = true;
1548 } else {
1549 output.add(segment);
1550 }
1551 }
1552 if (output.isEmpty || output.length == 1 && output[0].isEmpty) return "./";
floitsch 2015/07/17 13:03:20 parenthesis
Lasse Reichstein Nielsen 2015/07/17 18:40:22 Ack, with mixed || and &&, some parentheses can im
1553 if (appendSlash || output.last == '..') output.add("");
1554 return output.join("/");
1555 }
1556
1492 /** 1557 /**
1493 * Resolve [reference] as an URI relative to `this`. 1558 * Resolve [reference] as an URI relative to `this`.
1494 * 1559 *
1495 * First turn [reference] into a URI using [Uri.parse]. Then resolve the 1560 * First turn [reference] into a URI using [Uri.parse]. Then resolve the
1496 * resulting URI relative to `this`. 1561 * resulting URI relative to `this`.
1497 * 1562 *
1498 * Returns the resolved URI. 1563 * Returns the resolved URI.
1499 * 1564 *
1500 * See [resolveUri] for details. 1565 * See [resolveUri] for details.
1501 */ 1566 */
1502 Uri resolve(String reference) { 1567 Uri resolve(String reference) {
1503 return resolveUri(Uri.parse(reference)); 1568 return resolveUri(Uri.parse(reference));
1504 } 1569 }
1505 1570
1506 /** 1571 /**
1507 * Resolve [reference] as an URI relative to `this`. 1572 * Resolve [reference] as an URI relative to `this`.
1508 * 1573 *
1509 * Returns the resolved URI. 1574 * Returns the resolved URI.
1510 * 1575 *
1511 * The algorithm for resolving a reference is described in 1576 * The algorithm "Transform Reference" for resolving a reference is
1512 * [RFC-3986 Section 5] 1577 * described in [RFC-3986 Section 5]
1513 * (http://tools.ietf.org/html/rfc3986#section-5 "RFC-1123"). 1578 * (http://tools.ietf.org/html/rfc3986#section-5 "RFC-1123").
1579 *
1580 * Updated to handle the case where the base URI is just a relative path -
1581 * that is: when it has no scheme or authority and the path does not start
1582 * with a slash.
1583 * In that case, the paths are combined without removing leading "..", and
1584 * an empty path is not converted to "/".
1514 */ 1585 */
1515 Uri resolveUri(Uri reference) { 1586 Uri resolveUri(Uri reference) {
1516 // From RFC 3986. 1587 // From RFC 3986.
1517 String targetScheme; 1588 String targetScheme;
1518 String targetUserInfo = ""; 1589 String targetUserInfo = "";
1519 String targetHost; 1590 String targetHost;
1520 int targetPort; 1591 int targetPort;
1521 String targetPath; 1592 String targetPath;
1522 String targetQuery; 1593 String targetQuery;
1523 if (reference.scheme.isNotEmpty) { 1594 if (reference.scheme.isNotEmpty) {
(...skipping 10 matching lines...) Expand all
1534 } else { 1605 } else {
1535 targetScheme = this.scheme; 1606 targetScheme = this.scheme;
1536 if (reference.hasAuthority) { 1607 if (reference.hasAuthority) {
1537 targetUserInfo = reference.userInfo; 1608 targetUserInfo = reference.userInfo;
1538 targetHost = reference.host; 1609 targetHost = reference.host;
1539 targetPort = _makePort(reference.hasPort ? reference.port : null, 1610 targetPort = _makePort(reference.hasPort ? reference.port : null,
1540 targetScheme); 1611 targetScheme);
1541 targetPath = _removeDotSegments(reference.path); 1612 targetPath = _removeDotSegments(reference.path);
1542 if (reference.hasQuery) targetQuery = reference.query; 1613 if (reference.hasQuery) targetQuery = reference.query;
1543 } else { 1614 } else {
1615 targetUserInfo = this._userInfo;
1616 targetHost = this._host;
1617 targetPort = this._port;
1544 if (reference.path == "") { 1618 if (reference.path == "") {
1545 targetPath = this._path; 1619 targetPath = this._path;
1546 if (reference.hasQuery) { 1620 if (reference.hasQuery) {
1547 targetQuery = reference.query; 1621 targetQuery = reference.query;
1548 } else { 1622 } else {
1549 targetQuery = this._query; 1623 targetQuery = this._query;
1550 } 1624 }
1551 } else { 1625 } else {
1552 if (reference.path.startsWith("/")) { 1626 if (reference.hasAbsolutePath) {
1553 targetPath = _removeDotSegments(reference.path); 1627 targetPath = _removeDotSegments(reference.path);
1554 } else { 1628 } else {
1555 targetPath = _removeDotSegments(_merge(this._path, reference.path)); 1629 // This is the RFC 3986 behavior for merging.
1630 if (this.hasEmptyPath) {
1631 if (!this.hasScheme && !this.hasAuthority) {
1632 // Keep the path relative if no scheme or authority.
1633 targetPath = reference.path;
1634 } else {
1635 // Add path normalization on top of RFC algorithm.
1636 targetPath = _removeDotSegments("/" + reference.path);
1637 }
1638 } else {
1639 var mergedPath = _mergePaths(this._path, reference.path);
1640 if (this.hasScheme || this.hasAuthority || this.hasAbsolutePath) {
1641 targetPath = _removeDotSegments(mergedPath);
1642 } else {
1643 // Non-RFC 3986 beavior. If both base and reference are relative
floitsch 2015/07/17 13:03:20 behavior. Also state if this is violating the RFC
Lasse Reichstein Nielsen 2015/07/17 18:40:23 Not covered, will document.
1644 // path, allow the merged path to start with "..".
1645 targetPath = _normalizeRelativePath(mergedPath);
1646 }
1647 }
1556 } 1648 }
1557 if (reference.hasQuery) targetQuery = reference.query; 1649 if (reference.hasQuery) targetQuery = reference.query;
1558 } 1650 }
1559 targetUserInfo = this._userInfo;
1560 targetHost = this._host;
1561 targetPort = this._port;
1562 } 1651 }
1563 } 1652 }
1564 String fragment = reference.hasFragment ? reference.fragment : null; 1653 String fragment = reference.hasFragment ? reference.fragment : null;
1565 return new Uri._internal(targetScheme, 1654 return new Uri._internal(targetScheme,
1566 targetUserInfo, 1655 targetUserInfo,
1567 targetHost, 1656 targetHost,
1568 targetPort, 1657 targetPort,
1569 targetPath, 1658 targetPath,
1570 targetQuery, 1659 targetQuery,
1571 fragment); 1660 fragment);
1572 } 1661 }
1573 1662
1574 /** 1663 /**
1664 * Returns whether the URI has a [scheme] component.
1665 */
1666 bool get hasScheme => scheme.isNotEmpty;
1667
1668 /**
1575 * Returns whether the URI has an [authority] component. 1669 * Returns whether the URI has an [authority] component.
1576 */ 1670 */
1577 bool get hasAuthority => _host != null; 1671 bool get hasAuthority => _host != null;
1578 1672
1579 /** 1673 /**
1580 * Returns whether the URI has an explicit port. 1674 * Returns whether the URI has an explicit port.
1581 * 1675 *
1582 * If the port number is the default port number 1676 * If the port number is the default port number
1583 * (zero for unrecognized schemes, with http (80) and https (443) being 1677 * (zero for unrecognized schemes, with http (80) and https (443) being
1584 * recognized), 1678 * recognized),
1585 * then the port is made implicit and omitted from the URI. 1679 * then the port is made implicit and omitted from the URI.
1586 */ 1680 */
1587 bool get hasPort => _port != null; 1681 bool get hasPort => _port != null;
1588 1682
1589 /** 1683 /**
1590 * Returns whether the URI has a query part. 1684 * Returns whether the URI has a query part.
1591 */ 1685 */
1592 bool get hasQuery => _query != null; 1686 bool get hasQuery => _query != null;
1593 1687
1594 /** 1688 /**
1595 * Returns whether the URI has a fragment part. 1689 * Returns whether the URI has a fragment part.
1596 */ 1690 */
1597 bool get hasFragment => _fragment != null; 1691 bool get hasFragment => _fragment != null;
1598 1692
1599 /** 1693 /**
1694 * Returns whether the URI has an empty path.
1695 */
1696 bool get hasEmptyPath => _path.isEmpty;
1697
1698 /**
1699 * Returns whether the URI has an absolute path (starting with '/').
1700 */
1701 bool get hasAbsolutePath => _path.startsWith('/');
1702
1703 /**
1600 * Returns the origin of the URI in the form scheme://host:port for the 1704 * Returns the origin of the URI in the form scheme://host:port for the
1601 * schemes http and https. 1705 * schemes http and https.
1602 * 1706 *
1603 * It is an error if the scheme is not "http" or "https". 1707 * It is an error if the scheme is not "http" or "https".
1604 * 1708 *
1605 * See: http://www.w3.org/TR/2011/WD-html5-20110405/origin-0.html#origin 1709 * See: http://www.w3.org/TR/2011/WD-html5-20110405/origin-0.html#origin
1606 */ 1710 */
1607 String get origin { 1711 String get origin {
1608 if (scheme == "" || _host == null || _host == "") { 1712 if (scheme == "" || _host == null || _host == "") {
1609 throw new StateError("Cannot use origin without a scheme: $this"); 1713 throw new StateError("Cannot use origin without a scheme: $this");
(...skipping 860 matching lines...) Expand 10 before | Expand all | Expand 10 after
2470 0xafff, // 0x30 - 0x3f 1111111111110101 2574 0xafff, // 0x30 - 0x3f 1111111111110101
2471 // @ABCDEFGHIJKLMNO 2575 // @ABCDEFGHIJKLMNO
2472 0xffff, // 0x40 - 0x4f 1111111111111111 2576 0xffff, // 0x40 - 0x4f 1111111111111111
2473 // PQRSTUVWXYZ _ 2577 // PQRSTUVWXYZ _
2474 0x87ff, // 0x50 - 0x5f 1111111111100001 2578 0x87ff, // 0x50 - 0x5f 1111111111100001
2475 // abcdefghijklmno 2579 // abcdefghijklmno
2476 0xfffe, // 0x60 - 0x6f 0111111111111111 2580 0xfffe, // 0x60 - 0x6f 0111111111111111
2477 // pqrstuvwxyz ~ 2581 // pqrstuvwxyz ~
2478 0x47ff]; // 0x70 - 0x7f 1111111111100010 2582 0x47ff]; // 0x70 - 0x7f 1111111111100010
2479 } 2583 }
OLDNEW
« no previous file with comments | « no previous file | tests/corelib/uri_normalize_path_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698