Chromium Code Reviews| OLD | NEW |
|---|---|
| 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:** |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 69 | 69 |
| 70 /** | 70 /** |
| 71 * Cache the computed return value of [pathSegements]. | 71 * Cache the computed return value of [pathSegements]. |
| 72 */ | 72 */ |
| 73 List<String> _pathSegments; | 73 List<String> _pathSegments; |
| 74 | 74 |
| 75 /** | 75 /** |
| 76 * Cache the computed return value of [queryParameters]. | 76 * Cache the computed return value of [queryParameters]. |
| 77 */ | 77 */ |
| 78 Map<String, String> _queryParameters; | 78 Map<String, String> _queryParameters; |
| 79 Map<String, List<String>> _queryParameterLists; | |
|
Lasse Reichstein Nielsen
2015/12/14 13:16:54
I'm wondering if these can share something, but I
floitsch
2015/12/14 17:24:54
I don't really like expandos, but they would save
| |
| 79 | 80 |
| 80 /// Internal non-verifying constructor. Only call with validated arguments. | 81 /// Internal non-verifying constructor. Only call with validated arguments. |
| 81 Uri._internal(this.scheme, | 82 Uri._internal(this.scheme, |
| 82 this._userInfo, | 83 this._userInfo, |
| 83 this._host, | 84 this._host, |
| 84 this._port, | 85 this._port, |
| 85 this._path, | 86 this._path, |
| 86 this._query, | 87 this._query, |
| 87 this._fragment); | 88 this._fragment); |
| 88 | 89 |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 129 * list of characters: `!$&'()*+,;=:@`. If the other components | 130 * list of characters: `!$&'()*+,;=:@`. If the other components |
| 130 * calls for an absolute path a leading slash `/` is prepended if | 131 * calls for an absolute path a leading slash `/` is prepended if |
| 131 * not already there. | 132 * not already there. |
| 132 * | 133 * |
| 133 * The query component is set through either [query] or | 134 * The query component is set through either [query] or |
| 134 * [queryParameters]. When [query] is used the provided string should | 135 * [queryParameters]. When [query] is used the provided string should |
| 135 * be a valid URI query, but invalid characters other than general delimiters, | 136 * be a valid URI query, but invalid characters other than general delimiters, |
| 136 * will be escaped if necessary. | 137 * will be escaped if necessary. |
| 137 * When [queryParameters] is used the query is built from the | 138 * When [queryParameters] is used the query is built from the |
| 138 * provided map. Each key and value in the map is percent-encoded | 139 * provided map. Each key and value in the map is percent-encoded |
| 139 * and joined using equal and ampersand characters. The | 140 * and joined using equal and ampersand characters. |
| 140 * percent-encoding of the keys and values encodes all characters | 141 * A value in the map must be either a string, or a an [Iterable] of strings, |
| 141 * except for the unreserved characters. | 142 * where the latter corresponds to multiple values for the same key. |
| 143 * The percent-encoding of the keys and values encodes all characters | |
| 144 * except for the unreserved characters, and replaces spaces with `+`. | |
| 142 * If `query` is the empty string, it is equivalent to omitting it. | 145 * If `query` is the empty string, it is equivalent to omitting it. |
| 143 * To have an actual empty query part, | 146 * To have an actual empty query part, |
| 144 * use an empty list for `queryParameters`. | 147 * use an empty list for `queryParameters`. |
| 145 * If both `query` and `queryParameters` are omitted or `null`, the | 148 * If both `query` and `queryParameters` are omitted or `null`, |
| 146 * URI will have no query part. | 149 * the URI will have no query part. |
| 147 * | 150 * |
| 148 * The fragment component is set through [fragment]. | 151 * The fragment component is set through [fragment]. |
| 149 * It should be a valid URI fragment, but invalid characters other than | 152 * It should be a valid URI fragment, but invalid characters other than |
| 150 * general delimiters, will be escaped if necessary. | 153 * general delimiters, will be escaped if necessary. |
| 151 * If `fragment` is omitted or `null`, the URI will have no fragment part. | 154 * If `fragment` is omitted or `null`, the URI will have no fragment part. |
| 152 */ | 155 */ |
| 153 factory Uri({String scheme : "", | 156 factory Uri({String scheme : "", |
| 154 String userInfo : "", | 157 String userInfo : "", |
| 155 String host, | 158 String host, |
| 156 int port, | 159 int port, |
| 157 String path, | 160 String path, |
| 158 Iterable<String> pathSegments, | 161 Iterable<String> pathSegments, |
| 159 String query, | 162 String query, |
| 160 Map<String, String> queryParameters, | 163 Map<String, dynamic> queryParameters, |
| 161 String fragment}) { | 164 String fragment}) { |
| 162 scheme = _makeScheme(scheme, 0, _stringOrNullLength(scheme)); | 165 scheme = _makeScheme(scheme, 0, _stringOrNullLength(scheme)); |
| 163 userInfo = _makeUserInfo(userInfo, 0, _stringOrNullLength(userInfo)); | 166 userInfo = _makeUserInfo(userInfo, 0, _stringOrNullLength(userInfo)); |
| 164 host = _makeHost(host, 0, _stringOrNullLength(host), false); | 167 host = _makeHost(host, 0, _stringOrNullLength(host), false); |
| 165 // Special case this constructor for backwards compatibility. | 168 // Special case this constructor for backwards compatibility. |
| 166 if (query == "") query = null; | 169 if (query == "") query = null; |
| 167 query = _makeQuery(query, 0, _stringOrNullLength(query), queryParameters); | 170 query = _makeQuery(query, 0, _stringOrNullLength(query), queryParameters); |
| 168 fragment = _makeFragment(fragment, 0, _stringOrNullLength(fragment)); | 171 fragment = _makeFragment(fragment, 0, _stringOrNullLength(fragment)); |
| 169 port = _makePort(port, scheme); | 172 port = _makePort(port, scheme); |
| 170 bool isFile = (scheme == "file"); | 173 bool isFile = (scheme == "file"); |
| (...skipping 926 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1097 : new List<String>.unmodifiable( | 1100 : new List<String>.unmodifiable( |
| 1098 pathToSplit.split("/").map(Uri.decodeComponent)); | 1101 pathToSplit.split("/").map(Uri.decodeComponent)); |
| 1099 _pathSegments = result; | 1102 _pathSegments = result; |
| 1100 return result; | 1103 return result; |
| 1101 } | 1104 } |
| 1102 | 1105 |
| 1103 /** | 1106 /** |
| 1104 * Returns the URI query split into a map according to the rules | 1107 * Returns the URI query split into a map according to the rules |
| 1105 * specified for FORM post in the [HTML 4.01 specification section | 1108 * specified for FORM post in the [HTML 4.01 specification section |
| 1106 * 17.13.4](http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.13.4 "HTM L 4.01 section 17.13.4"). | 1109 * 17.13.4](http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.13.4 "HTM L 4.01 section 17.13.4"). |
| 1110 * Each key and value in the returned map has been decoded. | |
| 1111 * If there is no query the empty map is returned. | |
| 1112 * | |
| 1113 * Keys in the query string that have no value are mapped to the | |
| 1114 * empty string. | |
| 1115 * If a key occurs more than once in the query string, it is mapped to | |
| 1116 * one of the values. The [queryParameterValues] getter can provide a | |
| 1117 * map that maps to all the values. | |
| 1118 * | |
| 1119 * The returned map is unmodifiable and will throw [UnsupportedError] on any | |
| 1120 * calls that would mutate it. | |
| 1121 */ | |
| 1122 Map<String, String> get queryParameters { | |
| 1123 if (_queryParameters == null) { | |
| 1124 _queryParameters = | |
| 1125 new UnmodifiableMapView<String, String>(splitQueryString(query)); | |
| 1126 } | |
| 1127 return _queryParameters; | |
| 1128 } | |
| 1129 | |
| 1130 /** | |
| 1131 * Returns the URI query split into a map according to the rules | |
| 1132 * specified for FORM post in the [HTML 4.01 specification section | |
| 1133 * 17.13.4](http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.13.4 "HTM L 4.01 section 17.13.4"). | |
| 1107 * Each key and value in the returned map has been decoded. If there is no | 1134 * Each key and value in the returned map has been decoded. If there is no |
| 1108 * query the empty map is returned. | 1135 * query the empty map is returned. |
| 1109 * | 1136 * |
| 1110 * Keys in the query string that have no value are mapped to the | 1137 * Keys are mapped to lists of their values. If a key occurs only once, |
| 1111 * empty string. | 1138 * its value is a singleton list. If a key occurs with no value, the |
| 1139 * empty string is used as the value for that occurrence. | |
| 1112 * | 1140 * |
| 1113 * The returned map is unmodifiable and will throw [UnsupportedError] on any | 1141 * The returned map and the lists it contains are unmodifiable |
| 1114 * calls that would mutate it. | 1142 * and will throw [UnsupportedError] on any calls that would mutate them. |
| 1115 */ | 1143 */ |
| 1116 Map<String, String> get queryParameters { | 1144 Map<String, List<String>> get queryParameterLists { |
|
Lasse Reichstein Nielsen
2015/12/14 13:16:54
I'm not particularly happy with the name. Suggesti
floitsch
2015/12/14 17:24:54
multiQueryParameters?
Søren Gjesse
2015/12/15 07:27:42
+1
Lasse Reichstein Nielsen
2015/12/15 08:11:28
I don't think that works very well. It sounds like
floitsch
2015/12/15 09:17:52
alternatively we could go the long way:
queryPara
Lasse Reichstein Nielsen
2015/12/16 13:06:21
Longer could do it, but "with duplicates" doesn't
Lasse Reichstein Nielsen
2016/01/12 13:34:12
Going with queryParametersAll - which isn't perfec
| |
| 1117 if (_queryParameters == null) { | 1145 if (_queryParameterLists == null) { |
| 1118 _queryParameters = new UnmodifiableMapView(splitQueryString(query)); | 1146 Map queryParameterLists = _splitQueryStringAll(query); |
| 1147 for (var key in queryParameterLists.keys) { | |
| 1148 queryParameterLists[key] = | |
| 1149 new List<String>.unmodifiable(queryParameterLists[key]); | |
| 1150 } | |
| 1151 _queryParameterLists = new Map<String, List<String>>.unmodifiable( | |
| 1152 queryParameterLists); | |
| 1119 } | 1153 } |
| 1120 return _queryParameters; | 1154 return _queryParameterLists; |
| 1121 } | 1155 } |
| 1122 | 1156 |
| 1123 /** | 1157 /** |
| 1124 * Returns a URI where the path has been normalized. | 1158 * Returns a URI where the path has been normalized. |
| 1125 * | 1159 * |
| 1126 * A normalized path does not contain `.` segments or non-leading `..` | 1160 * A normalized path does not contain `.` segments or non-leading `..` |
| 1127 * segments. | 1161 * segments. |
| 1128 * Only a relative path with no scheme or authority may contain | 1162 * Only a relative path with no scheme or authority may contain |
| 1129 * leading `..` segments, | 1163 * leading `..` segments, |
| 1130 * a path that starts with `/` will also drop any leading `..` segments. | 1164 * a path that starts with `/` will also drop any leading `..` segments. |
| (...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1343 } | 1377 } |
| 1344 if (query != null) return _normalize(query, start, end, _queryCharTable); | 1378 if (query != null) return _normalize(query, start, end, _queryCharTable); |
| 1345 | 1379 |
| 1346 var result = new StringBuffer(); | 1380 var result = new StringBuffer(); |
| 1347 var first = true; | 1381 var first = true; |
| 1348 queryParameters.forEach((key, value) { | 1382 queryParameters.forEach((key, value) { |
| 1349 if (!first) { | 1383 if (!first) { |
| 1350 result.write("&"); | 1384 result.write("&"); |
| 1351 } | 1385 } |
| 1352 first = false; | 1386 first = false; |
| 1353 result.write(Uri.encodeQueryComponent(key)); | 1387 if (value == null) { |
| 1354 if (value != null && !value.isEmpty) { | 1388 result.write(Uri.encodeQueryComponent(key)); |
| 1355 result.write("="); | 1389 } else if (value is String) { |
| 1356 result.write(Uri.encodeQueryComponent(value)); | 1390 result.write(Uri.encodeQueryComponent(key)); |
| 1391 if (value.isNotEmpty) { | |
| 1392 result.write("="); | |
| 1393 result.write(Uri.encodeQueryComponent(value)); | |
| 1394 } | |
| 1395 } else { | |
| 1396 Iterable values = value; | |
| 1397 bool first = true; | |
| 1398 for (String value in values) { | |
| 1399 if (!first) { | |
| 1400 result.write("&"); | |
| 1401 } | |
| 1402 first = false; | |
| 1403 result.write(Uri.encodeQueryComponent(key)); | |
| 1404 if (value != null && value.isNotEmpty) { | |
| 1405 result.write("="); | |
| 1406 result.write(Uri.encodeQueryComponent(value)); | |
| 1407 } | |
| 1408 } | |
|
Lasse Reichstein Nielsen
2015/12/14 13:16:54
Maybe I should extract some of this into a helper
Lasse Reichstein Nielsen
2016/01/12 13:34:11
Done.
| |
| 1357 } | 1409 } |
| 1358 }); | 1410 }); |
| 1359 return result.toString(); | 1411 return result.toString(); |
| 1360 } | 1412 } |
| 1361 | 1413 |
| 1362 static String _makeFragment(String fragment, int start, int end) { | 1414 static String _makeFragment(String fragment, int start, int end) { |
| 1363 if (fragment == null) return null; | 1415 if (fragment == null) return null; |
| 1364 return _normalize(fragment, start, end, _queryCharTable); | 1416 return _normalize(fragment, start, end, _queryCharTable); |
| 1365 } | 1417 } |
| 1366 | 1418 |
| (...skipping 782 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2149 } else if (index != 0) { | 2201 } else if (index != 0) { |
| 2150 var key = element.substring(0, index); | 2202 var key = element.substring(0, index); |
| 2151 var value = element.substring(index + 1); | 2203 var value = element.substring(index + 1); |
| 2152 map[Uri.decodeQueryComponent(key, encoding: encoding)] = | 2204 map[Uri.decodeQueryComponent(key, encoding: encoding)] = |
| 2153 decodeQueryComponent(value, encoding: encoding); | 2205 decodeQueryComponent(value, encoding: encoding); |
| 2154 } | 2206 } |
| 2155 return map; | 2207 return map; |
| 2156 }); | 2208 }); |
| 2157 } | 2209 } |
| 2158 | 2210 |
| 2211 static List _createList() => []; | |
| 2212 | |
| 2213 static Map _splitQueryStringAll( | |
|
Lasse Reichstein Nielsen
2015/12/14 13:16:54
This one is private, while splitQueryString is pub
| |
| 2214 String query, {Encoding encoding: UTF8}) { | |
| 2215 Map result = {}; | |
| 2216 int i = 0; | |
| 2217 int start = 0; | |
| 2218 int equalsIndex = -1; | |
| 2219 void parsePair(int start, int equalsIndex, int end) { | |
| 2220 String key; | |
| 2221 String value; | |
| 2222 if (start == end) return; | |
| 2223 if (equalsIndex < 0) { | |
| 2224 key = _uriDecode(query, start, end, encoding, true); | |
| 2225 value = ""; | |
| 2226 } else { | |
| 2227 key = | |
| 2228 _uriDecode(query, start, equalsIndex, encoding, true); | |
| 2229 value = | |
| 2230 _uriDecode(query, equalsIndex + 1, end, encoding, true); | |
| 2231 } | |
| 2232 result.putIfAbsent(key, _createList).add(value); | |
| 2233 } | |
| 2234 const int _equals = 0x3d; | |
| 2235 const int _ampersand = 0x26; | |
| 2236 while (i < query.length) { | |
| 2237 int char = query.codeUnitAt(i); | |
| 2238 if (char == _equals) { | |
| 2239 if (equalsIndex < 0) equalsIndex = i; | |
| 2240 } else if (char == _ampersand) { | |
| 2241 parsePair(start, equalsIndex, i); | |
| 2242 start = i + 1; | |
| 2243 equalsIndex = -1; | |
| 2244 } | |
| 2245 i++; | |
| 2246 } | |
| 2247 parsePair(start, equalsIndex, i); | |
| 2248 return result; | |
| 2249 } | |
| 2250 | |
| 2159 /** | 2251 /** |
| 2160 * Parse the [host] as an IP version 4 (IPv4) address, returning the address | 2252 * Parse the [host] as an IP version 4 (IPv4) address, returning the address |
| 2161 * as a list of 4 bytes in network byte order (big endian). | 2253 * as a list of 4 bytes in network byte order (big endian). |
| 2162 * | 2254 * |
| 2163 * Throws a [FormatException] if [host] is not a valid IPv4 address | 2255 * Throws a [FormatException] if [host] is not a valid IPv4 address |
| 2164 * representation. | 2256 * representation. |
| 2165 */ | 2257 */ |
| 2166 static List<int> parseIPv4Address(String host) { | 2258 static List<int> parseIPv4Address(String host) { |
| 2167 void error(String msg) { | 2259 void error(String msg) { |
| 2168 throw new FormatException('Illegal IPv4 address, $msg'); | 2260 throw new FormatException('Illegal IPv4 address, $msg'); |
| (...skipping 1088 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3257 // All non-escape RFC-2396 uric characters. | 3349 // All non-escape RFC-2396 uric characters. |
| 3258 // | 3350 // |
| 3259 // uric = reserved | unreserved | escaped | 3351 // uric = reserved | unreserved | escaped |
| 3260 // reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," | 3352 // reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," |
| 3261 // unreserved = alphanum | mark | 3353 // unreserved = alphanum | mark |
| 3262 // mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" | 3354 // mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" |
| 3263 // | 3355 // |
| 3264 // This is the same characters as in a URI query (which is URI pchar plus '?') | 3356 // This is the same characters as in a URI query (which is URI pchar plus '?') |
| 3265 static const _uricTable = Uri._queryCharTable; | 3357 static const _uricTable = Uri._queryCharTable; |
| 3266 } | 3358 } |
| OLD | NEW |