Chromium Code Reviews| Index: sdk/lib/core/uri.dart |
| diff --git a/sdk/lib/core/uri.dart b/sdk/lib/core/uri.dart |
| index 30057f1132e5fc0952ffa3172755189f76a71116..83b03dbbc399772c9afb980ac48a963e8748faf6 100644 |
| --- a/sdk/lib/core/uri.dart |
| +++ b/sdk/lib/core/uri.dart |
| @@ -8,6 +8,7 @@ part of dart.core; |
| * A parsed URI, as specified by RFC-3986, http://tools.ietf.org/html/rfc3986. |
| */ |
| class Uri { |
| + final String _host; |
| int _port; |
| String _path; |
| @@ -47,7 +48,12 @@ class Uri { |
| * Returns the empty string if there is no authority component and |
| * hence no host. |
| */ |
| - final String host; |
| + String get host { |
| + if (_host != null && _host.startsWith('[')) { |
| + return _host.substring(1, _host.length - 1); |
| + } |
| + return _host; |
| + } |
| /** |
| * Returns the port part of the authority component. |
| @@ -153,9 +159,9 @@ class Uri { |
| * |
| * The fragment component is set through [fragment]. |
| */ |
| - Uri({scheme, |
| + Uri({String scheme, |
| this.userInfo: "", |
| - this.host: "", |
| + String host: "", |
| port: 0, |
| String path, |
| Iterable<String> pathSegments, |
| @@ -163,6 +169,7 @@ class Uri { |
| Map<String, String> queryParameters, |
| fragment: ""}) : |
| scheme = _makeScheme(scheme), |
| + _host = _makeHost(host), |
| query = _makeQuery(query, queryParameters), |
| fragment = _makeFragment(fragment) { |
| // Perform scheme specific normalization. |
| @@ -237,22 +244,33 @@ class Uri { |
| 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.substring(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 (int i = hostStart; i < authority.length; i++) { |
| - if (authority.codeUnitAt(i) == _COLON) { |
| - hasPort = true; |
| - host = authority.substring(hostStart, i); |
| - if (!host.isEmpty) { |
| - var portString = authority.substring(i + 1); |
| - if (portString.isNotEmpty) port = int.parse(portString); |
| - } |
| + for (;hostEnd < authority.length; hostEnd++) { |
|
floitsch
2013/09/10 12:11:46
nit: space after ";" ?
Søren Gjesse
2013/09/10 12:18:03
Please add space after ;
Anders Johnsen
2013/09/11 12:58:31
Done.
Anders Johnsen
2013/09/11 12:58:31
Done.
|
| + if (authority.codeUnitAt(hostEnd) == _COLON) { |
| + var portString = authority.substring(hostEnd + 1); |
| + if (portString.isNotEmpty) port = int.parse(portString); |
|
floitsch
2013/09/10 12:11:46
do we allow: "foo.bar:" as host?
is this valid?
ma
Anders Johnsen
2013/09/11 12:58:31
Yes, we even test for that:
check(new Ur
|
| break; |
| } |
| } |
| - if (!hasPort) { |
| - host = hasUserInfo ? authority.substring(hostStart) : authority; |
| - } |
| + host = authority.substring(hostStart, hostEnd); |
| return new Uri(scheme: scheme, |
| userInfo: userInfo, |
| @@ -474,6 +492,28 @@ class Uri { |
| return _queryParameters; |
| } |
| + static String _makeHost(String host) { |
| + if (host == null || host.isEmpty) return host; |
| + if (host.codeUnitAt(0) == _LEFT_BRACKET) { |
| + if (host.codeUnitAt(host.length - 1) != _RIGHT_BRACKET) { |
| + throw new FormatException('Missing end `]` to match `[` in host'); |
| + } |
| + parseIPv6Address(host.substring(1, host.length - 1)); |
| + return host; |
| + } |
| + for (int i = 0; i < host.length; i++) { |
| + if (host.codeUnitAt(i) == _COLON) { |
| + parseIPv6Address(host); |
| + var buffer = new StringBuffer(); |
| + buffer.writeCharCode(_LEFT_BRACKET); |
|
floitsch
2013/09/10 12:11:46
just write "[$host]".
This will use a string-buffe
Anders Johnsen
2013/09/11 12:58:31
Done.
|
| + buffer.write(host); |
| + buffer.writeCharCode(_RIGHT_BRACKET); |
| + return buffer.toString(); |
| + } |
| + } |
| + return host; |
| + } |
| + |
| static String _makeScheme(String scheme) { |
| bool isSchemeLowerCharacter(int ch) { |
| return ch < 128 && |
| @@ -828,15 +868,15 @@ class Uri { |
| * See: http://www.w3.org/TR/2011/WD-html5-20110405/origin-0.html#origin |
| */ |
| String get origin { |
| - if (scheme == "" || host == null || host == "") { |
| + if (scheme == "" || _host == null || _host == "") { |
| throw new StateError("Cannot use origin without a scheme: $this"); |
| } |
| if (scheme != "http" && scheme != "https") { |
| throw new StateError( |
| "Origin is only applicable schemes http and https: $this"); |
| } |
| - if (port == 0) return "$scheme://$host"; |
| - return "$scheme://$host:$port"; |
| + if (port == 0) return "$scheme://$_host"; |
| + return "$scheme://$_host:$port"; |
| } |
| /** |
| @@ -962,8 +1002,7 @@ class Uri { |
| void _writeAuthority(StringSink ss) { |
| _addIfNonEmpty(ss, userInfo, userInfo, "@"); |
| - ss.write(host == null ? "null" : |
| - host.contains(':') ? '[$host]' : host); |
| + ss.write(_host == null ? "null" : _host); |
| if (port != 0) { |
| ss.write(":"); |
| ss.write(port.toString()); |
| @@ -1148,6 +1187,115 @@ class Uri { |
| }); |
| } |
| + /** |
| + * Parse the [host] as a IPv4 address, returning the address as a list of |
|
floitsch
2013/09/10 12:11:46
as an IPv4
Søren Gjesse
2013/09/10 12:18:03
In socket we always write "IP version 4 (IPv4)".
Anders Johnsen
2013/09/11 12:58:31
Done.
|
| + * 4 bytes in network byte order (big endian). |
| + * |
| + * If it's unable to parse the address as IPv4, a `FormatException` will be |
|
floitsch
2013/09/10 12:11:46
If [host] is not a valid IPv4 address representati
Anders Johnsen
2013/09/11 12:58:31
Done.
|
| + * thrown. |
| + */ |
| + static List<int> parseIPv4Address(String host) { |
| + var bytes = host.split('.'); |
| + if (bytes.length != 4) { |
| + throw new FormatException( |
|
Søren Gjesse
2013/09/10 12:18:03
How about making all these FormatExceptions start
Anders Johnsen
2013/09/11 12:58:31
Done.
|
| + 'A IPv4 address should contain exactly 4 parts'); |
| + } |
| + return bytes |
| + .map((byteString) { |
| + int byte = int.parse(byteString); |
| + if (byte < 0 || byte > 255) { |
| + throw new FormatException( |
| + 'Each part must be in the range of `0...255`'); |
|
Søren Gjesse
2013/09/10 12:18:03
Only two dots in range.
Anders Johnsen
2013/09/11 12:58:31
Done.
|
| + } |
| + return byte; |
| + }) |
|
Søren Gjesse
2013/09/10 12:18:03
Maybe this should be a Uint8List.
floitsch
2013/09/10 12:23:32
Currently not possible. Afaik IE still doesn't hav
|
| + .toList(); |
| + } |
| + |
| + /** |
| + * Parse the [host] as a IPv6 address, returning the address as a list of |
|
floitsch
2013/09/10 12:11:46
an IPv6
Anders Johnsen
2013/09/11 12:58:31
Done.
|
| + * 16 bytes in network byte order (big endian). |
| + * |
| + * If it's unable to parse the address as IPv6, a `FormatException` will be |
|
floitsch
2013/09/10 12:11:46
ditto.
Anders Johnsen
2013/09/11 12:58:31
Done.
|
| + * thrown. |
| + */ |
|
floitsch
2013/09/10 12:11:46
Provide examples.
This would make it also easier t
Anders Johnsen
2013/09/11 12:58:31
Done.
|
| + static List<int> parseIPv6Address(String host) { |
| + int parseHex(int start, int end) { |
| + if (end - start > 4) { |
| + throw new FormatException( |
| + 'An IPv6 part can only contain a maximum of 4 hex digits'); |
| + } |
| + int value = int.parse(host.substring(start, end), radix: 16); |
| + if (value < 0 || value > (1 << 16) - 1) { |
| + throw new FormatException( |
| + 'Each part must be in the range of `0x0...0xFFFF`'); |
|
Søren Gjesse
2013/09/10 12:18:03
Only two dots in range.
Anders Johnsen
2013/09/11 12:58:31
Done.
|
| + } |
| + return value; |
| + } |
| + if (host.length < 2) throw new FormatException('IPv6 addres is too short'); |
| + List<int> segments = []; |
| + bool wildcardSeen = false; |
| + int partStart = 0; |
| + for (int i = 0; i < host.length; i++) { |
|
floitsch
2013/09/10 12:11:46
Add comment that this will parse all segments exce
Anders Johnsen
2013/09/11 12:58:31
Done.
|
| + if (host.codeUnitAt(i) == _COLON) { |
| + if (i == 0) { |
| + // If we see a `:` in the beginning, expect wildcard. |
| + i++; |
| + if (host.codeUnitAt(i) != _COLON) { |
| + throw new FormatException('Invalid start colon.'); |
| + } |
| + partStart = i; |
| + } |
| + if (i == partStart) { |
| + // Wildcard. We only allow one. |
| + if (wildcardSeen) { |
| + throw new FormatException('Only one IPv6 wildcart `::` is allowed'); |
|
floitsch
2013/09/10 12:11:46
wildcard
Anders Johnsen
2013/09/11 12:58:31
Done.
|
| + } |
| + wildcardSeen = true; |
| + segments.add(-1); |
| + } else { |
| + // Found a single colon. Parse [partStart..i] as a hex entry. |
| + segments.add(parseHex(partStart, i)); |
| + } |
| + partStart = i + 1; |
| + } |
| + } |
| + if (segments.length == 0) throw new FormatException('Too few IPv6 parts'); |
| + if (!(segments.last == -1 && partStart == host.length)) { |
|
floitsch
2013/09/10 12:11:46
Do the check explicitly:
if (partStart == host.len
Anders Johnsen
2013/09/11 12:58:31
Done.
|
| + try { |
| + segments.add(parseHex(partStart, host.length)); |
| + } catch (e) { |
| + // Failed to parse the last chunk as hex. Try IPv4. |
| + try { |
| + List<int> last = parseIPv4Address(host.substring(partStart)); |
| + segments.add(last[0] << 8 | last[1]); |
| + segments.add(last[2] << 8 | last[3]); |
| + } catch (e) { |
| + throw new FormatException('Invalid end of IPv6 address.'); |
| + } |
| + } |
| + if (segments.length > 8 || |
| + (wildcardSeen && segments.length > 7)) { |
| + |
| + throw new FormatException( |
| + 'An IPv6 address can only contain maximum 8 parts'); |
| + } |
| + } |
| + return segments |
|
Søren Gjesse
2013/09/10 12:18:03
Wouldn't it be simpler to allocate a list of lengt
Anders Johnsen
2013/09/11 12:58:31
IMO this is easier, than using indexes into a list
|
| + .expand((value) { |
| + if (value == -1) { |
| + var list = []; |
| + for (int i = 0; i < 9 - segments.length; i++) { |
| + list.addAll(const [0, 0]); |
| + } |
| + return list; |
| + } else { |
| + return [(value >> 8) & 0xFF, value & 0xFF]; |
| + } |
| + }) |
| + .toList(); |
| + } |
| + |
| // Frequently used character codes. |
| static const int _DOUBLE_QUOTE = 0x22; |
| static const int _PERCENT = 0x25; |
| @@ -1164,7 +1312,9 @@ class Uri { |
| static const int _UPPER_CASE_A = 0x41; |
| static const int _UPPER_CASE_F = 0x46; |
| static const int _UPPER_CASE_Z = 0x5A; |
| + static const int _LEFT_BRACKET = 0x5B; |
| static const int _BACKSLASH = 0x5C; |
| + static const int _RIGHT_BRACKET = 0x5D; |
| static const int _LOWER_CASE_A = 0x61; |
| static const int _LOWER_CASE_F = 0x66; |
| static const int _LOWER_CASE_Z = 0x7A; |