| Index: sdk/lib/core/uri.dart
|
| diff --git a/sdk/lib/core/uri.dart b/sdk/lib/core/uri.dart
|
| index 9d1466c71b5c9aba0cf4eb72d0238386707cd57d..73a97d80e1c3288aac66beca9735b5bf3d59c4c8 100644
|
| --- a/sdk/lib/core/uri.dart
|
| +++ b/sdk/lib/core/uri.dart
|
| @@ -16,14 +16,8 @@ part of dart.core;
|
| * [libtour]: http://www.dartlang.org/docs/dart-up-and-running/contents/ch03.html
|
| */
|
| class Uri {
|
| - // The host name of the URI.
|
| - // Set to `null` if there is no authority in a URI.
|
| final String _host;
|
| - // The port. Set to null if there is no port. Normalized to null if
|
| - // the port is the default port for the scheme.
|
| - // Set to the value of the default port if an empty port was supplied.
|
| int _port;
|
| - // The path. Always non-null.
|
| String _path;
|
|
|
| /**
|
| @@ -31,8 +25,6 @@ class Uri {
|
| *
|
| * Returns the empty string if there is no scheme component.
|
| */
|
| - // We represent the missing scheme as an empty string.
|
| - // A valid scheme cannot be empty.
|
| final String scheme;
|
|
|
| /**
|
| @@ -51,20 +43,12 @@ class Uri {
|
| }
|
|
|
| /**
|
| - * The user-info part of the authority.
|
| - *
|
| - * Does not distinguish between an empty user-info and an absent one.
|
| - * The value is always non-null.
|
| - */
|
| - final String _userInfo;
|
| -
|
| - /**
|
| * Returns the user info part of the authority component.
|
| *
|
| * Returns the empty string if there is no user info in the
|
| * authority component.
|
| */
|
| - String get userInfo => _userInfo;
|
| + final String userInfo;
|
|
|
| /**
|
| * Returns the host part of the authority component.
|
| @@ -76,8 +60,7 @@ class Uri {
|
| * removed.
|
| */
|
| String get host {
|
| - if (_host == null) return "";
|
| - if (_host.startsWith('[')) {
|
| + if (_host != null && _host.startsWith('[')) {
|
| return _host.substring(1, _host.length - 1);
|
| }
|
| return _host;
|
| @@ -86,21 +69,16 @@ class Uri {
|
| /**
|
| * Returns the port part of the authority component.
|
| *
|
| - * Returns the defualt port if there is no port number in the authority
|
| - * component. That's 80 for http, 443 for https, and 0 for everything else.
|
| + * Returns 0 if there is no port in the authority component.
|
| */
|
| int get port {
|
| - if (_port == null) return _defaultPort(scheme);
|
| + if (_port == 0) {
|
| + if (scheme == "http") return 80;
|
| + if (scheme == "https") return 443;
|
| + }
|
| return _port;
|
| }
|
|
|
| - // The default port for the scheme of this Uri..
|
| - static int _defaultPort(String scheme) {
|
| - if (scheme == "http") return 80;
|
| - if (scheme == "https") return 443;
|
| - return 0;
|
| - }
|
| -
|
| /**
|
| * Returns the path component.
|
| *
|
| @@ -111,19 +89,13 @@ class Uri {
|
| */
|
| String get path => _path;
|
|
|
| - // The query content, or null if there is no query.
|
| - final String _query;
|
| -
|
| /**
|
| * Returns the query component. The returned query is encoded. To get
|
| * direct access to the decoded query use [queryParameters].
|
| *
|
| * Returns the empty string if there is no query component.
|
| */
|
| - String get query => (_query == null) ? "" : _query;
|
| -
|
| - // The fragment content, or null if there is no fragment.
|
| - final String _fragment;
|
| + final String query;
|
|
|
| /**
|
| * Returns the fragment identifier component.
|
| @@ -131,7 +103,7 @@ class Uri {
|
| * Returns the empty string if there is no fragment identifier
|
| * component.
|
| */
|
| - String get fragment => (_fragment == null) ? "" : _fragment;
|
| + final String fragment;
|
|
|
| /**
|
| * Cache the computed return value of [pathSegements].
|
| @@ -210,12 +182,12 @@ class Uri {
|
| const int EOI = -1;
|
|
|
| String scheme = "";
|
| + String path;
|
| String userinfo = "";
|
| - String host = null;
|
| - int port = null;
|
| - String path = null;
|
| - String query = null;
|
| - String fragment = null;
|
| + String host = "";
|
| + int port = 0;
|
| + String query = "";
|
| + String fragment = "";
|
|
|
| int index = 0;
|
| int pathStart = 0;
|
| @@ -262,16 +234,13 @@ class Uri {
|
| hostStart = lastAt + 1;
|
| }
|
| if (lastColon >= 0) {
|
| - int portNumber;
|
| - if (lastColon + 1 < index) {
|
| - portNumber = 0;
|
| - for (int i = lastColon + 1; i < index; i++) {
|
| - int digit = uri.codeUnitAt(i);
|
| - if (_ZERO > digit || _NINE < digit) {
|
| - _fail(uri, i, "Invalid port number");
|
| - }
|
| - portNumber = portNumber * 10 + (digit - _ZERO);
|
| + int portNumber = 0;
|
| + for (int i = lastColon + 1; i < index; i++) {
|
| + int digit = uri.codeUnitAt(i);
|
| + if (_ZERO > digit || _NINE < digit) {
|
| + _fail(uri, i, "Invalid port number");
|
| }
|
| + portNumber = portNumber * 10 + (digit - _ZERO);
|
| }
|
| port = _makePort(portNumber, scheme);
|
| hostEnd = lastColon;
|
| @@ -371,7 +340,7 @@ class Uri {
|
| }
|
|
|
| assert(state == NOT_IN_PATH);
|
| - bool ensureLeadingSlash = (host != null || scheme == "file");
|
| + bool ensureLeadingSlash = (host != "" || scheme == "file");
|
| path = _makePath(uri, pathStart, index, null, ensureLeadingSlash);
|
|
|
| if (char == _QUESTION) {
|
| @@ -428,27 +397,27 @@ class Uri {
|
|
|
| /// Internal non-verifying constructor. Only call with validated arguments.
|
| Uri._internal(this.scheme,
|
| - this._userInfo,
|
| + this.userInfo,
|
| this._host,
|
| this._port,
|
| this._path,
|
| - this._query,
|
| - this._fragment);
|
| + this.query,
|
| + this.fragment);
|
|
|
| /**
|
| * Creates a new URI from its components.
|
| *
|
| * Each component is set through a named argument. Any number of
|
| - * components can be provided. The [path] and [query] components can be set
|
| - * using either of two different named arguments.
|
| + * components can be provided. The default value for the components
|
| + * not provided is the empry string, except for [port] which has a
|
| + * default value of 0. The [path] and [query] components can be set
|
| + * using two different named arguments.
|
| *
|
| * The scheme component is set through [scheme]. The scheme is
|
| - * normalized to all lowercase letters. If the scheme is omitted or empty,
|
| - * the URI will not have a scheme part.
|
| + * normalized to all lowercase letters.
|
| *
|
| * The user info part of the authority component is set through
|
| - * [userInfo]. It defaults to the empty string, which will be omitted
|
| - * from the string representation of the URI.
|
| + * [userInfo].
|
| *
|
| * The host part of the authority component is set through
|
| * [host]. The host can either be a hostname, an IPv4 address or an
|
| @@ -457,22 +426,14 @@ class Uri {
|
| * The host is normalized to all lowercase letters.
|
| *
|
| * The port part of the authority component is set through
|
| - * [port].
|
| - * If [port] is omitted or `null`, it implies the default port for
|
| - * the URI's scheme, and is equivalent to passing that port explicitly.
|
| - * The recognized schemes, and their default ports, are "http" (80) and
|
| - * "https" (443). All other schemes are considered as having zero as the
|
| - * default port.
|
| - *
|
| - * If any of `userInfo`, `host` or `port` are provided,
|
| - * the URI will have an autority according to [hasAuthority].
|
| + * [port]. The port is normalized for scheme http and https where
|
| + * port 80 and port 443 respectively is set.
|
| *
|
| * The path component is set through either [path] or
|
| - * [pathSegments]. When [path] is used, it should be a valid URI path,
|
| - * but invalid characters, except the general delimiters ':/@[]?#',
|
| - * will be escaped if necessary.
|
| - * When [pathSegments] is used, each of the provided segments
|
| - * is first percent-encoded and then joined using the forward slash
|
| + * [pathSegments]. When [path] is used, the provided string is
|
| + * expected to be fully percent-encoded, and is used in its literal
|
| + * form. When [pathSegments] is used, each of the provided segments
|
| + * is percent-encoded and joined using the forward slash
|
| * separator. The percent-encoding of the path segments encodes all
|
| * characters except for the unreserved characters and the following
|
| * list of characters: `!$&'()*+,;=:@`. If the other components
|
| @@ -480,43 +441,32 @@ class Uri {
|
| * not already there.
|
| *
|
| * The query component is set through either [query] or
|
| - * [queryParameters]. When [query] is used the provided string should
|
| - * be a valid URI query, but invalid characters other than general delimiters,
|
| - * will be escaped if necessary.
|
| - * When [queryParameters] is used the query is built from the
|
| + * [queryParameters]. When [query] is used the provided string is
|
| + * expected to be fully percent-encoded and is used in its literal
|
| + * form. When [queryParameters] is used the query is built from the
|
| * provided map. Each key and value in the map is percent-encoded
|
| * and joined using equal and ampersand characters. The
|
| * percent-encoding of the keys and values encodes all characters
|
| * except for the unreserved characters.
|
| - * If both `query` and `queryParameters` are omitted or `null`, the
|
| - * URI will have no query part.
|
| *
|
| * The fragment component is set through [fragment].
|
| - * It should be a valid URI fragment, but invalid characters other than
|
| - * general delimiters, will be escaped if necessary.
|
| - * If `fragment` is omitted or `null`, the URI will have no fragment part.
|
| */
|
| - factory Uri({String scheme : "",
|
| - String userInfo : "",
|
| - String host,
|
| - int port,
|
| - String path,
|
| - Iterable<String> pathSegments,
|
| - String query,
|
| - Map<String, String> queryParameters,
|
| - fragment}) {
|
| + factory Uri({String scheme,
|
| + String userInfo: "",
|
| + String host: "",
|
| + port: 0,
|
| + String path,
|
| + Iterable<String> pathSegments,
|
| + String query,
|
| + Map<String, String> queryParameters,
|
| + fragment: ""}) {
|
| scheme = _makeScheme(scheme, _stringOrNullLength(scheme));
|
| userInfo = _makeUserInfo(userInfo, 0, _stringOrNullLength(userInfo));
|
| host = _makeHost(host, 0, _stringOrNullLength(host), false);
|
| query = _makeQuery(query, 0, _stringOrNullLength(query), queryParameters);
|
| fragment = _makeFragment(fragment, 0, _stringOrNullLength(fragment));
|
| port = _makePort(port, scheme);
|
| - bool isFile = (scheme == "file");
|
| - if (host == null &&
|
| - (userInfo.isNotEmpty || port != null || isFile)) {
|
| - host = "";
|
| - }
|
| - bool ensureLeadingSlash = (host != null || isFile);
|
| + bool ensureLeadingSlash = (host != "" || scheme == "file");
|
| path = _makePath(path, 0, _stringOrNullLength(path), pathSegments,
|
| ensureLeadingSlash);
|
|
|
| @@ -546,9 +496,7 @@ class Uri {
|
| * The `scheme` is always set to `http`.
|
| *
|
| * The `userInfo`, `host` and `port` components are set from the
|
| - * [authority] argument. If `authority` is `null` or empty,
|
| - * the created `Uri` will have no authority, and will not be directly usable
|
| - * as an HTTP URL, which must have a non-empty host.
|
| + * [authority] argument.
|
| *
|
| * The `path` component is set from the [unencodedPath]
|
| * argument. The path passed must not be encoded as this constructor
|
| @@ -579,51 +527,50 @@ class Uri {
|
| String authority,
|
| String unencodedPath,
|
| Map<String, String> queryParameters) {
|
| - var userInfo = null;
|
| - var host = null;
|
| - var port = null;
|
| -
|
| - if (authority != null && authority.isNotEmpty) {
|
| - var hostStart = 0;
|
| - // Split off the user info.
|
| - bool hasUserInfo = false;
|
| - for (int i = 0; i < authority.length; i++) {
|
| - if (authority.codeUnitAt(i) == _AT_SIGN) {
|
| - hasUserInfo = true;
|
| - userInfo = authority.substring(0, i);
|
| - hostStart = i + 1;
|
| - break;
|
| - }
|
| - }
|
| - var hostEnd = hostStart;
|
| - if (hostStart < authority.length &&
|
| - authority.codeUnitAt(hostStart) == _LEFT_BRACKET) {
|
| - // IPv6 host.
|
| - for (; hostEnd < authority.length; hostEnd++) {
|
| - if (authority.codeUnitAt(hostEnd) == _RIGHT_BRACKET) break;
|
| - }
|
| - if (hostEnd == authority.length) {
|
| - throw new FormatException("Invalid IPv6 host entry.");
|
| - }
|
| - parseIPv6Address(authority, hostStart + 1, hostEnd);
|
| - hostEnd++; // Skip the closing bracket.
|
| - if (hostEnd != authority.length &&
|
| - authority.codeUnitAt(hostEnd) != _COLON) {
|
| - throw new FormatException("Invalid end of authority");
|
| - }
|
| + var userInfo = "";
|
| + var host = "";
|
| + var port = 0;
|
| +
|
| + var hostStart = 0;
|
| + // Split off the user info.
|
| + bool hasUserInfo = false;
|
| + for (int i = 0; i < authority.length; i++) {
|
| + if (authority.codeUnitAt(i) == _AT_SIGN) {
|
| + hasUserInfo = true;
|
| + userInfo = authority.substring(0, i);
|
| + hostStart = i + 1;
|
| + break;
|
| }
|
| - // Split host and port.
|
| - bool hasPort = false;
|
| + }
|
| + var hostEnd = hostStart;
|
| + if (hostStart < authority.length &&
|
| + authority.codeUnitAt(hostStart) == _LEFT_BRACKET) {
|
| + // IPv6 host.
|
| for (; hostEnd < authority.length; hostEnd++) {
|
| - if (authority.codeUnitAt(hostEnd) == _COLON) {
|
| - var portString = authority.substring(hostEnd + 1);
|
| - // We allow the empty port - falling back to initial value.
|
| - if (portString.isNotEmpty) port = int.parse(portString);
|
| - break;
|
| - }
|
| + if (authority.codeUnitAt(hostEnd) == _RIGHT_BRACKET) break;
|
| + }
|
| + if (hostEnd == authority.length) {
|
| + throw new FormatException("Invalid IPv6 host entry.");
|
| + }
|
| + parseIPv6Address(authority, hostStart + 1, hostEnd);
|
| + hostEnd++; // Skip the closing bracket.
|
| + if (hostEnd != authority.length &&
|
| + authority.codeUnitAt(hostEnd) != _COLON) {
|
| + throw new FormatException("Invalid end of authority");
|
| + }
|
| + }
|
| + // Split host and port.
|
| + bool hasPort = false;
|
| + for (; hostEnd < authority.length; hostEnd++) {
|
| + if (authority.codeUnitAt(hostEnd) == _COLON) {
|
| + var portString = authority.substring(hostEnd + 1);
|
| + // We allow the empty port - falling back to initial value.
|
| + if (portString.isNotEmpty) port = int.parse(portString);
|
| + break;
|
| }
|
| - host = authority.substring(hostStart, hostEnd);
|
| }
|
| + host = authority.substring(hostStart, hostEnd);
|
| +
|
| return new Uri(scheme: scheme,
|
| userInfo: userInfo,
|
| host: host,
|
| @@ -774,7 +721,7 @@ class Uri {
|
|
|
| static _makeFileUri(String path) {
|
| String sep = "/";
|
| - if (path.startsWith(sep)) {
|
| + if (path.length > 0 && path[0] == sep) {
|
| // Absolute file:// URI.
|
| return new Uri(scheme: "file", pathSegments: path.split(sep));
|
| } else {
|
| @@ -883,7 +830,12 @@ class Uri {
|
|
|
| static int _makePort(int port, String scheme) {
|
| // Perform scheme specific normalization.
|
| - if (port != null && port == _defaultPort(scheme)) return null;
|
| + if (port == 80 && scheme == "http") {
|
| + return 0;
|
| + }
|
| + if (port == 443 && scheme == "https") {
|
| + return 0;
|
| + }
|
| return port;
|
| }
|
|
|
| @@ -900,6 +852,7 @@ class Uri {
|
| */
|
| static String _makeHost(String host, int start, int end, bool strictIPv6) {
|
| // TODO(lrn): Should we normalize IPv6 addresses according to RFC 5952?
|
| +
|
| if (host == null) return null;
|
| if (start == end) return "";
|
| // Host is an IPv6 address if it starts with '[' or contains a colon.
|
| @@ -1032,7 +985,7 @@ class Uri {
|
| }
|
|
|
| static String _makeUserInfo(String userInfo, int start, int end) {
|
| - if (userInfo == null) return "";
|
| + if (userInfo == null) return "null";
|
| return _normalize(userInfo, start, end, _userinfoTable);
|
| }
|
|
|
| @@ -1057,7 +1010,7 @@ class Uri {
|
|
|
| static String _makeQuery(String query, int start, int end,
|
| Map<String, String> queryParameters) {
|
| - if (query == null && queryParameters == null) return null;
|
| + if (query == null && queryParameters == null) return "";
|
| if (query != null && queryParameters != null) {
|
| throw new ArgumentError('Both query and queryParameters specified');
|
| }
|
| @@ -1080,7 +1033,7 @@ class Uri {
|
| }
|
|
|
| static String _makeFragment(String fragment, int start, int end) {
|
| - if (fragment == null) return null;
|
| + if (fragment == null) return "";
|
| return _normalize(fragment, start, end, _queryCharTable);
|
| }
|
|
|
| @@ -1325,81 +1278,55 @@ class Uri {
|
| int targetPort;
|
| String targetPath;
|
| String targetQuery;
|
| - if (reference.scheme.isNotEmpty) {
|
| + if (reference.scheme != "") {
|
| targetScheme = reference.scheme;
|
| - if (reference.hasAuthority) {
|
| - targetUserInfo = reference.userInfo;
|
| - targetHost = reference.host;
|
| - targetPort = reference.hasPort ? reference.port : null;
|
| - }
|
| + targetUserInfo = reference.userInfo;
|
| + targetHost = reference.host;
|
| + targetPort = reference.port;
|
| targetPath = _removeDotSegments(reference.path);
|
| - if (reference.hasQuery) {
|
| - targetQuery = reference.query;
|
| - }
|
| + targetQuery = reference.query;
|
| } else {
|
| - targetScheme = this.scheme;
|
| if (reference.hasAuthority) {
|
| targetUserInfo = reference.userInfo;
|
| targetHost = reference.host;
|
| - targetPort = _makePort(reference.hasPort ? reference.port : null,
|
| - targetScheme);
|
| + targetPort = reference.port;
|
| targetPath = _removeDotSegments(reference.path);
|
| - if (reference.hasQuery) targetQuery = reference.query;
|
| + targetQuery = reference.query;
|
| } else {
|
| if (reference.path == "") {
|
| - targetPath = this._path;
|
| - if (reference.hasQuery) {
|
| + targetPath = this.path;
|
| + if (reference.query != "") {
|
| targetQuery = reference.query;
|
| } else {
|
| - targetQuery = this._query;
|
| + targetQuery = this.query;
|
| }
|
| } else {
|
| if (reference.path.startsWith("/")) {
|
| targetPath = _removeDotSegments(reference.path);
|
| } else {
|
| - targetPath = _removeDotSegments(_merge(this._path, reference.path));
|
| + targetPath = _removeDotSegments(_merge(this.path, reference.path));
|
| }
|
| - if (reference.hasQuery) targetQuery = reference.query;
|
| + targetQuery = reference.query;
|
| }
|
| - targetUserInfo = this._userInfo;
|
| - targetHost = this._host;
|
| - targetPort = this._port;
|
| + targetUserInfo = this.userInfo;
|
| + targetHost = this.host;
|
| + targetPort = this.port;
|
| }
|
| + targetScheme = this.scheme;
|
| }
|
| - String fragment = reference.hasFragment ? reference.fragment : null;
|
| - return new Uri._internal(targetScheme,
|
| - targetUserInfo,
|
| - targetHost,
|
| - targetPort,
|
| - targetPath,
|
| - targetQuery,
|
| - fragment);
|
| + return new Uri(scheme: targetScheme,
|
| + userInfo: targetUserInfo,
|
| + host: targetHost,
|
| + port: targetPort,
|
| + path: targetPath,
|
| + query: targetQuery,
|
| + fragment: reference.fragment);
|
| }
|
|
|
| /**
|
| * Returns whether the URI has an [authority] component.
|
| */
|
| - bool get hasAuthority => _host != null;
|
| -
|
| - /**
|
| - * Returns whether the URI has an explicit port.
|
| - *
|
| - * If the port number is the default port number
|
| - * (zero for unrecognized schemes, with http (80) and https (443) being
|
| - * recognized),
|
| - * then the port is made implicit and omitted from the URI.
|
| - */
|
| - bool get hasPort => _port != null;
|
| -
|
| - /**
|
| - * Returns whether the URI has a query part.
|
| - */
|
| - bool get hasQuery => _query != null;
|
| -
|
| - /**
|
| - * Returns whether the URI has a fragment part.
|
| - */
|
| - bool get hasFragment => _fragment != null;
|
| + bool get hasAuthority => host != "";
|
|
|
| /**
|
| * Returns the origin of the URI in the form scheme://host:port for the
|
| @@ -1417,7 +1344,7 @@ class Uri {
|
| throw new StateError(
|
| "Origin is only applicable schemes http and https: $this");
|
| }
|
| - if (_port == null) return "$scheme://$_host";
|
| + if (_port == 0) return "$scheme://$_host";
|
| return "$scheme://$_host:$_port";
|
| }
|
|
|
| @@ -1544,14 +1471,11 @@ class Uri {
|
| }
|
|
|
| void _writeAuthority(StringSink ss) {
|
| - if (_userInfo.isNotEmpty) {
|
| - ss.write(_userInfo);
|
| - ss.write("@");
|
| - }
|
| - if (_host != null) ss.write(_host);
|
| - if (_port != null) {
|
| + _addIfNonEmpty(ss, userInfo, userInfo, "@");
|
| + ss.write(_host == null ? "null" : _host);
|
| + if (_port != 0) {
|
| ss.write(":");
|
| - ss.write(_port);
|
| + ss.write(_port.toString());
|
| }
|
| }
|
|
|
| @@ -1565,8 +1489,8 @@ class Uri {
|
| _writeAuthority(sb);
|
| }
|
| sb.write(path);
|
| - if (_query != null) { sb..write("?")..write(_query); }
|
| - if (_fragment != null) { sb..write("#")..write(_fragment); }
|
| + _addIfNonEmpty(sb, query, "?", query);
|
| + _addIfNonEmpty(sb, fragment, "#", fragment);
|
| return sb.toString();
|
| }
|
|
|
|
|