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

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

Issue 16019002: Merge the dart:uri library into dart:core and update the Uri class (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Final cleanup Created 7 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 | « sdk/lib/core/corelib_sources.gypi ('k') | sdk/lib/io/http_impl.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 part of dart.core;
6
7 /**
8 * A parsed URI, as specified by RFC-3986, http://tools.ietf.org/html/rfc3986.
9 */
10 class Uri {
11 int _port;
12
13 /**
14 * Returns the scheme.
15 *
16 * Returns the empty string if there is no scheme.
17 */
18 final String scheme;
19
20 /**
21 * Returns the authority.
22 *
23 * The authority is formatted from the [userInfo], [host] and [port]
24 * components.
25 *
26 * Returns the empty string if there is no authority.
27 */
28 String get authority {
29 if (!hasAuthority) return "";
30 var sb = new StringBuffer();
31 _writeAuthority(sb);
32 return sb.toString();
33 }
34 /**
35 * Returns the user info part of the authority.
36 *
37 * Returns the empty string if there is no user info in the authority.
38 */
39 final String userInfo;
40
41 /**
42 * Returns the host part of the authority.
43 *
44 * Returns the empty string if there is no authority and hence no host.
45 */
46 final String host;
47
48 /**
49 * Returns the port part of the authority.
50 *
51 * Returns 0 if there is no port in the authority.
52 */
53 int get port => _port;
54
55 /**
56 * Returns the path.
57 *
58 * The returned path is encoded. To get direct access to the decoded
59 * path use [pathSegments].
60 *
61 * Returns the empty string if there is no path.
62 */
63 final String path;
64
65 /**
66 * Returns the URI query. The returned query is encoded. To get
67 * direct access to the decoded query use [queryParameters].
68 *
69 * Returns the empty string if there is no query.
70 */
71 final String query;
72
73 /**
74 * Returns the fragment.
75 *
76 * Returns the empty string if there is no fragment.
77 */
78 final String fragment;
79
80 /**
81 * Creates a new URI object by parsing a URI string.
82 */
83 static Uri parse(String uri) => new Uri._fromMatch(_splitRe.firstMatch(uri));
84
85 Uri._fromMatch(Match m) :
86 this(scheme: _emptyIfNull(m[_COMPONENT_SCHEME]),
87 userInfo: _emptyIfNull(m[_COMPONENT_USER_INFO]),
88 host: _eitherOf(
89 m[_COMPONENT_HOST], m[_COMPONENT_HOST_IPV6]),
90 port: _parseIntOrZero(m[_COMPONENT_PORT]),
91 path: _emptyIfNull(m[_COMPONENT_PATH]),
92 query: _emptyIfNull(m[_COMPONENT_QUERY_DATA]),
93 fragment: _emptyIfNull(m[_COMPONENT_FRAGMENT]));
94
95 /*
96 * Create a new URI from its components.
97 *
98 * Each component is set through a named argument. Any number of
99 * components can be provided. The default value for the components
100 * not provided is the empry string, except for [port] which has a
101 * default value of 0. The [path] and [query] components can be set
102 * using two different named arguments.
103 *
104 * The scheme component is set through [scheme]. The scheme is
105 * normalized to all lowercase letters.
106 *
107 * The user info part of the authority component is set through
108 * [userInfo].
109 *
110 * The host part of the authority component is set through
111 * [host]. The host can either be a hostname, a IPv4 address or an
112 * IPv6 address, contained in '[' and ']'. If the host contains a
113 * ':' character, the '[' and ']' are added if not already provided.
114 *
115 * The port part of the authority component is set through
116 * [port]. The port is normalized for scheme http and https where
117 * port 80 and port 443 respectively is set.
118 *
119 * The path component is set through either [path] or
120 * [pathSegments]. When [path] is used, the provided string is
121 * expected to be fully percent-encoded, and is used in its literal
122 * form. When [pathSegments] is used, each of the provided segments
123 * is percent-encoded and joined using the forward slash
124 * separator. The percent-encoding of the path segments encodes all
125 * characters except for the unreserved characters and the following
126 * list of characters: `!$&'()*+,;=:@`.
127 *
128 * The query component is set through either [query] or
129 * [queryParameters]. When [query] is used the provided string is
130 * expected to be fully percent-encoded and is used in its literal
131 * form. When [queryParameters] is used the query is built from the
132 * provided map. Each key and value in the map is percent-encoded
133 * and joined using equal and ampersand characters. The
134 * percent-encoding of the keys and values encodes all characters
135 * except for the unreserved characters.
136 *
137 * The fragment component is set through [fragment].
138 */
139 Uri({scheme,
140 this.userInfo: "",
141 this.host: "",
142 port: 0,
143 String path,
144 List<String> pathSegments,
145 String query,
146 Map<String, String> queryParameters,
147 fragment: ""}) :
148 scheme = _makeScheme(scheme),
149 path = _makePath(path, pathSegments),
150 query = _makeQuery(query, queryParameters),
151 fragment = _makeFragment(fragment) {
152 // Perform scheme specific normalization.
153 if (scheme == "http" && port == 80) {
154 _port = 0;
155 } else if (scheme == "https" && port == 443) {
156 _port = 0;
157 } else {
158 _port = port;
159 }
160 }
161
162 /*
163 * Returns the URI path split into its segments. Each of the
164 * segments in the returned list have been decoded. If the path is
165 * empty the empty list will be returned.
166 */
167 List<String> get pathSegments {
168 if (path == "") return const<String>[];
169 return path.split("/").map(Uri.decodeComponent).toList(growable: false);
170 }
171
172 /*
173 * Returns the URI query split into a map according to the rules
174 * specified for FORM post in the HTML 4.01 specification. Each key
175 * and value in the returned map have been decoded. If there is no
176 * query the empty map will be returned.
177 */
178 Map<String, String> get queryParameters {
179 return query.split("&").fold({}, (map, element) {
180 int index = element.indexOf("=");
181 if (index == -1) {
182 if (!element.isEmpty) map[element] = "";
183 } else if (index != 0) {
184 var key = element.substring(0, index);
185 var value = element.substring(index + 1);
186 map[Uri.decodeQueryComponent(key)] = decodeQueryComponent(value);
187 }
188 return map;
189 });
190 }
191
192 static String _makeScheme(String scheme) {
193 bool isSchemeLowerCharacter(int ch) {
194 return ch < 128 &&
195 ((_schemeLowerTable[ch >> 4] & (1 << (ch & 0x0f))) != 0);
196 }
197
198 bool isSchemeCharacter(int ch) {
199 return ch < 128 && ((_schemeTable[ch >> 4] & (1 << (ch & 0x0f))) != 0);
200 }
201
202 if (scheme == null) return "";
203 bool allLowercase = true;
204 int length = scheme.length;
205 for (int i = 0; i < length; i++) {
206 int codeUnit = scheme.codeUnitAt(i);
207 if (!isSchemeLowerCharacter(codeUnit)) {
208 if (isSchemeCharacter(codeUnit)) {
209 allLowercase = false;
210 } else {
211 throw new ArgumentError('Illegal scheme: $scheme');
212 }
213 }
214 }
215
216 return allLowercase ? scheme : scheme.toLowerCase();
217 }
218
219 static String _makePath(String path, List<String> pathSegments) {
220 if (path == null && pathSegments == null) return "";
221 if (path != null && pathSegments != null) {
222 throw new ArgumentError('Both path and pathSegments specified');
223 }
224 if (path != null) return _normalize(path);
225
226 return pathSegments.map((s) => _uriEncode(_pathCharTable, s)).join("/");
227 }
228
229 static String _makeQuery(String query, Map<String, String> queryParameters) {
230 if (query == null && queryParameters == null) return "";
231 if (query != null && queryParameters != null) {
232 throw new ArgumentError('Both query and queryParameters specified');
233 }
234 if (query != null) return _normalize(query);
235
236 var result = new StringBuffer();
237 var first = true;
238 queryParameters.forEach((key, value) {
239 if (!first) {
240 result.write("&");
241 }
242 first = false;
243 result.write(Uri.encodeQueryComponent(key));
244 if (value != null && !value.isEmpty) {
245 result.write("=");
246 result.write(Uri.encodeQueryComponent(value));
247 }
248 });
249 return result.toString();
250 }
251
252 static String _makeFragment(String fragment) {
253 if (fragment == null) return "";
254 return _normalize(fragment);
255 }
256
257 static String _normalize(String component) {
258 bool isNormalizedHexDigit(int digit) {
259 return (_ZERO <= digit && digit <= _NINE) ||
260 (_UPPER_CASE_A <= digit && digit <= _UPPER_CASE_F);
261 }
262
263 bool isLowerCaseHexDigit(int digit) {
264 return _LOWER_CASE_A <= digit && digit <= _LOWER_CASE_F;
265 }
266
267 bool isUnreserved(int ch) {
268 return ch < 128 &&
269 ((_unreservedTable[ch >> 4] & (1 << (ch & 0x0f))) != 0);
270 }
271
272 int normalizeHexDigit(int index) {
273 var codeUnit = component.codeUnitAt(index);
274 if (isLowerCaseHexDigit(codeUnit)) {
275 return codeUnit - 0x20;
276 } else if (!isNormalizedHexDigit(codeUnit)) {
277 throw new ArgumentError("Invalid URI component: $component");
278 } else {
279 return codeUnit;
280 }
281 }
282
283 int decodeHexDigitPair(int index) {
284 int byte = 0;
285 for (int i = 0; i < 2; i++) {
286 var codeUnit = component.codeUnitAt(index + i);
287 if (_ZERO <= codeUnit && codeUnit <= _NINE) {
288 byte = byte * 16 + codeUnit - _ZERO;
289 } else {
290 // Check ranges A-F (0x41-0x46) and a-f (0x61-0x66).
291 codeUnit |= 0x20;
292 if (_LOWER_CASE_A <= codeUnit &&
293 codeUnit <= _LOWER_CASE_F) {
294 byte = byte * 16 + codeUnit - _LOWER_CASE_A + 10;
295 } else {
296 throw new ArgumentError(
297 "Invalid percent-encoding in URI component: $component");
298 }
299 }
300 }
301 return byte;
302 }
303
304 // Start building the normalized component string.
305 StringBuffer result;
306 int length = component.length;
307 int index = 0;
308 int prevIndex = 0;
309 while (index < length) {
310
311 // Copy a part of the component string to the result.
312 fillResult() {
313 if (result == null) {
314 assert(prevIndex == 0);
315 result = new StringBuffer(component.substring(prevIndex, index));
316 } else {
317 result.write(component.substring(prevIndex, index));
318 }
319 }
320
321 // Normalize percent encoding to uppercase and don't encode
322 // unreserved characters.
323 if (component.codeUnitAt(index) == _PERCENT) {
324 if (length < index + 2) {
325 throw new ArgumentError(
326 "Invalid percent-encoding in URI component: $component");
327 }
328
329 var codeUnit1 = component.codeUnitAt(index + 1);
330 var codeUnit2 = component.codeUnitAt(index + 2);
331 var decodedCodeUnit = decodeHexDigitPair(index + 1);
332 if (isNormalizedHexDigit(codeUnit1) &&
333 isNormalizedHexDigit(codeUnit2) &&
334 !isUnreserved(decodedCodeUnit)) {
335 index += 3;
336 } else {
337 fillResult();
338 if (isUnreserved(decodedCodeUnit)) {
339 result.writeCharCode(decodedCodeUnit);
340 } else {
341 result.write("%");
342 result.writeCharCode(normalizeHexDigit(index + 1));
343 result.writeCharCode(normalizeHexDigit(index + 2));
344 }
345 index += 3;
346 prevIndex = index;
347 }
348 } else {
349 index++;
350 }
351 }
352 assert(index == length);
353
354 if (result == null) return component;
355 return result.toString();
356 }
357
358 static String _emptyIfNull(String val) => val != null ? val : '';
359
360 static int _parseIntOrZero(String val) {
361 if (val != null && val != '') {
362 return int.parse(val);
363 } else {
364 return 0;
365 }
366 }
367
368 static String _eitherOf(String val1, String val2) {
369 if (val1 != null) return val1;
370 if (val2 != null) return val2;
371 return '';
372 }
373
374 // NOTE: This code was ported from: closure-library/closure/goog/uri/utils.js
375 static final RegExp _splitRe = new RegExp(
376 '^'
377 '(?:'
378 '([^:/?#.]+)' // scheme - ignore special characters
379 // used by other URL parts such as :,
380 // ?, /, #, and .
381 ':)?'
382 '(?://'
383 '(?:([^/?#]*)@)?' // userInfo
384 '(?:'
385 r'([\w\d\-\u0100-\uffff.%]*)'
386 // host - restrict to letters,
387 // digits, dashes, dots, percent
388 // escapes, and unicode characters.
389 '|'
390 // TODO(ajohnsen): Only allow a max number of parts?
391 r'\[([A-Fa-f0-9:.]*)\])'
392 // IPv6 host - restrict to hex,
393 // dot and colon.
394 '(?::([0-9]+))?' // port
395 ')?'
396 r'([^?#[]+)?' // path
397 r'(?:\?([^#]*))?' // query
398 '(?:#(.*))?' // fragment
399 r'$');
400
401 static const _COMPONENT_SCHEME = 1;
402 static const _COMPONENT_USER_INFO = 2;
403 static const _COMPONENT_HOST = 3;
404 static const _COMPONENT_HOST_IPV6 = 4;
405 static const _COMPONENT_PORT = 5;
406 static const _COMPONENT_PATH = 6;
407 static const _COMPONENT_QUERY_DATA = 7;
408 static const _COMPONENT_FRAGMENT = 8;
409
410 /**
411 * Returns `true` if the URI is absolute.
412 */
413 bool get isAbsolute {
414 if ("" == scheme) return false;
415 if ("" != fragment) return false;
416 return true;
417 }
418
419 String _merge(String base, String reference) {
420 if (base == "") return "/$reference";
421 return "${base.substring(0, base.lastIndexOf("/") + 1)}$reference";
422 }
423
424 String _removeDotSegments(String path) {
425 List<String> output = [];
426 bool appendSlash = false;
427 for (String segment in path.split("/")) {
428 appendSlash = false;
429 if (segment == "..") {
430 if (!output.isEmpty &&
431 ((output.length != 1) || (output[0] != ""))) output.removeLast();
432 appendSlash = true;
433 } else if ("." == segment) {
434 appendSlash = true;
435 } else {
436 output.add(segment);
437 }
438 }
439 if (appendSlash) output.add("");
440 return output.join("/");
441 }
442
443 Uri resolve(String uri) {
444 return resolveUri(Uri.parse(uri));
445 }
446
447 Uri resolveUri(Uri reference) {
448 // From RFC 3986.
449 String targetScheme;
450 String targetUserInfo;
451 String targetHost;
452 int targetPort;
453 String targetPath;
454 String targetQuery;
455 if (reference.scheme != "") {
456 targetScheme = reference.scheme;
457 targetUserInfo = reference.userInfo;
458 targetHost = reference.host;
459 targetPort = reference.port;
460 targetPath = _removeDotSegments(reference.path);
461 targetQuery = reference.query;
462 } else {
463 if (reference.hasAuthority) {
464 targetUserInfo = reference.userInfo;
465 targetHost = reference.host;
466 targetPort = reference.port;
467 targetPath = _removeDotSegments(reference.path);
468 targetQuery = reference.query;
469 } else {
470 if (reference.path == "") {
471 targetPath = this.path;
472 if (reference.query != "") {
473 targetQuery = reference.query;
474 } else {
475 targetQuery = this.query;
476 }
477 } else {
478 if (reference.path.startsWith("/")) {
479 targetPath = _removeDotSegments(reference.path);
480 } else {
481 targetPath = _removeDotSegments(_merge(this.path, reference.path));
482 }
483 targetQuery = reference.query;
484 }
485 targetUserInfo = this.userInfo;
486 targetHost = this.host;
487 targetPort = this.port;
488 }
489 targetScheme = this.scheme;
490 }
491 return new Uri(scheme: targetScheme,
492 userInfo: targetUserInfo,
493 host: targetHost,
494 port: targetPort,
495 path: targetPath,
496 query: targetQuery,
497 fragment: reference.fragment);
498 }
499
500 bool get hasAuthority => host != "";
501
502 /**
503 * Returns the origin of the URI in the form scheme://host:port for the
504 * schemes http and https.
505 *
506 * Throws StateError if the scheme is not http or https.
507 *
508 * See: http://www.w3.org/TR/2011/WD-html5-20110405/origin-0.html#origin
509 */
510 String get origin {
511 if (scheme == "" || host == null || host == "") {
512 throw new StateError("Cannot use origin without a scheme: $this");
513 }
514 if (scheme != "http" && scheme != "https") {
515 throw new StateError(
516 "Origin is only applicable schemes http and https: $this");
517 }
518 if (port == 0) return "$scheme://$host";
519 return "$scheme://$host:$port";
520 }
521
522 void _writeAuthority(StringSink ss) {
523 _addIfNonEmpty(ss, userInfo, userInfo, "@");
524 ss.write(host == null ? "null" :
525 host.contains(':') ? '[$host]' : host);
526 if (port != 0) {
527 ss.write(":");
528 ss.write(port.toString());
529 }
530 }
531
532 String toString() {
533 StringBuffer sb = new StringBuffer();
534 _addIfNonEmpty(sb, scheme, scheme, ':');
535 if (hasAuthority || (scheme == "file")) {
536 sb.write("//");
537 _writeAuthority(sb);
538 }
539 sb.write(path);
540 _addIfNonEmpty(sb, query, "?", query);
541 _addIfNonEmpty(sb, fragment, "#", fragment);
542 return sb.toString();
543 }
544
545 bool operator==(other) {
546 if (other is! Uri) return false;
547 Uri uri = other;
548 return scheme == uri.scheme &&
549 userInfo == uri.userInfo &&
550 host == uri.host &&
551 port == uri.port &&
552 path == uri.path &&
553 query == uri.query &&
554 fragment == uri.fragment;
555 }
556
557 int get hashCode {
558 int combine(part, current) {
559 // The sum is truncated to 30 bits to make sure it fits into a Smi.
560 return (current * 31 + part.hashCode) & 0x3FFFFFFF;
561 }
562 return combine(scheme, combine(userInfo, combine(host, combine(port,
563 combine(path, combine(query, combine(fragment, 1)))))));
564 }
565
566 static void _addIfNonEmpty(StringBuffer sb, String test,
567 String first, String second) {
568 if ("" != test) {
569 sb.write(first == null ? "null" : first);
570 sb.write(second == null ? "null" : second);
571 }
572 }
573
574 /**
575 * Encode the string [component] using percent-encoding to make it
576 * safe for literal use as a URI component.
577 *
578 * All characters except uppercase and lowercase letters, digits and
579 * the characters `!$&'()*+,;=:@` are percent-encoded. This is the
580 * set of characters specified in RFC 2396 and the which is
581 * specified for the encodeUriComponent in ECMA-262 version 5.1.
582 *
583 * When manually encoding path segments or query components remember
584 * to encode each part separately before building the path or query
585 * string.
586 *
587 * For encoding the query part consider using
588 * [encodeQueryComponent].
589 *
590 * To avoid the need for explicitly encoding use the [pathSegments]
591 * and [queryParameters] optional named arguments when constructing
592 * a Uri.
593 */
594 static String encodeComponent(String component) {
595 return _uriEncode(_unreserved2396Table, component);
596 }
597
598 /*
599 * Encode the string [component] according to the HTML 4.01 rules
600 * for encoding the posting of a HTML form as a query string
601 * component.
602 *
603 * Spaces will be replaced with plus and all characters except for
604 * uppercase and lowercase letters, decimal digits and the
605 * characters `-._~`. Note that the set of characters encoded is a
606 * superset of what HTML 4.01 says as it refers to RFC 1738 for
607 * reserved characters.
608 *
609 * When manually encoding query components remember to encode each
610 * part separately before building the query string.
611 *
612 * To avoid the need for explicitly encoding the query use the
613 * [queryParameters] optional named arguments when constructing a
614 * URI.
615 *
616 * See http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2 for more
617 * details.
618 */
619 static String encodeQueryComponent(String component) {
620 return _uriEncode(_unreservedTable, component, spaceToPlus: true);
621 }
622
623 /**
624 * Decodes the percent-encoding in [encodedComponent].
625 *
626 * Note that decoding a URI component might change its meaning as
627 * some of the decoded characters could be characters with are
628 * delimiters for a given URI componene type. Always split a URI
629 * component using the delimiters for the component before decoding
630 * the individual parts.
631 *
632 * For handling the [path] and [query] components consider using
633 * [pathSegments] and [queryParameters] to get the separated and
634 * decoded component.
635 */
636 static String decodeComponent(String encodedComponent) {
637 return _uriDecode(encodedComponent);
638 }
639
640 static String decodeQueryComponent(String encodedComponent) {
641 return _uriDecode(encodedComponent, plusToSpace: true);
642 }
643
644 /**
645 * Encode the string [uri] using percent-encoding to make it
646 * safe for literal use as a full URI.
647 *
648 * All characters except uppercase and lowercase letters, digits and
649 * the characters `!#$&'()*+,-./:;=?@_~` are percent-encoded. This
650 * is the set of characters specified in in ECMA-262 version 5.1 for
651 * the encodeURI function .
652 */
653 static String encodeFull(String uri) {
654 return _uriEncode(_encodeFullTable, uri);
655 }
656
657 /**
658 * Decodes the percent-encoding in [uri].
659 *
660 * Note that decoding a full URI might change its meaning as some of
661 * the decoded characters could be reserved characters. In most
662 * cases an encoded URI should be parsed into components using
663 * [Uri.parse] before decoding the separate components.
664 */
665 static String decodeFull(String uri) {
666 return _uriDecode(uri);
667 }
668
669 // Frequently used character codes.
670 static const int _PERCENT = 0x25;
671 static const int _ZERO = 0x30;
672 static const int _NINE = 0x39;
673 static const int _UPPER_CASE_A = 0x41;
674 static const int _UPPER_CASE_F = 0x46;
675 static const int _LOWER_CASE_A = 0x61;
676 static const int _LOWER_CASE_F = 0x66;
677
678 /**
679 * This is the internal implementation of JavaScript's encodeURI function.
680 * It encodes all characters in the string [text] except for those
681 * that appear in [canonicalTable], and returns the escaped string.
682 */
683 static String _uriEncode(List<int> canonicalTable,
684 String text,
685 {bool spaceToPlus: false}) {
686 byteToHex(int v) {
687 final String hex = '0123456789ABCDEF';
688 return '%${hex[v >> 4]}${hex[v & 0x0f]}';
689 }
690
691 StringBuffer result = new StringBuffer();
692 for (int i = 0; i < text.length; i++) {
693 int ch = text.codeUnitAt(i);
694 if (ch < 128 && ((canonicalTable[ch >> 4] & (1 << (ch & 0x0f))) != 0)) {
695 result.write(text[i]);
696 } else if (spaceToPlus && text[i] == " ") {
697 result.write("+");
698 } else {
699 if (ch >= 0xD800 && ch < 0xDC00) {
700 // Low surrogate. We expect a next char high surrogate.
701 ++i;
702 int nextCh = text.length == i ? 0 : text.codeUnitAt(i);
703 if (nextCh >= 0xDC00 && nextCh < 0xE000) {
704 // convert the pair to a U+10000 codepoint
705 ch = 0x10000 + ((ch - 0xD800) << 10) + (nextCh - 0xDC00);
706 } else {
707 throw new ArgumentError('Malformed URI');
708 }
709 }
710 for (int codepoint in codepointsToUtf8([ch])) {
711 result.write(byteToHex(codepoint));
712 }
713 }
714 }
715 return result.toString();
716 }
717
718 /**
719 * Convert a byte (2 character hex sequence) in string [s] starting
720 * at position [pos] to its ordinal value
721 */
722 static int _hexCharPairToByte(String s, int pos) {
723 int byte = 0;
724 for (int i = 0; i < 2; i++) {
725 var charCode = s.codeUnitAt(pos + i);
726 if (0x30 <= charCode && charCode <= 0x39) {
727 byte = byte * 16 + charCode - 0x30;
728 } else {
729 // Check ranges A-F (0x41-0x46) and a-f (0x61-0x66).
730 charCode |= 0x20;
731 if (0x61 <= charCode && charCode <= 0x66) {
732 byte = byte * 16 + charCode - 0x57;
733 } else {
734 throw new ArgumentError("Invalid URL encoding");
735 }
736 }
737 }
738 return byte;
739 }
740
741 /**
742 * A JavaScript-like decodeURI function. It unescapes the string [text] and
743 * returns the unescaped string.
744 */
745 static String _uriDecode(String text, {bool plusToSpace: false}) {
746 StringBuffer result = new StringBuffer();
747 List<int> codepoints = new List<int>();
748 for (int i = 0; i < text.length;) {
749 String ch = text[i];
750 if (ch != '%') {
751 if (plusToSpace && ch == '+') {
752 result.write(" ");
753 } else {
754 result.write(ch);
755 }
756 i++;
757 } else {
758 codepoints.clear();
759 while (ch == '%') {
760 if (++i > text.length - 2) {
761 throw new ArgumentError('Truncated URI');
762 }
763 codepoints.add(_hexCharPairToByte(text, i));
764 i += 2;
765 if (i == text.length)
766 break;
767 ch = text[i];
768 }
769 result.write(decodeUtf8(codepoints));
770 }
771 }
772 return result.toString();
773 }
774
775 // Tables of char-codes organized as a bit vector of 128 bits where
776 // each bit indicate whether a character code on the 0-127 needs to
777 // be escaped or not.
778
779 // The unreserved characters of RFC 3986.
780 static const _unreservedTable = const [
781 // LSB MSB
782 // | |
783 0x0000, // 0x00 - 0x0f 0000000000000000
784 0x0000, // 0x10 - 0x1f 0000000000000000
785 // -.
786 0x6000, // 0x20 - 0x2f 0000000000000110
787 // 0123456789
788 0x03ff, // 0x30 - 0x3f 1111111111000000
789 // ABCDEFGHIJKLMNO
790 0xfffe, // 0x40 - 0x4f 0111111111111111
791 // PQRSTUVWXYZ _
792 0x87ff, // 0x50 - 0x5f 1111111111100001
793 // abcdefghijklmno
794 0xfffe, // 0x60 - 0x6f 0111111111111111
795 // pqrstuvwxyz ~
796 0x47ff]; // 0x70 - 0x7f 1111111111100010
797
798 // The unreserved characters of RFC 2396.
799 static const _unreserved2396Table = const [
800 // LSB MSB
801 // | |
802 0x0000, // 0x00 - 0x0f 0000000000000000
803 0x0000, // 0x10 - 0x1f 0000000000000000
804 // ! '()* -.
805 0x6782, // 0x20 - 0x2f 0100000111100110
806 // 0123456789
807 0x03ff, // 0x30 - 0x3f 1111111111000000
808 // ABCDEFGHIJKLMNO
809 0xfffe, // 0x40 - 0x4f 0111111111111111
810 // PQRSTUVWXYZ _
811 0x87ff, // 0x50 - 0x5f 1111111111100001
812 // abcdefghijklmno
813 0xfffe, // 0x60 - 0x6f 0111111111111111
814 // pqrstuvwxyz ~
815 0x47ff]; // 0x70 - 0x7f 1111111111100010
816
817 // Table of reserved characters specified by ECMAScript 5.
818 static const _encodeFullTable = const [
819 // LSB MSB
820 // | |
821 0x0000, // 0x00 - 0x0f 0000000000000000
822 0x0000, // 0x10 - 0x1f 0000000000000000
823 // ! #$ &'()*+,-./
824 0xf7da, // 0x20 - 0x2f 0101101111101111
825 // 0123456789:; = ?
826 0xafff, // 0x30 - 0x3f 1111111111110101
827 // @ABCDEFGHIJKLMNO
828 0xffff, // 0x40 - 0x4f 1111111111111111
829 // PQRSTUVWXYZ _
830 0x87ff, // 0x50 - 0x5f 1111111111100001
831 // abcdefghijklmno
832 0xfffe, // 0x60 - 0x6f 0111111111111111
833 // pqrstuvwxyz ~
834 0x47ff]; // 0x70 - 0x7f 1111111111100010
835
836 // Characters allowed in the scheme.
837 static const _schemeTable = const [
838 // LSB MSB
839 // | |
840 0x0000, // 0x00 - 0x0f 0000000000000000
841 0x0000, // 0x10 - 0x1f 0000000000000000
842 // + -.
843 0x6800, // 0x20 - 0x2f 0000000000010110
844 // 0123456789
845 0x03ff, // 0x30 - 0x3f 1111111111000000
846 // ABCDEFGHIJKLMNO
847 0xfffe, // 0x40 - 0x4f 0111111111111111
848 // PQRSTUVWXYZ
849 0x07ff, // 0x50 - 0x5f 1111111111100001
850 // abcdefghijklmno
851 0xfffe, // 0x60 - 0x6f 0111111111111111
852 // pqrstuvwxyz
853 0x07ff]; // 0x70 - 0x7f 1111111111100010
854
855 // Characters allowed in scheme except for upper case letters.
856 static const _schemeLowerTable = const [
857 // LSB MSB
858 // | |
859 0x0000, // 0x00 - 0x0f 0000000000000000
860 0x0000, // 0x10 - 0x1f 0000000000000000
861 // + -.
862 0x6800, // 0x20 - 0x2f 0000000000010110
863 // 0123456789
864 0x03ff, // 0x30 - 0x3f 1111111111000000
865 //
866 0x0000, // 0x40 - 0x4f 0111111111111111
867 //
868 0x0000, // 0x50 - 0x5f 1111111111100001
869 // abcdefghijklmno
870 0xfffe, // 0x60 - 0x6f 0111111111111111
871 // pqrstuvwxyz
872 0x07ff]; // 0x70 - 0x7f 1111111111100010
873
874 // Sub delimiter characters combined with unreserved as of 3986.
875 // sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
876 // / "*" / "+" / "," / ";" / "="
877 // RFC 3986 section 2.3.
878 // unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
879 static const _subDelimitersTable = const [
880 // LSB MSB
881 // | |
882 0x0000, // 0x00 - 0x0f 0000000000000000
883 0x0000, // 0x10 - 0x1f 0000000000000000
884 // ! $ &'()*+,-.
885 0x7fd2, // 0x20 - 0x2f 0100101111111110
886 // 0123456789 ; =
887 0x2bff, // 0x30 - 0x3f 1111111111010100
888 // ABCDEFGHIJKLMNO
889 0xfffe, // 0x40 - 0x4f 0111111111111111
890 // PQRSTUVWXYZ _
891 0x87ff, // 0x50 - 0x5f 1111111111100001
892 // abcdefghijklmno
893 0xfffe, // 0x60 - 0x6f 0111111111111111
894 // pqrstuvwxyz ~
895 0x47ff]; // 0x70 - 0x7f 1111111111100010
896
897 // Characters allowed in the path as of RFC 3986.
898 // RFC 3986 section 3.3.
899 // pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
900 static const _pathCharTable = const [
901 // LSB MSB
902 // | |
903 0x0000, // 0x00 - 0x0f 0000000000000000
904 0x0000, // 0x10 - 0x1f 0000000000000000
905 // ! $ &'()*+,-.
906 0x7fd2, // 0x20 - 0x2f 0100101111111110
907 // 0123456789:; =
908 0x2fff, // 0x30 - 0x3f 1111111111110100
909 // @ABCDEFGHIJKLMNO
910 0xffff, // 0x40 - 0x4f 1111111111111111
911 // PQRSTUVWXYZ _
912 0x87ff, // 0x50 - 0x5f 1111111111100001
913 // abcdefghijklmno
914 0xfffe, // 0x60 - 0x6f 0111111111111111
915 // pqrstuvwxyz ~
916 0x47ff]; // 0x70 - 0x7f 1111111111100010
917
918 // Characters allowed in the query as of RFC 3986.
919 // RFC 3986 section 3.4.
920 // query = *( pchar / "/" / "?" )
921 static const _queryCharTable = const [
922 // LSB MSB
923 // | |
924 0x0000, // 0x00 - 0x0f 0000000000000000
925 0x0000, // 0x10 - 0x1f 0000000000000000
926 // ! $ &'()*+,-./
927 0xffd2, // 0x20 - 0x2f 0100101111111111
928 // 0123456789:; = ?
929 0xafff, // 0x30 - 0x3f 1111111111110101
930 // @ABCDEFGHIJKLMNO
931 0xffff, // 0x40 - 0x4f 1111111111111111
932 // PQRSTUVWXYZ _
933 0x87ff, // 0x50 - 0x5f 1111111111100001
934 // abcdefghijklmno
935 0xfffe, // 0x60 - 0x6f 0111111111111111
936 // pqrstuvwxyz ~
937 0x47ff]; // 0x70 - 0x7f 1111111111100010
938 }
OLDNEW
« no previous file with comments | « sdk/lib/core/corelib_sources.gypi ('k') | sdk/lib/io/http_impl.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698