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

Unified Diff: sdk/lib/core/uri.dart

Issue 23904004: Accept IPv6 addresses in Uri.http and Uri.https, and correctly nest IPv6 addresses in '[' and ']'. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Add parsing of IPv4 and IPv6 to Uri and use those. Created 7 years, 3 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | tests/corelib/uri_http_test.dart » ('j') | tests/corelib/uri_ipv6_test.dart » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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;
« no previous file with comments | « no previous file | tests/corelib/uri_http_test.dart » ('j') | tests/corelib/uri_ipv6_test.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698