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

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

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