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

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

Issue 352093003: Try to retain original structure of URI. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 6 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 | Annotate | Revision Log
« no previous file with comments | « no previous file | tests/co19/co19-co19.status » ('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:**
11 * 11 *
12 * * [URIs][uris] in the [library tour][libtour] 12 * * [URIs][uris] in the [library tour][libtour]
13 * * [RFC-3986](http://tools.ietf.org/html/rfc3986) 13 * * [RFC-3986](http://tools.ietf.org/html/rfc3986)
14 * 14 *
15 * [uris]: http://www.dartlang.org/docs/dart-up-and-running/contents/ch03.html#c h03-uri 15 * [uris]: http://www.dartlang.org/docs/dart-up-and-running/contents/ch03.html#c h03-uri
16 * [libtour]: http://www.dartlang.org/docs/dart-up-and-running/contents/ch03.htm l 16 * [libtour]: http://www.dartlang.org/docs/dart-up-and-running/contents/ch03.htm l
17 */ 17 */
18 class Uri { 18 class Uri {
19 // The host name of the URI.
20 // Set to `null` if there is no authority in a URI.
19 final String _host; 21 final String _host;
22 // The port. Set to null if there is no port. Normalized to null if
23 // the port is the default port for the scheme.
24 // Set to the value of the default port if an empty port was supplied.
20 int _port; 25 int _port;
26 // The path. Always non-null.
21 String _path; 27 String _path;
22 28
23 /** 29 /**
24 * Returns the scheme component. 30 * Returns the scheme component.
25 * 31 *
26 * Returns the empty string if there is no scheme component. 32 * Returns the empty string if there is no scheme component.
27 */ 33 */
34 // We represent the missing scheme as an empty string.
35 // A valid scheme cannot be empty.
28 final String scheme; 36 final String scheme;
29 37
30 /** 38 /**
31 * Returns the authority component. 39 * Returns the authority component.
32 * 40 *
33 * The authority is formatted from the [userInfo], [host] and [port] 41 * The authority is formatted from the [userInfo], [host] and [port]
34 * parts. 42 * parts.
35 * 43 *
36 * Returns the empty string if there is no authority component. 44 * Returns the empty string if there is no authority component.
37 */ 45 */
38 String get authority { 46 String get authority {
39 if (!hasAuthority) return ""; 47 if (!hasAuthority) return "";
40 var sb = new StringBuffer(); 48 var sb = new StringBuffer();
41 _writeAuthority(sb); 49 _writeAuthority(sb);
42 return sb.toString(); 50 return sb.toString();
43 } 51 }
44 52
45 /** 53 /**
54 * The user-info part of the authority.
55 *
56 * Does not distinguish between an empty user-info and an absent one.
57 * The value is always non-null.
Anders Johnsen 2014/06/25 09:18:00 This contradicts with the getter below.
Lasse Reichstein Nielsen 2014/06/25 10:49:18 True, I don't need the getter anyway.
58 */
59 final String _userInfo;
60
61 /**
46 * Returns the user info part of the authority component. 62 * Returns the user info part of the authority component.
47 * 63 *
48 * Returns the empty string if there is no user info in the 64 * Returns the empty string if there is no user info in the
49 * authority component. 65 * authority component.
50 */ 66 */
51 final String userInfo; 67 String get userInfo {
68 return (_userInfo != null) ? _userInfo : "";
69 }
52 70
53 /** 71 /**
54 * Returns the host part of the authority component. 72 * Returns the host part of the authority component.
55 * 73 *
56 * Returns the empty string if there is no authority component and 74 * Returns the empty string if there is no authority component and
57 * hence no host. 75 * hence no host.
58 * 76 *
59 * If the host is an IP version 6 address, the surrounding `[` and `]` is 77 * If the host is an IP version 6 address, the surrounding `[` and `]` is
60 * removed. 78 * removed.
61 */ 79 */
62 String get host { 80 String get host {
63 if (_host != null && _host.startsWith('[')) { 81 if (_host == null) return "";
82 if (_host.startsWith('[')) {
64 return _host.substring(1, _host.length - 1); 83 return _host.substring(1, _host.length - 1);
65 } 84 }
66 return _host; 85 return _host;
67 } 86 }
68 87
69 /** 88 /**
70 * Returns the port part of the authority component. 89 * Returns the port part of the authority component.
71 * 90 *
72 * Returns 0 if there is no port in the authority component. 91 * Also returns 0 if there is no port number in the authority component.
Anders Johnsen 2014/06/25 09:18:00 remove 'Also', it only returns once.
Lasse Reichstein Nielsen 2014/06/25 10:49:18 Done.
73 */ 92 */
74 int get port { 93 int get port {
75 if (_port == 0) { 94 if (_port == null) return _defaultPort(scheme);
76 if (scheme == "http") return 80;
77 if (scheme == "https") return 443;
78 }
79 return _port; 95 return _port;
80 } 96 }
81 97
98 // The default port for the scheme of this Uri..
99 static int _defaultPort(String scheme) {
100 if (scheme == "http") return 80;
101 if (scheme == "https") return 443;
102 return 0;
103 }
104
82 /** 105 /**
83 * Returns the path component. 106 * Returns the path component.
84 * 107 *
85 * The returned path is encoded. To get direct access to the decoded 108 * The returned path is encoded. To get direct access to the decoded
86 * path use [pathSegments]. 109 * path use [pathSegments].
87 * 110 *
88 * Returns the empty string if there is no path component. 111 * Returns the empty string if there is no path component.
89 */ 112 */
90 String get path => _path; 113 String get path => _path;
91 114
115 // The query content, or null if there is no query.
116 final String _query;
117
92 /** 118 /**
93 * Returns the query component. The returned query is encoded. To get 119 * Returns the query component. The returned query is encoded. To get
94 * direct access to the decoded query use [queryParameters]. 120 * direct access to the decoded query use [queryParameters].
95 * 121 *
96 * Returns the empty string if there is no query component. 122 * Returns the empty string if there is no query component.
97 */ 123 */
98 final String query; 124 String get query => (_query == null) ? "" : _query;
125
126 // The fragment content, or null if there is no fragment.
127 final String _fragment;
99 128
100 /** 129 /**
101 * Returns the fragment identifier component. 130 * Returns the fragment identifier component.
102 * 131 *
103 * Returns the empty string if there is no fragment identifier 132 * Returns the empty string if there is no fragment identifier
104 * component. 133 * component.
105 */ 134 */
106 final String fragment; 135 String get fragment => (_fragment == null) ? "" : _fragment;
107 136
108 /** 137 /**
109 * Cache the computed return value of [pathSegements]. 138 * Cache the computed return value of [pathSegements].
110 */ 139 */
111 List<String> _pathSegments; 140 List<String> _pathSegments;
112 141
113 /** 142 /**
114 * Cache the computed return value of [queryParameters]. 143 * Cache the computed return value of [queryParameters].
115 */ 144 */
116 Map<String, String> _queryParameters; 145 Map<String, String> _queryParameters;
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
175 // 204 //
176 // query = *( pchar / "/" / "?" ) 205 // query = *( pchar / "/" / "?" )
177 // 206 //
178 // fragment = *( pchar / "/" / "?" ) 207 // fragment = *( pchar / "/" / "?" )
179 bool isRegName(int ch) { 208 bool isRegName(int ch) {
180 return ch < 128 && ((_regNameTable[ch >> 4] & (1 << (ch & 0x0f))) != 0); 209 return ch < 128 && ((_regNameTable[ch >> 4] & (1 << (ch & 0x0f))) != 0);
181 } 210 }
182 const int EOI = -1; 211 const int EOI = -1;
183 212
184 String scheme = ""; 213 String scheme = "";
185 String path;
186 String userinfo = ""; 214 String userinfo = "";
187 String host = ""; 215 String host = null;
188 int port = 0; 216 int port = null;
189 String query = ""; 217 String path = null;
190 String fragment = ""; 218 String query = null;
219 String fragment = null;
191 220
192 int index = 0; 221 int index = 0;
193 int pathStart = 0; 222 int pathStart = 0;
194 // End of input-marker. 223 // End of input-marker.
195 int char = EOI; 224 int char = EOI;
196 225
197 void parseAuth() { 226 void parseAuth() {
198 if (index == uri.length) { 227 if (index == uri.length) {
199 char = EOI; 228 char = EOI;
200 return; 229 return;
(...skipping 26 matching lines...) Expand all
227 index++; 256 index++;
228 char = EOI; 257 char = EOI;
229 } 258 }
230 int hostStart = authStart; 259 int hostStart = authStart;
231 int hostEnd = index; 260 int hostEnd = index;
232 if (lastAt >= 0) { 261 if (lastAt >= 0) {
233 userinfo = _makeUserInfo(uri, authStart, lastAt); 262 userinfo = _makeUserInfo(uri, authStart, lastAt);
234 hostStart = lastAt + 1; 263 hostStart = lastAt + 1;
235 } 264 }
236 if (lastColon >= 0) { 265 if (lastColon >= 0) {
237 int portNumber = 0; 266 int portNumber;
238 for (int i = lastColon + 1; i < index; i++) { 267 if (lastColon + 1 < index) {
239 int digit = uri.codeUnitAt(i); 268 portNumber = 0;
240 if (_ZERO > digit || _NINE < digit) { 269 for (int i = lastColon + 1; i < index; i++) {
241 _fail(uri, i, "Invalid port number"); 270 int digit = uri.codeUnitAt(i);
271 if (_ZERO > digit || _NINE < digit) {
272 _fail(uri, i, "Invalid port number");
273 }
274 portNumber = portNumber * 10 + (digit - _ZERO);
242 } 275 }
243 portNumber = portNumber * 10 + (digit - _ZERO);
244 } 276 }
245 port = _makePort(portNumber, scheme); 277 port = _makePort(portNumber, scheme);
246 hostEnd = lastColon; 278 hostEnd = lastColon;
247 } 279 }
248 host = _makeHost(uri, hostStart, hostEnd, true); 280 host = _makeHost(uri, hostStart, hostEnd, true);
249 if (index < uri.length) { 281 if (index < uri.length) {
250 char = uri.codeUnitAt(index); 282 char = uri.codeUnitAt(index);
251 } 283 }
252 } 284 }
253 285
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
333 char = uri.codeUnitAt(index); 365 char = uri.codeUnitAt(index);
334 if (char == _QUESTION || char == _NUMBER_SIGN) { 366 if (char == _QUESTION || char == _NUMBER_SIGN) {
335 break; 367 break;
336 } 368 }
337 char = EOI; 369 char = EOI;
338 } 370 }
339 state = NOT_IN_PATH; 371 state = NOT_IN_PATH;
340 } 372 }
341 373
342 assert(state == NOT_IN_PATH); 374 assert(state == NOT_IN_PATH);
343 bool ensureLeadingSlash = (host != "" || scheme == "file"); 375 bool ensureLeadingSlash = (host != null || scheme == "file");
344 path = _makePath(uri, pathStart, index, null, ensureLeadingSlash); 376 path = _makePath(uri, pathStart, index, null, ensureLeadingSlash);
345 377
346 if (char == _QUESTION) { 378 if (char == _QUESTION) {
347 int numberSignIndex = uri.indexOf('#', index + 1); 379 int numberSignIndex = uri.indexOf('#', index + 1);
348 if (numberSignIndex < 0) { 380 if (numberSignIndex < 0) {
349 query = _makeQuery(uri, index + 1, uri.length, null); 381 query = _makeQuery(uri, index + 1, uri.length, null);
350 } else { 382 } else {
351 query = _makeQuery(uri, index + 1, numberSignIndex, null); 383 query = _makeQuery(uri, index + 1, numberSignIndex, null);
352 fragment = _makeFragment(uri, numberSignIndex + 1, uri.length); 384 fragment = _makeFragment(uri, numberSignIndex + 1, uri.length);
353 } 385 }
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
390 } 422 }
391 // Combine message, slice and a caret pointing to the error index. 423 // Combine message, slice and a caret pointing to the error index.
392 message = "$message$pre${uri.substring(min, max)}$post\n" 424 message = "$message$pre${uri.substring(min, max)}$post\n"
393 "${' ' * (pre.length + index - min)}^"; 425 "${' ' * (pre.length + index - min)}^";
394 } 426 }
395 throw new FormatException(message); 427 throw new FormatException(message);
396 } 428 }
397 429
398 /// Internal non-verifying constructor. Only call with validated arguments. 430 /// Internal non-verifying constructor. Only call with validated arguments.
399 Uri._internal(this.scheme, 431 Uri._internal(this.scheme,
400 this.userInfo, 432 this._userInfo,
401 this._host, 433 this._host,
402 this._port, 434 this._port,
403 this._path, 435 this._path,
404 this.query, 436 this._query,
405 this.fragment); 437 this._fragment);
406 438
407 /** 439 /**
408 * Creates a new URI from its components. 440 * Creates a new URI from its components.
409 * 441 *
410 * Each component is set through a named argument. Any number of 442 * Each component is set through a named argument. Any number of
411 * components can be provided. The default value for the components 443 * components can be provided. The [path] and [query] components can be set
412 * not provided is the empry string, except for [port] which has a 444 * using either of two different named arguments.
413 * default value of 0. The [path] and [query] components can be set
414 * using two different named arguments.
415 * 445 *
416 * The scheme component is set through [scheme]. The scheme is 446 * The scheme component is set through [scheme]. The scheme is
417 * normalized to all lowercase letters. 447 * normalized to all lowercase letters. If the scheme is omitted or empty,
448 * the URI will not have a scheme part.
Anders Johnsen 2014/06/25 09:18:00 will have an empty scheme part?
Lasse Reichstein Nielsen 2014/06/25 10:49:18 That sounds like an empty string before a colon, w
418 * 449 *
419 * The user info part of the authority component is set through 450 * The user info part of the authority component is set through
420 * [userInfo]. 451 * [userInfo]. It defaults to the empty string, which will be omitted
452 * from the string representation of the URI.
421 * 453 *
422 * The host part of the authority component is set through 454 * The host part of the authority component is set through
423 * [host]. The host can either be a hostname, an IPv4 address or an 455 * [host]. The host can either be a hostname, an IPv4 address or an
424 * IPv6 address, contained in '[' and ']'. If the host contains a 456 * IPv6 address, contained in '[' and ']'. If the host contains a
425 * ':' character, the '[' and ']' are added if not already provided. 457 * ':' character, the '[' and ']' are added if not already provided.
426 * The host is normalized to all lowercase letters. 458 * The host is normalized to all lowercase letters.
427 * 459 *
428 * The port part of the authority component is set through 460 * The port part of the authority component is set through
429 * [port]. The port is normalized for scheme http and https where 461 * [port].
430 * port 80 and port 443 respectively is set. 462 * If [port] is omitted or `null`, it implies the default port for
463 * the URI's scheme, and is equivalent to passing that port explicitly.
464 * The recognized schemes, and their default ports, are "http" (80) and
465 * "https" (443). All other schemes are considered as having zero as the
466 * default port.
467 *
468 * If any of `userInfo`, `host` or `port` are provided,
469 * the URI will have an autority according to [hasAuthority].
431 * 470 *
432 * The path component is set through either [path] or 471 * The path component is set through either [path] or
433 * [pathSegments]. When [path] is used, the provided string is 472 * [pathSegments]. When [path] is used, it should be a valid URI path,
434 * expected to be fully percent-encoded, and is used in its literal 473 * but invalid characters, except the general delimiters ':/@[]?#',
435 * form. When [pathSegments] is used, each of the provided segments 474 * will be escaped if necessary.
436 * is percent-encoded and joined using the forward slash 475 * When [pathSegments] is used, each of the provided segments
476 * is first percent-encoded and then joined using the forward slash
437 * separator. The percent-encoding of the path segments encodes all 477 * separator. The percent-encoding of the path segments encodes all
438 * characters except for the unreserved characters and the following 478 * characters except for the unreserved characters and the following
439 * list of characters: `!$&'()*+,;=:@`. If the other components 479 * list of characters: `!$&'()*+,;=:@`. If the other components
440 * calls for an absolute path a leading slash `/` is prepended if 480 * calls for an absolute path a leading slash `/` is prepended if
441 * not already there. 481 * not already there.
442 * 482 *
443 * The query component is set through either [query] or 483 * The query component is set through either [query] or
444 * [queryParameters]. When [query] is used the provided string is 484 * [queryParameters]. When [query] is used the provided string should
445 * expected to be fully percent-encoded and is used in its literal 485 * be a valid URI query, but invalid characters other than general delimiters,
446 * form. When [queryParameters] is used the query is built from the 486 * will be escaped if necessary.
487 * When [queryParameters] is used the query is built from the
447 * provided map. Each key and value in the map is percent-encoded 488 * provided map. Each key and value in the map is percent-encoded
448 * and joined using equal and ampersand characters. The 489 * and joined using equal and ampersand characters. The
449 * percent-encoding of the keys and values encodes all characters 490 * percent-encoding of the keys and values encodes all characters
450 * except for the unreserved characters. 491 * except for the unreserved characters.
492 * If both `query` and `queryParameters` are omitted or `null`, the
493 * URI will have no query part.
451 * 494 *
452 * The fragment component is set through [fragment]. 495 * The fragment component is set through [fragment].
496 * It should be a valid URI fragment, but invalid characters other than
497 * general delimiters, will be escaped if necessary.
498 * If `fragment` is omitted or `null`, the URI will have no fragment part.
453 */ 499 */
454 factory Uri({String scheme, 500 factory Uri({String scheme : "",
455 String userInfo: "", 501 String userInfo : "",
456 String host: "", 502 String host,
457 port: 0, 503 port,
Anders Johnsen 2014/06/25 09:18:00 int port
Lasse Reichstein Nielsen 2014/06/25 10:49:18 Done.
458 String path, 504 String path,
459 Iterable<String> pathSegments, 505 Iterable<String> pathSegments,
460 String query, 506 String query,
461 Map<String, String> queryParameters, 507 Map<String, String> queryParameters,
462 fragment: ""}) { 508 fragment}) {
463 scheme = _makeScheme(scheme, _stringOrNullLength(scheme)); 509 scheme = _makeScheme(scheme, _stringOrNullLength(scheme));
464 userInfo = _makeUserInfo(userInfo, 0, _stringOrNullLength(userInfo)); 510 userInfo = _makeUserInfo(userInfo, 0, _stringOrNullLength(userInfo));
465 host = _makeHost(host, 0, _stringOrNullLength(host), false); 511 host = _makeHost(host, 0, _stringOrNullLength(host), false);
466 query = _makeQuery(query, 0, _stringOrNullLength(query), queryParameters); 512 query = _makeQuery(query, 0, _stringOrNullLength(query), queryParameters);
467 fragment = _makeFragment(fragment, 0, _stringOrNullLength(fragment)); 513 fragment = _makeFragment(fragment, 0, _stringOrNullLength(fragment));
468 port = _makePort(port, scheme); 514 port = _makePort(port, scheme);
469 bool ensureLeadingSlash = (host != "" || scheme == "file"); 515 bool isFile = (scheme == "file");
516 if (host == null &&
517 (userInfo.isNotEmpty || port != null || isFile)) {
518 host = "";
519 }
520 bool ensureLeadingSlash = (host != null || isFile);
470 path = _makePath(path, 0, _stringOrNullLength(path), pathSegments, 521 path = _makePath(path, 0, _stringOrNullLength(path), pathSegments,
471 ensureLeadingSlash); 522 ensureLeadingSlash);
472 523
473 return new Uri._internal(scheme, userInfo, host, port, 524 return new Uri._internal(scheme, userInfo, host, port,
474 path, query, fragment); 525 path, query, fragment);
475 } 526 }
476 527
477 /** 528 /**
478 * Creates a new `http` URI from authority, path and query. 529 * Creates a new `http` URI from authority, path and query.
479 * 530 *
480 * Examples: 531 * Examples:
481 * 532 *
482 * ``` 533 * ```
483 * // http://example.org/path?q=dart. 534 * // http://example.org/path?q=dart.
484 * new Uri.http("google.com", "/search", { "q" : "dart" }); 535 * new Uri.http("google.com", "/search", { "q" : "dart" });
485 * 536 *
486 * // http://user:pass@localhost:8080 537 * // http://user:pass@localhost:8080
487 * new Uri.http("user:pass@localhost:8080", ""); 538 * new Uri.http("user:pass@localhost:8080", "");
488 * 539 *
489 * // http://example.org/a%20b 540 * // http://example.org/a%20b
490 * new Uri.http("example.org", "a b"); 541 * new Uri.http("example.org", "a b");
491 * 542 *
492 * // http://example.org/a%252F 543 * // http://example.org/a%252F
493 * new Uri.http("example.org", "/a%2F"); 544 * new Uri.http("example.org", "/a%2F");
494 * ``` 545 * ```
495 * 546 *
496 * The `scheme` is always set to `http`. 547 * The `scheme` is always set to `http`.
497 * 548 *
498 * The `userInfo`, `host` and `port` components are set from the 549 * The `userInfo`, `host` and `port` components are set from the
499 * [authority] argument. 550 * [authority] argument. If `authority` is `null` or empty,
551 * the created `Uri` will have no authority, and will not be directly usable
552 * as an HTTP URL, which must have a non-empty host.
500 * 553 *
501 * The `path` component is set from the [unencodedPath] 554 * The `path` component is set from the [unencodedPath]
502 * argument. The path passed must not be encoded as this constructor 555 * argument. The path passed must not be encoded as this constructor
503 * encodes the path. 556 * encodes the path.
504 * 557 *
505 * The `query` component is set from the optional [queryParameters] 558 * The `query` component is set from the optional [queryParameters]
506 * argument. 559 * argument.
507 */ 560 */
508 factory Uri.http(String authority, 561 factory Uri.http(String authority,
509 String unencodedPath, 562 String unencodedPath,
(...skipping 10 matching lines...) Expand all
520 factory Uri.https(String authority, 573 factory Uri.https(String authority,
521 String unencodedPath, 574 String unencodedPath,
522 [Map<String, String> queryParameters]) { 575 [Map<String, String> queryParameters]) {
523 return _makeHttpUri("https", authority, unencodedPath, queryParameters); 576 return _makeHttpUri("https", authority, unencodedPath, queryParameters);
524 } 577 }
525 578
526 static Uri _makeHttpUri(String scheme, 579 static Uri _makeHttpUri(String scheme,
527 String authority, 580 String authority,
528 String unencodedPath, 581 String unencodedPath,
529 Map<String, String> queryParameters) { 582 Map<String, String> queryParameters) {
530 var userInfo = ""; 583 var userInfo = null;
531 var host = ""; 584 var host = null;
532 var port = 0; 585 var port = null;
533 586
534 var hostStart = 0; 587 if (authority != null && authority.isNotEmpty) {
535 // Split off the user info. 588 var hostStart = 0;
536 bool hasUserInfo = false; 589 // Split off the user info.
537 for (int i = 0; i < authority.length; i++) { 590 bool hasUserInfo = false;
538 if (authority.codeUnitAt(i) == _AT_SIGN) { 591 for (int i = 0; i < authority.length; i++) {
539 hasUserInfo = true; 592 if (authority.codeUnitAt(i) == _AT_SIGN) {
540 userInfo = authority.substring(0, i); 593 hasUserInfo = true;
541 hostStart = i + 1; 594 userInfo = authority.substring(0, i);
542 break; 595 hostStart = i + 1;
596 break;
597 }
543 } 598 }
599 var hostEnd = hostStart;
600 if (hostStart < authority.length &&
601 authority.codeUnitAt(hostStart) == _LEFT_BRACKET) {
602 // IPv6 host.
603 for (; hostEnd < authority.length; hostEnd++) {
604 if (authority.codeUnitAt(hostEnd) == _RIGHT_BRACKET) break;
605 }
606 if (hostEnd == authority.length) {
607 throw new FormatException("Invalid IPv6 host entry.");
608 }
609 parseIPv6Address(authority, hostStart + 1, hostEnd);
610 hostEnd++; // Skip the closing bracket.
611 if (hostEnd != authority.length &&
612 authority.codeUnitAt(hostEnd) != _COLON) {
613 throw new FormatException("Invalid end of authority");
614 }
615 }
616 // Split host and port.
617 bool hasPort = false;
618 for (; hostEnd < authority.length; hostEnd++) {
619 if (authority.codeUnitAt(hostEnd) == _COLON) {
620 var portString = authority.substring(hostEnd + 1);
621 // We allow the empty port - falling back to initial value.
622 if (portString.isNotEmpty) port = int.parse(portString);
623 break;
624 }
625 }
626 host = authority.substring(hostStart, hostEnd);
544 } 627 }
545 var hostEnd = hostStart;
546 if (hostStart < authority.length &&
547 authority.codeUnitAt(hostStart) == _LEFT_BRACKET) {
548 // IPv6 host.
549 for (; hostEnd < authority.length; hostEnd++) {
550 if (authority.codeUnitAt(hostEnd) == _RIGHT_BRACKET) break;
551 }
552 if (hostEnd == authority.length) {
553 throw new FormatException("Invalid IPv6 host entry.");
554 }
555 parseIPv6Address(authority, hostStart + 1, hostEnd);
556 hostEnd++; // Skip the closing bracket.
557 if (hostEnd != authority.length &&
558 authority.codeUnitAt(hostEnd) != _COLON) {
559 throw new FormatException("Invalid end of authority");
560 }
561 }
562 // Split host and port.
563 bool hasPort = false;
564 for (; hostEnd < authority.length; hostEnd++) {
565 if (authority.codeUnitAt(hostEnd) == _COLON) {
566 var portString = authority.substring(hostEnd + 1);
567 // We allow the empty port - falling back to initial value.
568 if (portString.isNotEmpty) port = int.parse(portString);
569 break;
570 }
571 }
572 host = authority.substring(hostStart, hostEnd);
573
574 return new Uri(scheme: scheme, 628 return new Uri(scheme: scheme,
575 userInfo: userInfo, 629 userInfo: userInfo,
576 host: host, 630 host: host,
577 port: port, 631 port: port,
578 pathSegments: unencodedPath.split("/"), 632 pathSegments: unencodedPath.split("/"),
579 queryParameters: queryParameters); 633 queryParameters: queryParameters);
580 } 634 }
581 635
582 /** 636 /**
583 * Creates a new file URI from an absolute or relative file path. 637 * Creates a new file URI from an absolute or relative file path.
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after
714 throw new ArgumentError("Illegal drive letter " + 768 throw new ArgumentError("Illegal drive letter " +
715 new String.fromCharCode(charCode)); 769 new String.fromCharCode(charCode));
716 } else { 770 } else {
717 throw new UnsupportedError("Illegal drive letter " + 771 throw new UnsupportedError("Illegal drive letter " +
718 new String.fromCharCode(charCode)); 772 new String.fromCharCode(charCode));
719 } 773 }
720 } 774 }
721 775
722 static _makeFileUri(String path) { 776 static _makeFileUri(String path) {
723 String sep = "/"; 777 String sep = "/";
724 if (path.length > 0 && path[0] == sep) { 778 if (path.startsWith(sep)) {
725 // Absolute file:// URI. 779 // Absolute file:// URI.
726 return new Uri(scheme: "file", pathSegments: path.split(sep)); 780 return new Uri(scheme: "file", pathSegments: path.split(sep));
727 } else { 781 } else {
728 // Relative URI. 782 // Relative URI.
729 return new Uri(pathSegments: path.split(sep)); 783 return new Uri(pathSegments: path.split(sep));
730 } 784 }
731 } 785 }
732 786
733 static _makeWindowsFileUrl(String path) { 787 static _makeWindowsFileUrl(String path) {
734 if (path.startsWith("\\\\?\\")) { 788 if (path.startsWith("\\\\?\\")) {
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
823 */ 877 */
824 Map<String, String> get queryParameters { 878 Map<String, String> get queryParameters {
825 if (_queryParameters == null) { 879 if (_queryParameters == null) {
826 _queryParameters = new UnmodifiableMapView(splitQueryString(query)); 880 _queryParameters = new UnmodifiableMapView(splitQueryString(query));
827 } 881 }
828 return _queryParameters; 882 return _queryParameters;
829 } 883 }
830 884
831 static int _makePort(int port, String scheme) { 885 static int _makePort(int port, String scheme) {
832 // Perform scheme specific normalization. 886 // Perform scheme specific normalization.
833 if (port == 80 && scheme == "http") { 887 if (port != null && port == _defaultPort(scheme)) return null;
834 return 0;
835 }
836 if (port == 443 && scheme == "https") {
837 return 0;
838 }
839 return port; 888 return port;
840 } 889 }
841 890
842 /** 891 /**
843 * Check and normalize a most name. 892 * Check and normalize a most name.
844 * 893 *
845 * If the host name starts and ends with '[' and ']', it is considered an 894 * If the host name starts and ends with '[' and ']', it is considered an
846 * IPv6 address. If [strictIPv6] is false, the address is also considered 895 * IPv6 address. If [strictIPv6] is false, the address is also considered
847 * an IPv6 address if it contains any ':' character. 896 * an IPv6 address if it contains any ':' character.
848 * 897 *
849 * If it is not an IPv6 address, it is case- and escape-normalized. 898 * If it is not an IPv6 address, it is case- and escape-normalized.
850 * This escapes all characters not valid in a reg-name, 899 * This escapes all characters not valid in a reg-name,
851 * and converts all non-escape upper-case letters to lower-case. 900 * and converts all non-escape upper-case letters to lower-case.
852 */ 901 */
853 static String _makeHost(String host, int start, int end, bool strictIPv6) { 902 static String _makeHost(String host, int start, int end, bool strictIPv6) {
854 // TODO(lrn): Should we normalize IPv6 addresses according to RFC 5952? 903 // TODO(lrn): Should we normalize IPv6 addresses according to RFC 5952?
855
856 if (host == null) return null; 904 if (host == null) return null;
857 if (start == end) return ""; 905 if (start == end) return "";
858 // Host is an IPv6 address if it starts with '[' or contains a colon. 906 // Host is an IPv6 address if it starts with '[' or contains a colon.
859 if (host.codeUnitAt(start) == _LEFT_BRACKET) { 907 if (host.codeUnitAt(start) == _LEFT_BRACKET) {
860 if (host.codeUnitAt(end - 1) != _RIGHT_BRACKET) { 908 if (host.codeUnitAt(end - 1) != _RIGHT_BRACKET) {
861 _fail(host, start, 'Missing end `]` to match `[` in host'); 909 _fail(host, start, 'Missing end `]` to match `[` in host');
862 } 910 }
863 parseIPv6Address(host, start + 1, end - 1); 911 parseIPv6Address(host, start + 1, end - 1);
864 // RFC 5952 requires hex digits to be lower case. 912 // RFC 5952 requires hex digits to be lower case.
865 return host.substring(start, end).toLowerCase(); 913 return host.substring(start, end).toLowerCase();
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after
978 if (_LOWER_CASE_A <= char && _LOWER_CASE_Z >= char) { 1026 if (_LOWER_CASE_A <= char && _LOWER_CASE_Z >= char) {
979 allLowercase = false; 1027 allLowercase = false;
980 } 1028 }
981 } 1029 }
982 scheme = scheme.substring(0, end); 1030 scheme = scheme.substring(0, end);
983 if (!allLowercase) scheme = scheme.toLowerCase(); 1031 if (!allLowercase) scheme = scheme.toLowerCase();
984 return scheme; 1032 return scheme;
985 } 1033 }
986 1034
987 static String _makeUserInfo(String userInfo, int start, int end) { 1035 static String _makeUserInfo(String userInfo, int start, int end) {
988 if (userInfo == null) return "null"; 1036 if (userInfo == null) return "";
989 return _normalize(userInfo, start, end, _userinfoTable); 1037 return _normalize(userInfo, start, end, _userinfoTable);
990 } 1038 }
991 1039
992 static String _makePath(String path, int start, int end, 1040 static String _makePath(String path, int start, int end,
993 Iterable<String> pathSegments, 1041 Iterable<String> pathSegments,
994 bool ensureLeadingSlash) { 1042 bool ensureLeadingSlash) {
995 if (path == null && pathSegments == null) return ""; 1043 if (path == null && pathSegments == null) return "";
996 if (path != null && pathSegments != null) { 1044 if (path != null && pathSegments != null) {
997 throw new ArgumentError('Both path and pathSegments specified'); 1045 throw new ArgumentError('Both path and pathSegments specified');
998 } 1046 }
999 var result; 1047 var result;
1000 if (path != null) { 1048 if (path != null) {
1001 result = _normalize(path, start, end, _pathCharOrSlashTable); 1049 result = _normalize(path, start, end, _pathCharOrSlashTable);
1002 } else { 1050 } else {
1003 result = pathSegments.map((s) => _uriEncode(_pathCharTable, s)).join("/"); 1051 result = pathSegments.map((s) => _uriEncode(_pathCharTable, s)).join("/");
1004 } 1052 }
1005 if (ensureLeadingSlash && result.isNotEmpty && !result.startsWith("/")) { 1053 if (ensureLeadingSlash && result.isNotEmpty && !result.startsWith("/")) {
1006 return "/$result"; 1054 return "/$result";
1007 } 1055 }
1008 return result; 1056 return result;
1009 } 1057 }
1010 1058
1011 static String _makeQuery(String query, int start, int end, 1059 static String _makeQuery(String query, int start, int end,
1012 Map<String, String> queryParameters) { 1060 Map<String, String> queryParameters) {
1013 if (query == null && queryParameters == null) return ""; 1061 if (query == null && queryParameters == null) return null;
1014 if (query != null && queryParameters != null) { 1062 if (query != null && queryParameters != null) {
1015 throw new ArgumentError('Both query and queryParameters specified'); 1063 throw new ArgumentError('Both query and queryParameters specified');
1016 } 1064 }
1017 if (query != null) return _normalize(query, start, end, _queryCharTable); 1065 if (query != null) return _normalize(query, start, end, _queryCharTable);
1018 1066
1019 var result = new StringBuffer(); 1067 var result = new StringBuffer();
1020 var first = true; 1068 var first = true;
1021 queryParameters.forEach((key, value) { 1069 queryParameters.forEach((key, value) {
1022 if (!first) { 1070 if (!first) {
1023 result.write("&"); 1071 result.write("&");
1024 } 1072 }
1025 first = false; 1073 first = false;
1026 result.write(Uri.encodeQueryComponent(key)); 1074 result.write(Uri.encodeQueryComponent(key));
1027 if (value != null && !value.isEmpty) { 1075 if (value != null && !value.isEmpty) {
1028 result.write("="); 1076 result.write("=");
1029 result.write(Uri.encodeQueryComponent(value)); 1077 result.write(Uri.encodeQueryComponent(value));
1030 } 1078 }
1031 }); 1079 });
1032 return result.toString(); 1080 return result.toString();
1033 } 1081 }
1034 1082
1035 static String _makeFragment(String fragment, int start, int end) { 1083 static String _makeFragment(String fragment, int start, int end) {
1036 if (fragment == null) return ""; 1084 if (fragment == null) return null;
1037 return _normalize(fragment, start, end, _queryCharTable); 1085 return _normalize(fragment, start, end, _queryCharTable);
1038 } 1086 }
1039 1087
1040 static int _stringOrNullLength(String s) => (s == null) ? 0 : s.length; 1088 static int _stringOrNullLength(String s) => (s == null) ? 0 : s.length;
1041 1089
1042 static bool _isHexDigit(int char) { 1090 static bool _isHexDigit(int char) {
1043 if (_NINE >= char) return _ZERO <= char; 1091 if (_NINE >= char) return _ZERO <= char;
1044 char |= 0x20; 1092 char |= 0x20;
1045 return _LOWER_CASE_A <= char && _LOWER_CASE_F >= char; 1093 return _LOWER_CASE_A <= char && _LOWER_CASE_F >= char;
1046 } 1094 }
(...skipping 224 matching lines...) Expand 10 before | Expand all | Expand 10 after
1271 * (http://tools.ietf.org/html/rfc3986#section-5 "RFC-1123"). 1319 * (http://tools.ietf.org/html/rfc3986#section-5 "RFC-1123").
1272 */ 1320 */
1273 Uri resolveUri(Uri reference) { 1321 Uri resolveUri(Uri reference) {
1274 // From RFC 3986. 1322 // From RFC 3986.
1275 String targetScheme; 1323 String targetScheme;
1276 String targetUserInfo; 1324 String targetUserInfo;
1277 String targetHost; 1325 String targetHost;
1278 int targetPort; 1326 int targetPort;
1279 String targetPath; 1327 String targetPath;
1280 String targetQuery; 1328 String targetQuery;
1281 if (reference.scheme != "") { 1329 if (reference.scheme.isNotEmpty) {
1282 targetScheme = reference.scheme; 1330 targetScheme = reference.scheme;
1283 targetUserInfo = reference.userInfo;
1284 targetHost = reference.host;
1285 targetPort = reference.port;
1286 targetPath = _removeDotSegments(reference.path);
1287 targetQuery = reference.query;
1288 } else {
1289 if (reference.hasAuthority) { 1331 if (reference.hasAuthority) {
1290 targetUserInfo = reference.userInfo; 1332 targetUserInfo = reference.userInfo;
1291 targetHost = reference.host; 1333 targetHost = reference.host;
1292 targetPort = reference.port; 1334 targetPort = reference.hasPort ? reference.port : null;
1335 }
1336 targetPath = _removeDotSegments(reference.path);
1337 if (reference.hasQuery) {
1338 targetQuery = reference.query;
1339 }
1340 } else {
1341 targetScheme = this.scheme;
1342 if (reference.hasAuthority) {
1343 targetUserInfo = reference.userInfo;
1344 targetHost = reference.host;
1345 targetPort = _makePort(reference.hasPort ? reference.port : null,
1346 targetScheme);
1293 targetPath = _removeDotSegments(reference.path); 1347 targetPath = _removeDotSegments(reference.path);
1294 targetQuery = reference.query; 1348 if (reference.hasQuery) targetQuery = reference.query;
1295 } else { 1349 } else {
1296 if (reference.path == "") { 1350 if (reference.path == "") {
1297 targetPath = this.path; 1351 targetPath = this._path;
1298 if (reference.query != "") { 1352 if (reference.hasQuery) {
1299 targetQuery = reference.query; 1353 targetQuery = reference.query;
1300 } else { 1354 } else {
1301 targetQuery = this.query; 1355 targetQuery = this._query;
1302 } 1356 }
1303 } else { 1357 } else {
1304 if (reference.path.startsWith("/")) { 1358 if (reference.path.startsWith("/")) {
1305 targetPath = _removeDotSegments(reference.path); 1359 targetPath = _removeDotSegments(reference.path);
1306 } else { 1360 } else {
1307 targetPath = _removeDotSegments(_merge(this.path, reference.path)); 1361 targetPath = _removeDotSegments(_merge(this._path, reference.path));
1308 } 1362 }
1309 targetQuery = reference.query; 1363 if (reference.hasQuery) targetQuery = reference.query;
1310 } 1364 }
1311 targetUserInfo = this.userInfo; 1365 targetUserInfo = this._userInfo;
1312 targetHost = this.host; 1366 targetHost = this._host;
1313 targetPort = this.port; 1367 targetPort = this._port;
1314 } 1368 }
1315 targetScheme = this.scheme;
1316 } 1369 }
1317 return new Uri(scheme: targetScheme, 1370 String fragment = reference.hasFragment ? reference.fragment : null;
1318 userInfo: targetUserInfo, 1371 return new Uri._internal(targetScheme,
1319 host: targetHost, 1372 targetUserInfo,
1320 port: targetPort, 1373 targetHost,
1321 path: targetPath, 1374 targetPort,
1322 query: targetQuery, 1375 targetPath,
1323 fragment: reference.fragment); 1376 targetQuery,
1377 fragment);
1324 } 1378 }
1325 1379
1326 /** 1380 /**
1327 * Returns whether the URI has an [authority] component. 1381 * Returns whether the URI has an [authority] component.
1328 */ 1382 */
1329 bool get hasAuthority => host != ""; 1383 bool get hasAuthority => _host != null;
1384
1385 /**
1386 * Returns whether the URI has an explicit port.
1387 *
1388 * If the port number is the default port number
1389 * (zero for unrecognized schemes, with http (80) and https (443) being
1390 * recognized),
1391 * then the port is made implicit and omitted from the URI.
1392 */
1393 bool get hasPort => _port != null;
1394
1395 /**
1396 * Returns whether the URI has a query part.
1397 */
1398 bool get hasQuery => _query != null;
1399
1400 /**
1401 * Returns whether the URI has a fragment part.
1402 */
1403 bool get hasFragment => _fragment != null;
1330 1404
1331 /** 1405 /**
1332 * Returns the origin of the URI in the form scheme://host:port for the 1406 * Returns the origin of the URI in the form scheme://host:port for the
1333 * schemes http and https. 1407 * schemes http and https.
1334 * 1408 *
1335 * It is an error if the scheme is not "http" or "https". 1409 * It is an error if the scheme is not "http" or "https".
1336 * 1410 *
1337 * See: http://www.w3.org/TR/2011/WD-html5-20110405/origin-0.html#origin 1411 * See: http://www.w3.org/TR/2011/WD-html5-20110405/origin-0.html#origin
1338 */ 1412 */
1339 String get origin { 1413 String get origin {
1340 if (scheme == "" || _host == null || _host == "") { 1414 if (scheme == "" || _host == null || _host == "") {
1341 throw new StateError("Cannot use origin without a scheme: $this"); 1415 throw new StateError("Cannot use origin without a scheme: $this");
1342 } 1416 }
1343 if (scheme != "http" && scheme != "https") { 1417 if (scheme != "http" && scheme != "https") {
1344 throw new StateError( 1418 throw new StateError(
1345 "Origin is only applicable schemes http and https: $this"); 1419 "Origin is only applicable schemes http and https: $this");
1346 } 1420 }
1347 if (_port == 0) return "$scheme://$_host"; 1421 if (_port == null) return "$scheme://$_host";
Anders Johnsen 2014/06/25 09:18:00 Maybe use the new getters?
Lasse Reichstein Nielsen 2014/06/25 10:49:18 hasPort? I *try* not to use public overridable m
1348 return "$scheme://$_host:$_port"; 1422 return "$scheme://$_host:$_port";
1349 } 1423 }
1350 1424
1351 /** 1425 /**
1352 * Returns the file path from a file URI. 1426 * Returns the file path from a file URI.
1353 * 1427 *
1354 * The returned path has either Windows or non-Windows 1428 * The returned path has either Windows or non-Windows
1355 * semantics. 1429 * semantics.
1356 * 1430 *
1357 * For non-Windows semantics the slash ("/") is used to separate 1431 * For non-Windows semantics the slash ("/") is used to separate
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after
1464 if (hasDriveLetter && segments.length == 1) result.write("\\"); 1538 if (hasDriveLetter && segments.length == 1) result.write("\\");
1465 return result.toString(); 1539 return result.toString();
1466 } 1540 }
1467 1541
1468 bool get _isPathAbsolute { 1542 bool get _isPathAbsolute {
1469 if (path == null || path.isEmpty) return false; 1543 if (path == null || path.isEmpty) return false;
1470 return path.startsWith('/'); 1544 return path.startsWith('/');
1471 } 1545 }
1472 1546
1473 void _writeAuthority(StringSink ss) { 1547 void _writeAuthority(StringSink ss) {
1474 _addIfNonEmpty(ss, userInfo, userInfo, "@"); 1548 if (_userInfo.isNotEmpty) {
1475 ss.write(_host == null ? "null" : _host); 1549 ss.write(_userInfo);
1476 if (_port != 0) { 1550 ss.write("@");
1551 }
1552 if (_host != null) ss.write(_host);
1553 if (_port != null) {
1477 ss.write(":"); 1554 ss.write(":");
1478 ss.write(_port.toString()); 1555 ss.write(_port);
1479 } 1556 }
1480 } 1557 }
1481 1558
1482 String toString() { 1559 String toString() {
1483 StringBuffer sb = new StringBuffer(); 1560 StringBuffer sb = new StringBuffer();
1484 _addIfNonEmpty(sb, scheme, scheme, ':'); 1561 _addIfNonEmpty(sb, scheme, scheme, ':');
1485 if (hasAuthority || path.startsWith("//") || (scheme == "file")) { 1562 if (hasAuthority || path.startsWith("//") || (scheme == "file")) {
1486 // File URIS always have the authority, even if it is empty. 1563 // File URIS always have the authority, even if it is empty.
1487 // The empty URI means "localhost". 1564 // The empty URI means "localhost".
1488 sb.write("//"); 1565 sb.write("//");
1489 _writeAuthority(sb); 1566 _writeAuthority(sb);
1490 } 1567 }
1491 sb.write(path); 1568 sb.write(path);
1492 _addIfNonEmpty(sb, query, "?", query); 1569 if (_query != null) { sb..write("?")..write(_query); }
1493 _addIfNonEmpty(sb, fragment, "#", fragment); 1570 if (_fragment != null) { sb..write("#")..write(_fragment); }
1494 return sb.toString(); 1571 return sb.toString();
1495 } 1572 }
1496 1573
1497 bool operator==(other) { 1574 bool operator==(other) {
1498 if (other is! Uri) return false; 1575 if (other is! Uri) return false;
1499 Uri uri = other; 1576 Uri uri = other;
1500 return scheme == uri.scheme && 1577 return scheme == uri.scheme &&
1501 userInfo == uri.userInfo && 1578 userInfo == uri.userInfo &&
1502 host == uri.host && 1579 host == uri.host &&
1503 port == uri.port && 1580 port == uri.port &&
(...skipping 691 matching lines...) Expand 10 before | Expand all | Expand 10 after
2195 0xafff, // 0x30 - 0x3f 1111111111110101 2272 0xafff, // 0x30 - 0x3f 1111111111110101
2196 // @ABCDEFGHIJKLMNO 2273 // @ABCDEFGHIJKLMNO
2197 0xffff, // 0x40 - 0x4f 1111111111111111 2274 0xffff, // 0x40 - 0x4f 1111111111111111
2198 // PQRSTUVWXYZ _ 2275 // PQRSTUVWXYZ _
2199 0x87ff, // 0x50 - 0x5f 1111111111100001 2276 0x87ff, // 0x50 - 0x5f 1111111111100001
2200 // abcdefghijklmno 2277 // abcdefghijklmno
2201 0xfffe, // 0x60 - 0x6f 0111111111111111 2278 0xfffe, // 0x60 - 0x6f 0111111111111111
2202 // pqrstuvwxyz ~ 2279 // pqrstuvwxyz ~
2203 0x47ff]; // 0x70 - 0x7f 1111111111100010 2280 0x47ff]; // 0x70 - 0x7f 1111111111100010
2204 } 2281 }
OLDNEW
« no previous file with comments | « no previous file | tests/co19/co19-co19.status » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698