OLD | NEW |
---|---|
(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 URI scheme. If there is no scheme the empty string | |
floitsch
2013/05/24 20:37:27
All these comments are dartdocs. -> /** instead of
Søren Gjesse
2013/05/27 10:47:21
Of cause - thanks.
| |
15 * will be returned. | |
floitsch
2013/05/24 20:37:27
No need for future.
If there is no scheme the empt
Søren Gjesse
2013/05/27 10:47:21
Done.
Similar change for all Uri component proper
| |
16 */ | |
17 final String scheme; | |
18 | |
19 /* | |
20 * Returns the user info part of the URI authority. If there is no | |
21 * user info in the authority the empty string will be returned. | |
22 */ | |
23 final String userInfo; | |
24 | |
25 /* | |
26 * Returns the host part of the URI authority. If there is no | |
27 * authority and hence no host the empty string will be returned. | |
28 */ | |
29 final String host; | |
30 | |
31 /* | |
32 * Returns the port part of the URI authority. If there is no | |
33 * port in the authority 0 will be returned. | |
34 */ | |
35 int get port => _port; | |
36 | |
37 /* | |
38 * Returns the URI path. The returned path is encoded. To get direct | |
39 * access to the decoded path use [pathSegement]. If there is no | |
floitsch
2013/05/24 20:37:27
pathSegments
Søren Gjesse
2013/05/27 10:47:21
Done.
| |
40 * path the empty string will be returned. | |
41 */ | |
42 final String path; | |
43 | |
44 /* | |
45 * Returns the URI query. The returned query is encoded. To get | |
46 * direct access to the decoded query use [queryParameters]. If | |
47 * there is no query the empty string will be returned. | |
48 */ | |
49 final String query; | |
50 | |
51 /* | |
52 * Returns the URI fragment. If there is no fragment the empty string | |
53 * will be returned. | |
54 */ | |
55 final String fragment; | |
56 | |
57 /** | |
58 * Creates a new URI object by parsing a URI string. | |
59 */ | |
60 static Uri parse(String uri) => new Uri._fromMatch(_splitRe.firstMatch(uri)); | |
61 | |
62 Uri._fromMatch(Match m) : | |
63 this(scheme: _emptyIfNull(m[_COMPONENT_SCHEME]), | |
64 userInfo: _emptyIfNull(m[_COMPONENT_USER_INFO]), | |
65 host: _eitherOf( | |
66 m[_COMPONENT_HOST], m[_COMPONENT_HOST_IPV6]), | |
67 port: _parseIntOrZero(m[_COMPONENT_PORT]), | |
68 path: _emptyIfNull(m[_COMPONENT_PATH]), | |
69 query: _emptyIfNull(m[_COMPONENT_QUERY_DATA]), | |
70 fragment: _emptyIfNull(m[_COMPONENT_FRAGMENT])); | |
71 | |
72 /* | |
73 * Create a new URI from its components. | |
74 * | |
75 * The URI scheme is set through [scheme]. The scheme will be | |
76 * normalized to all lowercase letters. | |
77 * | |
78 * The URI user info of the domain is set through [userInfo]. | |
79 * | |
80 * The URI host part of the domain is set through [host]. The host | |
81 * can either be a hostname, a IPv4 address or an IPv6 address, | |
82 * contained in '[' and ']'. If the host contains a ':' character, | |
83 * the host will have the '[' and ']' added. | |
84 * | |
85 * The URI port is set through [port]. The port is normalized for | |
86 * scheme http and https where port 80 and port 443 respectively | |
87 * will be set as 0. | |
floitsch
2013/05/24 20:37:27
is set to 0.
Søren Gjesse
2013/05/27 10:47:21
This was a broken sentence. Removed the "as 0" fro
| |
88 * | |
89 * The path component can be specified using either [path] or | |
90 * [pathSegments]. When [path] is used the provided string is | |
91 * expected to be fully percent-encoded and will be used in its | |
floitsch
2013/05/24 20:37:27
is used
Søren Gjesse
2013/05/27 10:47:21
Done.
| |
92 * literal form for the URI path. When [pathSegments] is used each | |
93 * of the provided segments will be percent encoded and joined using | |
94 * the forward slash separator. | |
95 * | |
96 * The query component can be specified using either [query] or | |
97 * [queryParameters]. When [query] is used the provided string is | |
98 * expected to be fully percent-encoded and will be used in its | |
99 * literal form for the URI query. When [queryParameters] is used xxx | |
floitsch
2013/05/24 20:37:27
xxx
Søren Gjesse
2013/05/27 10:47:21
My mark of unfinished comment - sorry.
| |
100 * | |
101 * The URI fragment is set through [fragment]. | |
102 */ | |
103 Uri({scheme, | |
104 this.userInfo: "", | |
105 this.host: "", | |
106 port: 0, | |
107 String path, | |
108 List<String> pathSegments, | |
109 String query, | |
110 Map<String, String> queryParameters, | |
111 fragment: ""}) : | |
112 scheme = _makeScheme(scheme), | |
113 path = _makePath(path, pathSegments), | |
114 query = _makeQuery(query, queryParameters), | |
115 fragment = _makeFragment(fragment) { | |
116 // Perform scheme specific normalization. | |
117 if (scheme == "http" && port == 80) { | |
118 _port = 0; | |
119 } else if (scheme == "https" && port == 443) { | |
120 _port = 0; | |
121 } else { | |
122 _port = port; | |
123 } | |
124 } | |
125 | |
126 /* | |
127 * Returns the URI path split into its segments. Each of the | |
128 * segments in the returned list have been decoded. If the path is | |
129 * empty the empty list will be returned. | |
130 */ | |
131 List<String> get pathSegments { | |
132 if (path == "") return []; | |
floitsch
2013/05/24 20:37:27
const <String>[];
?
Søren Gjesse
2013/05/27 10:47:21
Done.
| |
133 return path.split("/").map(Uri.decodeComponent).toList(growable: false); | |
134 } | |
135 | |
136 /* | |
137 * Returns the URI query split into a map according to the rules | |
138 * specified for FORM post in the HTML 4.01 specification. Each key | |
139 * and value in the returned map have been decoded. If there is no | |
140 * query the empty map will be returned. | |
141 */ | |
142 Map<String, String> get queryParameters { | |
143 return query.split("&").fold({}, (map, element) { | |
144 int index = element.indexOf("="); | |
145 if (index == -1) { | |
146 if (!element.isEmpty) map[element] = ""; | |
147 } else if (index != 0) { | |
148 var key = element.substring(0, index); | |
149 var value = element.substring(index + 1, element.length); | |
floitsch
2013/05/24 20:37:27
substring doesn't need the second argument if it i
Søren Gjesse
2013/05/27 10:47:21
Done.
| |
150 map[Uri.decodeQueryComponent(key)] = decodeQueryComponent(value); | |
151 } | |
152 return map; | |
153 }); | |
154 } | |
155 | |
156 static String _makeScheme(String scheme) { | |
157 bool isSchemeLowerCharacter(int ch) { | |
158 return ch < 128 && | |
159 ((_schemeLowerTable[ch >> 4] & (1 << (ch & 0x0f))) != 0); | |
160 } | |
161 | |
162 bool isSchemeCharacter(int ch) { | |
163 return ch < 128 && ((_schemeTable[ch >> 4] & (1 << (ch & 0x0f))) != 0); | |
164 } | |
165 | |
166 if (scheme == null) return ""; | |
167 bool allLowercase = true; | |
168 int length = scheme.length; | |
169 for (int i = 0; i < length; i++) { | |
170 int codeUnit = scheme.codeUnitAt(i); | |
171 if (!isSchemeLowerCharacter(codeUnit)) { | |
172 if (isSchemeCharacter(codeUnit)) { | |
173 allLowercase = false; | |
174 } else { | |
175 throw new ArgumentError('Illegal scheme'); | |
floitsch
2013/05/24 20:37:27
Add the scheme: 'Illegal scheme: $scheme'.
Søren Gjesse
2013/05/27 10:47:21
Done.
| |
176 } | |
177 } | |
178 } | |
179 | |
180 return allLowercase ? scheme : scheme.toLowerCase(); | |
181 } | |
182 | |
183 static String _makePath(String path, List<String> pathSegments) { | |
184 if (path == null && pathSegments == null) return ""; | |
185 if (path != null && pathSegments != null) { | |
186 throw new ArgumentError('Both path and pathSegments specified'); | |
187 } | |
188 if (path != null) return _normalize(path); | |
189 | |
190 return pathSegments.map((s) => _uriEncode(_pathCharTable, s)).join("/"); | |
191 } | |
192 | |
193 static String _makeQuery(String query, Map<String, String> queryParameters) { | |
194 if (query == null && queryParameters == null) return ""; | |
195 if (query != null && queryParameters != null) { | |
196 throw new ArgumentError('Both query and queryParameters specified'); | |
197 } | |
198 if (query != null) return _normalize(query); | |
199 | |
200 var result = new StringBuffer(); | |
201 var first = true; | |
202 queryParameters.forEach((key, value) { | |
203 if (!first) { | |
204 result.write("&"); | |
205 } | |
206 first = false; | |
207 result.write(Uri.encodeQueryComponent(key)); | |
208 if (value != null && !value.isEmpty) { | |
209 result.write("="); | |
210 result.write(Uri.encodeQueryComponent(value)); | |
211 } | |
212 }); | |
213 return result.toString(); | |
214 } | |
215 | |
216 static String _makeFragment(String fragment) { | |
217 if (fragment == null) return ""; | |
218 return _normalize(fragment); | |
219 } | |
220 | |
221 static String _normalize(String component) { | |
222 bool isNormalizedHexDigit(int digit) { | |
223 return (_CharCode.ZERO <= digit && digit <= _CharCode.NINE) || | |
224 (_CharCode.UPPER_CASE_A <= digit && digit <= _CharCode.UPPER_CASE_F); | |
225 } | |
226 | |
227 bool isLowerCaseHexDigit(int digit) { | |
228 return _CharCode.LOWER_CASE_A <= digit && digit <= _CharCode.LOWER_CASE_F; | |
229 } | |
230 | |
231 bool isUnreserved(int ch) { | |
232 return ch < 128 && | |
233 ((_unreservedTable[ch >> 4] & (1 << (ch & 0x0f))) != 0); | |
234 } | |
235 | |
236 int normalizeHexDigit(int index) { | |
237 var codeUnit = component.codeUnitAt(index); | |
238 if (isLowerCaseHexDigit(codeUnit)) { | |
239 return codeUnit - 0x20; | |
240 } else if (!isNormalizedHexDigit(codeUnit)) { | |
241 throw new ArgumentError("Invalid URI component"); | |
242 } else { | |
243 return codeUnit; | |
244 } | |
245 } | |
246 | |
247 int decodeHexDigitPair(int index) { | |
248 int byte = 0; | |
249 for (int i = 0; i < 2; i++) { | |
250 var codeUnit = component.codeUnitAt(index + i); | |
251 if (_CharCode.ZERO <= codeUnit && codeUnit <= _CharCode.NINE) { | |
252 byte = byte * 16 + codeUnit - _CharCode.ZERO; | |
253 } else { | |
254 // Check ranges A-F (0x41-0x46) and a-f (0x61-0x66). | |
255 codeUnit |= 0x20; | |
256 if (_CharCode.LOWER_CASE_A <= codeUnit && | |
257 codeUnit <= _CharCode.LOWER_CASE_F) { | |
258 byte = byte * 16 + codeUnit - 0x57; | |
floitsch
2013/05/24 20:37:27
what's 0x57?
Søren Gjesse
2013/05/27 10:47:21
LOWERCASE_A - 10
Change the code to which should
| |
259 } else { | |
260 throw new ArgumentError("Invalid URI encoding"); | |
floitsch
2013/05/24 20:37:27
add reason.
Søren Gjesse
2013/05/27 10:47:21
Done.
| |
261 } | |
262 } | |
263 } | |
264 return byte; | |
265 } | |
266 | |
267 // Start building the normalized component string. | |
268 StringBuffer result; | |
269 int length = component.length; | |
270 int index = 0; | |
271 int prevIndex = 0; | |
272 while (index < length) { | |
273 | |
274 // Copy a part of the component string to the result. | |
275 fillResult() { | |
276 if (result == null) { | |
277 assert(prevIndex == 0); | |
278 result = new StringBuffer(component.substring(prevIndex, index)); | |
279 } else { | |
280 result.write(component.substring(prevIndex, index)); | |
281 } | |
282 } | |
283 | |
284 // Normalize percent encoding to uppercase and don't encode | |
285 // unreserved characters. | |
286 if (component.codeUnitAt(index) == _CharCode.PERCENT) { | |
287 if (length < index + 2) { | |
288 throw new ArgumentError("Invalid Uri component"); | |
floitsch
2013/05/24 20:37:27
add reason.
Søren Gjesse
2013/05/27 10:47:21
Done.
| |
289 } | |
290 | |
291 var codeUnit1 = component.codeUnitAt(index + 1); | |
292 var codeUnit2 = component.codeUnitAt(index + 2); | |
293 var decodedCodeUnit = decodeHexDigitPair(index + 1); | |
294 if (isNormalizedHexDigit(codeUnit1) && | |
295 isNormalizedHexDigit(codeUnit2) && | |
296 !isUnreserved(decodedCodeUnit)) { | |
297 index += 3; | |
298 } else { | |
299 fillResult(); | |
300 if (isUnreserved(decodedCodeUnit)) { | |
301 result.writeCharCode(decodedCodeUnit); | |
302 } else { | |
303 result.write("%"); | |
304 result.writeCharCode(normalizeHexDigit(index + 1)); | |
305 result.writeCharCode(normalizeHexDigit(index + 2)); | |
306 } | |
307 index += 3; | |
308 prevIndex = index; | |
309 } | |
310 } else { | |
311 index++; | |
312 } | |
313 } | |
314 assert(index == length); | |
315 | |
316 if (result == null) return component; | |
317 return result.toString(); | |
318 } | |
319 | |
320 static String _emptyIfNull(String val) => val != null ? val : ''; | |
321 | |
322 static int _parseIntOrZero(String val) { | |
323 if (val != null && val != '') { | |
324 return int.parse(val); | |
325 } else { | |
326 return 0; | |
327 } | |
328 } | |
329 | |
330 static String _eitherOf(String val1, String val2) { | |
331 if (val1 != null) return val1; | |
332 if (val2 != null) return val2; | |
333 return ''; | |
334 } | |
335 | |
336 // NOTE: This code was ported from: closure-library/closure/goog/uri/utils.js | |
337 static final RegExp _splitRe = new RegExp( | |
338 '^' | |
339 '(?:' | |
340 '([^:/?#.]+)' // scheme - ignore special characters | |
341 // used by other URL parts such as :, | |
342 // ?, /, #, and . | |
343 ':)?' | |
344 '(?://' | |
345 '(?:([^/?#]*)@)?' // userInfo | |
346 '(?:' | |
347 r'([\w\d\-\u0100-\uffff.%]*)' | |
348 // host - restrict to letters, | |
349 // digits, dashes, dots, percent | |
350 // escapes, and unicode characters. | |
351 '|' | |
352 // TODO(ajohnsen): Only allow a max number of parts? | |
353 r'\[([A-Fa-f0-9:.]*)\])' | |
354 // IPv6 host - restrict to hex, | |
355 // dot and colon. | |
356 '(?::([0-9]+))?' // port | |
357 ')?' | |
358 r'([^?#[]+)?' // path | |
359 r'(?:\?([^#]*))?' // query | |
360 '(?:#(.*))?' // fragment | |
361 r'$'); | |
362 | |
363 static const _COMPONENT_SCHEME = 1; | |
364 static const _COMPONENT_USER_INFO = 2; | |
365 static const _COMPONENT_HOST = 3; | |
366 static const _COMPONENT_HOST_IPV6 = 4; | |
367 static const _COMPONENT_PORT = 5; | |
368 static const _COMPONENT_PATH = 6; | |
369 static const _COMPONENT_QUERY_DATA = 7; | |
370 static const _COMPONENT_FRAGMENT = 8; | |
371 | |
372 /** | |
373 * Returns `true` if the URI is absolute. | |
374 */ | |
375 bool get isAbsolute { | |
376 if ("" == scheme) return false; | |
377 if ("" != fragment) return false; | |
378 return true; | |
379 | |
380 /* absolute-URI = scheme ":" hier-part [ "?" query ] | |
floitsch
2013/05/24 20:37:27
why is this comment here?
Søren Gjesse
2013/05/27 10:47:21
It was there in the dart:uri library. Removed it.
| |
381 * hier-part = "//" authority path-abempty | |
382 * / path-absolute | |
383 * / path-rootless | |
384 * / path-empty | |
385 * | |
386 * path = path-abempty ; begins with "/" or is empty | |
387 * / path-absolute ; begins with "/" but not "//" | |
388 * / path-noscheme ; begins with a non-colon segment | |
389 * / path-rootless ; begins with a segment | |
390 * / path-empty ; zero characters | |
391 * | |
392 * path-abempty = *( "/" segment ) | |
393 * path-absolute = "/" [ segment-nz *( "/" segment ) ] | |
394 * path-noscheme = segment-nz-nc *( "/" segment ) | |
395 * path-rootless = segment-nz *( "/" segment ) | |
396 * path-empty = 0<pchar> | |
397 * segment = *pchar | |
398 * segment-nz = 1*pchar | |
399 * segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" ) | |
400 * ; non-zero-length segment without any colon ":" | |
401 * | |
402 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" | |
403 */ | |
404 } | |
405 | |
406 Uri resolve(String uri) { | |
407 return resolveUri(Uri.parse(uri)); | |
408 } | |
409 | |
410 Uri resolveUri(Uri reference) { | |
411 // From RFC 3986. | |
412 String targetScheme; | |
413 String targetUserInfo; | |
414 String targetHost; | |
415 int targetPort; | |
416 String targetPath; | |
417 String targetQuery; | |
418 if (reference.scheme != "") { | |
419 targetScheme = reference.scheme; | |
420 targetUserInfo = reference.userInfo; | |
421 targetHost = reference.host; | |
422 targetPort = reference.port; | |
423 targetPath = removeDotSegments(reference.path); | |
424 targetQuery = reference.query; | |
425 } else { | |
426 if (reference.hasAuthority) { | |
427 targetUserInfo = reference.userInfo; | |
428 targetHost = reference.host; | |
429 targetPort = reference.port; | |
430 targetPath = removeDotSegments(reference.path); | |
431 targetQuery = reference.query; | |
432 } else { | |
433 if (reference.path == "") { | |
434 targetPath = this.path; | |
435 if (reference.query != "") { | |
436 targetQuery = reference.query; | |
437 } else { | |
438 targetQuery = this.query; | |
439 } | |
440 } else { | |
441 if (reference.path.startsWith("/")) { | |
442 targetPath = removeDotSegments(reference.path); | |
443 } else { | |
444 targetPath = removeDotSegments(merge(this.path, reference.path)); | |
445 } | |
446 targetQuery = reference.query; | |
447 } | |
448 targetUserInfo = this.userInfo; | |
449 targetHost = this.host; | |
450 targetPort = this.port; | |
451 } | |
452 targetScheme = this.scheme; | |
453 } | |
454 return new Uri(scheme: targetScheme, | |
455 userInfo: targetUserInfo, | |
floitsch
2013/05/24 20:37:27
indentation.
Søren Gjesse
2013/05/27 10:47:21
Done.
| |
456 host: targetHost, | |
457 port: targetPort, | |
458 path: targetPath, | |
459 query: targetQuery, | |
460 fragment: reference.fragment); | |
461 } | |
462 | |
463 bool get hasAuthority { | |
464 return (userInfo != "") || (host != "") || (port != 0); | |
floitsch
2013/05/24 20:37:27
why is the authority linked to a non-zery port?
Søren Gjesse
2013/05/27 10:47:21
Good catch!
An authority always have a host. Chan
| |
465 } | |
466 | |
467 /** | |
468 * For http/https schemes returns URI's [origin][] - scheme://host:port. | |
floitsch
2013/05/24 20:37:27
Returns the URI's [origin][] for http/https scheme
Søren Gjesse
2013/05/27 10:47:21
Done.
| |
469 * For all other schemes throws StateError. | |
470 * [origin]: http://www.w3.org/TR/2011/WD-html5-20110405/origin-0.html#origin | |
471 */ | |
472 String get origin { | |
473 if (scheme == "") { | |
474 throw new StateError("Cannot use origin without a scheme"); | |
floitsch
2013/05/24 20:37:27
give information: "Cannot use origin without a sch
Søren Gjesse
2013/05/27 10:47:21
Done.
| |
475 } | |
476 if (scheme != "http" && scheme != "https") { | |
477 throw new StateError( | |
478 "origin is applicable to http/https schemes only. Not \'$scheme\'"); | |
479 } | |
480 StringBuffer sb = new StringBuffer(); | |
481 sb.write(scheme); | |
482 sb.write(":"); | |
483 if (host == null || host == "") { | |
floitsch
2013/05/24 20:37:27
Move check up to the checks.
Søren Gjesse
2013/05/27 10:47:21
Done.
| |
484 throw new StateError("Cannot use origin without a host"); | |
485 } | |
486 | |
487 sb.write("//"); | |
488 sb.write(host); | |
489 if (port != 0) { | |
490 sb.write(":"); | |
491 sb.write(port); | |
492 } | |
493 return sb.toString(); | |
floitsch
2013/05/24 20:37:27
No need for StringBuffers.
if (port == 0) return "
Søren Gjesse
2013/05/27 10:47:21
Good point. Changed.
| |
494 } | |
495 | |
496 String toString() { | |
497 StringBuffer sb = new StringBuffer(); | |
498 _addIfNonEmpty(sb, scheme, scheme, ':'); | |
499 if (hasAuthority || (scheme == "file")) { | |
500 sb.write("//"); | |
501 _addIfNonEmpty(sb, userInfo, userInfo, "@"); | |
502 sb.write(host == null ? "null" : | |
503 host.contains(':') ? '[$host]' : host); | |
504 if (port != 0) { | |
505 sb.write(":"); | |
506 sb.write(port.toString()); | |
507 } | |
508 } | |
509 sb.write(path == null ? "null" : path); | |
floitsch
2013/05/24 20:37:27
isn't the path null-check redundant?
Søren Gjesse
2013/05/27 10:47:21
It is - removed.
| |
510 _addIfNonEmpty(sb, query, "?", query); | |
511 _addIfNonEmpty(sb, fragment, "#", fragment); | |
512 return sb.toString(); | |
513 } | |
514 | |
515 bool operator==(other) { | |
516 if (other is! Uri) return false; | |
517 Uri uri = other; | |
518 return scheme == uri.scheme && | |
519 userInfo == uri.userInfo && | |
520 host == uri.host && | |
521 port == uri.port && | |
522 path == uri.path && | |
523 query == uri.query && | |
524 fragment == uri.fragment; | |
525 } | |
526 | |
527 int get hashCode { | |
528 int combine(part, current) { | |
529 // The sum is truncated to 30 bits to make sure it fits into a Smi. | |
530 return (current * 31 + part.hashCode) & 0x3FFFFFFF; | |
531 } | |
532 return combine(scheme, combine(userInfo, combine(host, combine(port, | |
533 combine(path, combine(query, combine(fragment, 1))))))); | |
534 } | |
535 | |
536 static void _addIfNonEmpty(StringBuffer sb, String test, | |
537 String first, String second) { | |
538 if ("" != test) { | |
539 sb.write(first == null ? "null" : first); | |
540 sb.write(second == null ? "null" : second); | |
541 } | |
542 } | |
543 | |
544 /** | |
545 * A javaScript-like URI component encoder, this encodes a URI | |
546 * [component] by replacing each instance of certain characters by | |
floitsch
2013/05/24 20:37:27
If the set of characters is fixed, add it at the e
Søren Gjesse
2013/05/27 10:47:21
Done.
| |
547 * one, two, three, or four escape sequences representing the UTF-8 | |
548 * encoding of the character (will only be four escape sequences for | |
549 * characters composed of two "surrogate" characters). To avoid | |
550 * unexpected requests to the server, you should call | |
551 * Uri.encodeComponent on any user-entered parameters that will be | |
552 * passed as part of a URI. | |
553 * | |
554 * For encoding the query part consider using | |
555 * [encodeQueryComponent]. | |
556 * | |
557 * When manually encoding path segments or query components remember | |
558 * to encode each part separately before building the path or query | |
559 * string. | |
560 * | |
561 * To avoid the need for explicitly encoding use the [pathSegments] | |
562 * and [queryParameters] optional named arguments when constructing | |
563 * a Uri. | |
564 */ | |
565 static String encodeComponent(String component) { | |
566 return _uriEncode(_unreserved2396Table, component); | |
567 } | |
568 | |
569 /* | |
570 * Encode [component] according to the HTML 4.01 rules for encoding | |
571 * the posting of a HTML form as a query string component. Spaces | |
572 * will be replaced with plus and all characters except for | |
573 * uppercase and lowercase letters, decimal digits, hyphen, period, | |
574 * underscore and tilde will be encoded. Note that the set of | |
575 * characters encoded is a superset of what HTML 4.01 says as it | |
576 * refers to RFC 1738 for reserved characters. | |
577 * | |
578 * When manually encoding query components remember to encode each | |
579 * part separately before building the query string. | |
580 * | |
581 * See http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2 for more | |
floitsch
2013/05/24 20:37:27
I think there is a way to make this a link.
Søren Gjesse
2013/05/27 10:47:21
I will postpone the adding of proper links to a se
| |
582 * details. | |
583 */ | |
584 static String encodeQueryComponent(String component) { | |
585 return _uriEncode(_unreservedTable, component, spaceToPlus: true); | |
586 } | |
587 | |
588 /** | |
589 * A javaScript-like URI component decoder. | |
floitsch
2013/05/24 20:37:27
Is it JavaScript like, or more general? In other w
Søren Gjesse
2013/05/27 10:47:21
Rephrased the comment and explained more precisely
| |
590 */ | |
591 static String decodeComponent(String encodedComponent) { | |
592 return _uriDecode(encodedComponent); | |
593 } | |
594 | |
595 static String decodeQueryComponent(String encodedComponent) { | |
596 return _uriDecode(encodedComponent, plusToSpace: true); | |
597 } | |
598 | |
599 /** | |
600 * A JavaScript-like URI encoder. Encodes a full URI string by | |
601 * replacing each instance of certain characters by one, two, three, | |
602 * or four escape sequences representing the UTF-8 encoding of the | |
603 * character (will only be four escape sequences for characters | |
604 * composed of two "surrogate" characters). This assumes that [uri] | |
605 * is a complete URI, so does not encode reserved characters that | |
606 * have special meaning in the URI: [:#;,/?:@&=+\$:] It returns the | |
floitsch
2013/05/24 20:37:27
nit (personal preference): `#;,/?:@&=+\$`.
Søren Gjesse
2013/05/27 10:47:21
Done.
| |
607 * escaped URI. | |
608 */ | |
609 static String encodeFull(String uri) { | |
610 return _uriEncode(_encodeFullTable, uri); | |
611 } | |
612 | |
613 /** | |
614 * A JavaScript-like URI decoder. Decodes a full URI string. This is | |
floitsch
2013/05/24 20:37:27
Start with what it does: "Decodes ...".
Make one s
Søren Gjesse
2013/05/27 10:47:21
Rephrased the comment and explained more precisely
| |
615 * an implementation of JavaScript's decodeURIComponent function. | |
616 * Decodes a Uniform Resource Identifier [uri] previously created by | |
617 * encodeURI or by a similar routine. It replaces each escape sequence | |
618 * in [uri] with the character that it represents. | |
619 */ | |
620 static String decodeFull(String uri) { | |
621 return _uriDecode(uri); | |
622 } | |
623 | |
624 // Tables of char-codes organized as a bit vector of 128 bits where | |
625 // each bit indicate whether a character code on the 0-127 needs to | |
626 // be escaped or not. | |
627 | |
628 // The unreserved characters of RFC 3986. | |
629 static const _unreservedTable = const [ | |
630 // LSB MSB | |
631 // | | | |
632 0x0000, // 0x00 - 0x0f 0000000000000000 | |
633 0x0000, // 0x10 - 0x1f 0000000000000000 | |
634 // -. | |
635 0x6000, // 0x20 - 0x2f 0000000000000110 | |
636 // 0123456789 | |
637 0x03ff, // 0x30 - 0x3f 1111111111000000 | |
638 // ABCDEFGHIJKLMNO | |
639 0xfffe, // 0x40 - 0x4f 0111111111111111 | |
640 // PQRSTUVWXYZ _ | |
641 0x87ff, // 0x50 - 0x5f 1111111111100001 | |
642 // abcdefghijklmno | |
643 0xfffe, // 0x60 - 0x6f 0111111111111111 | |
644 // pqrstuvwxyz ~ | |
645 0x47ff]; // 0x70 - 0x7f 1111111111100010 | |
646 | |
647 // The unreserved characters of RFC 2396. | |
648 static const _unreserved2396Table = const [ | |
649 // LSB MSB | |
650 // | | | |
651 0x0000, // 0x00 - 0x0f 0000000000000000 | |
652 0x0000, // 0x10 - 0x1f 0000000000000000 | |
653 // ! '()* -. | |
654 0x6782, // 0x20 - 0x2f 0100000111100110 | |
655 // 0123456789 | |
656 0x03ff, // 0x30 - 0x3f 1111111111000000 | |
657 // ABCDEFGHIJKLMNO | |
658 0xfffe, // 0x40 - 0x4f 0111111111111111 | |
659 // PQRSTUVWXYZ _ | |
660 0x87ff, // 0x50 - 0x5f 1111111111100001 | |
661 // abcdefghijklmno | |
662 0xfffe, // 0x60 - 0x6f 0111111111111111 | |
663 // pqrstuvwxyz ~ | |
664 0x47ff]; // 0x70 - 0x7f 1111111111100010 | |
665 | |
666 // Table of reserved characters specified by ECMAScript 5. | |
667 static const _encodeFullTable = const [ | |
668 // LSB MSB | |
669 // | | | |
670 0x0000, // 0x00 - 0x0f 0000000000000000 | |
671 0x0000, // 0x10 - 0x1f 0000000000000000 | |
672 // ! #$ &'()*+,-./ | |
673 0xf7da, // 0x20 - 0x2f 0101101111101111 | |
674 // 0123456789:; = ? | |
675 0xafff, // 0x30 - 0x3f 1111111111110101 | |
676 // @ABCDEFGHIJKLMNO | |
677 0xffff, // 0x40 - 0x4f 1111111111111111 | |
678 // PQRSTUVWXYZ _ | |
679 0x87ff, // 0x50 - 0x5f 1111111111100001 | |
680 // abcdefghijklmno | |
681 0xfffe, // 0x60 - 0x6f 0111111111111111 | |
682 // pqrstuvwxyz ~ | |
683 0x47ff]; // 0x70 - 0x7f 1111111111100010 | |
684 | |
685 // Characters allowed in the scheme. | |
686 static const _schemeTable = const [ | |
687 // LSB MSB | |
688 // | | | |
689 0x0000, // 0x00 - 0x0f 0000000000000000 | |
690 0x0000, // 0x10 - 0x1f 0000000000000000 | |
691 // + -. | |
692 0x6800, // 0x20 - 0x2f 0000000000010110 | |
693 // 0123456789 | |
694 0x03ff, // 0x30 - 0x3f 1111111111000000 | |
695 // ABCDEFGHIJKLMNO | |
696 0xfffe, // 0x40 - 0x4f 0111111111111111 | |
697 // PQRSTUVWXYZ | |
698 0x07ff, // 0x50 - 0x5f 1111111111100001 | |
699 // abcdefghijklmno | |
700 0xfffe, // 0x60 - 0x6f 0111111111111111 | |
701 // pqrstuvwxyz | |
702 0x07ff]; // 0x70 - 0x7f 1111111111100010 | |
703 | |
704 // Characters allowed in scheme except for upper case letters. | |
705 static const _schemeLowerTable = const [ | |
706 // LSB MSB | |
707 // | | | |
708 0x0000, // 0x00 - 0x0f 0000000000000000 | |
709 0x0000, // 0x10 - 0x1f 0000000000000000 | |
710 // + -. | |
711 0x6800, // 0x20 - 0x2f 0000000000010110 | |
712 // 0123456789 | |
713 0x03ff, // 0x30 - 0x3f 1111111111000000 | |
714 // | |
715 0x0000, // 0x40 - 0x4f 0111111111111111 | |
716 // | |
717 0x0000, // 0x50 - 0x5f 1111111111100001 | |
718 // abcdefghijklmno | |
719 0xfffe, // 0x60 - 0x6f 0111111111111111 | |
720 // pqrstuvwxyz | |
721 0x07ff]; // 0x70 - 0x7f 1111111111100010 | |
722 | |
723 // Sub delimiter characters combined with unreserved as of 3986. | |
724 // sub-delims = "!" / "$" / "&" / "'" / "(" / ")" | |
725 // / "*" / "+" / "," / ";" / "=" | |
726 // RFC 3986 section 2.3. | |
727 // unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" | |
728 static const _subDelimitersTable = const [ | |
729 // LSB MSB | |
730 // | | | |
731 0x0000, // 0x00 - 0x0f 0000000000000000 | |
732 0x0000, // 0x10 - 0x1f 0000000000000000 | |
733 // ! $ &'()*+,-. | |
734 0x7fd2, // 0x20 - 0x2f 0100101111111110 | |
735 // 0123456789 ; = | |
736 0x2bff, // 0x30 - 0x3f 1111111111010100 | |
737 // ABCDEFGHIJKLMNO | |
738 0xfffe, // 0x40 - 0x4f 0111111111111111 | |
739 // PQRSTUVWXYZ _ | |
740 0x87ff, // 0x50 - 0x5f 1111111111100001 | |
741 // abcdefghijklmno | |
742 0xfffe, // 0x60 - 0x6f 0111111111111111 | |
743 // pqrstuvwxyz ~ | |
744 0x47ff]; // 0x70 - 0x7f 1111111111100010 | |
745 | |
746 // Characters allowed in the path as of RFC 3986. | |
747 // RFC 3986 section 3.3. | |
748 // pchar = unreserved / pct-encoded / sub-delims / ":" / "@" | |
749 static const _pathCharTable = const [ | |
750 // LSB MSB | |
751 // | | | |
752 0x0000, // 0x00 - 0x0f 0000000000000000 | |
753 0x0000, // 0x10 - 0x1f 0000000000000000 | |
754 // ! $ &'()*+,-. | |
755 0x7fd2, // 0x20 - 0x2f 0100101111111110 | |
756 // 0123456789:; = | |
757 0x2fff, // 0x30 - 0x3f 1111111111110100 | |
758 // @ABCDEFGHIJKLMNO | |
759 0xffff, // 0x40 - 0x4f 1111111111111111 | |
760 // PQRSTUVWXYZ _ | |
761 0x87ff, // 0x50 - 0x5f 1111111111100001 | |
762 // abcdefghijklmno | |
763 0xfffe, // 0x60 - 0x6f 0111111111111111 | |
764 // pqrstuvwxyz ~ | |
765 0x47ff]; // 0x70 - 0x7f 1111111111100010 | |
766 | |
767 // Characters allowed in the query as of RFC 3986. | |
768 // RFC 3986 section 3.4. | |
769 // query = *( pchar / "/" / "?" ) | |
770 static const _queryCharTable = const [ | |
771 // LSB MSB | |
772 // | | | |
773 0x0000, // 0x00 - 0x0f 0000000000000000 | |
774 0x0000, // 0x10 - 0x1f 0000000000000000 | |
775 // ! $ &'()*+,-./ | |
776 0xffd2, // 0x20 - 0x2f 0100101111111111 | |
777 // 0123456789:; = ? | |
778 0xafff, // 0x30 - 0x3f 1111111111110101 | |
779 // @ABCDEFGHIJKLMNO | |
780 0xffff, // 0x40 - 0x4f 1111111111111111 | |
781 // PQRSTUVWXYZ _ | |
782 0x87ff, // 0x50 - 0x5f 1111111111100001 | |
783 // abcdefghijklmno | |
784 0xfffe, // 0x60 - 0x6f 0111111111111111 | |
785 // pqrstuvwxyz ~ | |
786 0x47ff]; // 0x70 - 0x7f 1111111111100010 | |
787 } | |
OLD | NEW |