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 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
129 * list of characters: `!$&'()*+,;=:@`. If the other components | 129 * list of characters: `!$&'()*+,;=:@`. If the other components |
130 * calls for an absolute path a leading slash `/` is prepended if | 130 * calls for an absolute path a leading slash `/` is prepended if |
131 * not already there. | 131 * not already there. |
132 * | 132 * |
133 * The query component is set through either [query] or | 133 * The query component is set through either [query] or |
134 * [queryParameters]. When [query] is used the provided string should | 134 * [queryParameters]. When [query] is used the provided string should |
135 * be a valid URI query, but invalid characters other than general delimiters, | 135 * be a valid URI query, but invalid characters other than general delimiters, |
136 * will be escaped if necessary. | 136 * will be escaped if necessary. |
137 * When [queryParameters] is used the query is built from the | 137 * When [queryParameters] is used the query is built from the |
138 * provided map. Each key and value in the map is percent-encoded | 138 * provided map. Each key and value in the map is percent-encoded |
139 * and joined using equal and ampersand characters. The | 139 * and joined using equal and ampersand characters. |
140 * percent-encoding of the keys and values encodes all characters | 140 * A value in the map must be either a string, or a an [Iterable] of strings, |
141 * except for the unreserved characters. | 141 * where the latter corresponds to multiple values for the same key. |
142 * The percent-encoding of the keys and values encodes all characters | |
143 * except for the unreserved characters, and replaces spaces with `+`. | |
142 * If `query` is the empty string, it is equivalent to omitting it. | 144 * If `query` is the empty string, it is equivalent to omitting it. |
143 * To have an actual empty query part, | 145 * To have an actual empty query part, |
144 * use an empty list for `queryParameters`. | 146 * use an empty list for `queryParameters`. |
145 * If both `query` and `queryParameters` are omitted or `null`, the | 147 * If both `query` and `queryParameters` are omitted or `null`, |
146 * URI will have no query part. | 148 * the URI will have no query part. |
147 * | 149 * |
148 * The fragment component is set through [fragment]. | 150 * The fragment component is set through [fragment]. |
149 * It should be a valid URI fragment, but invalid characters other than | 151 * It should be a valid URI fragment, but invalid characters other than |
150 * general delimiters, will be escaped if necessary. | 152 * general delimiters, will be escaped if necessary. |
151 * If `fragment` is omitted or `null`, the URI will have no fragment part. | 153 * If `fragment` is omitted or `null`, the URI will have no fragment part. |
152 */ | 154 */ |
153 factory Uri({String scheme : "", | 155 factory Uri({String scheme : "", |
154 String userInfo : "", | 156 String userInfo : "", |
155 String host, | 157 String host, |
156 int port, | 158 int port, |
157 String path, | 159 String path, |
158 Iterable<String> pathSegments, | 160 Iterable<String> pathSegments, |
159 String query, | 161 String query, |
160 Map<String, String> queryParameters, | 162 Map<String, dynamic> queryParameters, |
161 String fragment}) { | 163 String fragment}) { |
162 scheme = _makeScheme(scheme, 0, _stringOrNullLength(scheme)); | 164 scheme = _makeScheme(scheme, 0, _stringOrNullLength(scheme)); |
163 userInfo = _makeUserInfo(userInfo, 0, _stringOrNullLength(userInfo)); | 165 userInfo = _makeUserInfo(userInfo, 0, _stringOrNullLength(userInfo)); |
164 host = _makeHost(host, 0, _stringOrNullLength(host), false); | 166 host = _makeHost(host, 0, _stringOrNullLength(host), false); |
165 // Special case this constructor for backwards compatibility. | 167 // Special case this constructor for backwards compatibility. |
166 if (query == "") query = null; | 168 if (query == "") query = null; |
167 query = _makeQuery(query, 0, _stringOrNullLength(query), queryParameters); | 169 query = _makeQuery(query, 0, _stringOrNullLength(query), queryParameters); |
168 fragment = _makeFragment(fragment, 0, _stringOrNullLength(fragment)); | 170 fragment = _makeFragment(fragment, 0, _stringOrNullLength(fragment)); |
169 port = _makePort(port, scheme); | 171 port = _makePort(port, scheme); |
170 bool isFile = (scheme == "file"); | 172 bool isFile = (scheme == "file"); |
(...skipping 926 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1097 : new List<String>.unmodifiable( | 1099 : new List<String>.unmodifiable( |
1098 pathToSplit.split("/").map(Uri.decodeComponent)); | 1100 pathToSplit.split("/").map(Uri.decodeComponent)); |
1099 _pathSegments = result; | 1101 _pathSegments = result; |
1100 return result; | 1102 return result; |
1101 } | 1103 } |
1102 | 1104 |
1103 /** | 1105 /** |
1104 * Returns the URI query split into a map according to the rules | 1106 * Returns the URI query split into a map according to the rules |
1105 * specified for FORM post in the [HTML 4.01 specification section | 1107 * 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"). | 1108 * 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 * Each key and value in the returned map has been decoded. | |
1110 * If there is no query the empty map is returned. | |
1111 * | |
1112 * Keys in the query string that have no value are mapped to the | |
1113 * empty string. | |
1114 * If a key occurs more than once in the query string, it is mapped to | |
1115 * one of the values. The [queryParameterValues] getter can provide a | |
floitsch
2015/12/11 19:00:58
which one?
Lasse Reichstein Nielsen
2016/01/12 13:34:11
I really don't want to promise that.
It's likely g
| |
1116 * map that maps to all the values. | |
1117 * | |
1118 * The returned map is unmodifiable and will throw [UnsupportedError] on any | |
1119 * calls that would mutate it. | |
1120 */ | |
1121 Map<String, String> get queryParameters { | |
1122 if (_queryParameters == null) { | |
1123 _queryParameters = | |
1124 new UnmodifiableMapView<String, String>(splitQueryString(query)); | |
1125 } | |
1126 return _queryParameters; | |
1127 } | |
1128 | |
1129 /** | |
1130 * Returns the URI query split into a map according to the rules | |
1131 * specified for FORM post in the [HTML 4.01 specification section | |
1132 * 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 | 1133 * Each key and value in the returned map has been decoded. If there is no |
1108 * query the empty map is returned. | 1134 * query the empty map is returned. |
1109 * | 1135 * |
1110 * Keys in the query string that have no value are mapped to the | 1136 * Keys are mapped to lists of their values. If a key occurs only once, |
1111 * empty string. | 1137 * its value is a singleton list. If a key occurs with no value, the |
1138 * empty string is used as the value for that occurrence. | |
1112 * | 1139 * |
1113 * The returned map is unmodifiable and will throw [UnsupportedError] on any | 1140 * The returned map and the lists it contains are unmodifiable |
1114 * calls that would mutate it. | 1141 * and will throw [UnsupportedError] on any calls that would mutate them. |
1115 */ | 1142 */ |
1116 Map<String, String> get queryParameters { | 1143 Map<String, List<String>> get queryParameterLists { |
1117 if (_queryParameters == null) { | 1144 if (_queryParameterLists == null) { |
1118 _queryParameters = new UnmodifiableMapView(splitQueryString(query)); | 1145 Map queryParameterLists = _splitQueryStringAll(query); |
1146 for (var key in queryParameterLists.keys) { | |
1147 queryParameterLists[key] = | |
1148 new List<String>.unmodifiable(queryParameterLists[key]); | |
1149 } | |
1150 _queryParameterLists = new Map<String, List<String>>.unmodifiable( | |
1151 queryParameterLists); | |
1119 } | 1152 } |
1120 return _queryParameters; | 1153 return _queryParameterLists; |
1121 } | 1154 } |
1122 | 1155 |
1123 /** | 1156 /** |
1124 * Returns a URI where the path has been normalized. | 1157 * Returns a URI where the path has been normalized. |
1125 * | 1158 * |
1126 * A normalized path does not contain `.` segments or non-leading `..` | 1159 * A normalized path does not contain `.` segments or non-leading `..` |
1127 * segments. | 1160 * segments. |
1128 * Only a relative path with no scheme or authority may contain | 1161 * Only a relative path with no scheme or authority may contain |
1129 * leading `..` segments, | 1162 * leading `..` segments, |
1130 * a path that starts with `/` will also drop any leading `..` segments. | 1163 * 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 } | 1376 } |
1344 if (query != null) return _normalize(query, start, end, _queryCharTable); | 1377 if (query != null) return _normalize(query, start, end, _queryCharTable); |
1345 | 1378 |
1346 var result = new StringBuffer(); | 1379 var result = new StringBuffer(); |
1347 var first = true; | 1380 var first = true; |
1348 queryParameters.forEach((key, value) { | 1381 queryParameters.forEach((key, value) { |
1349 if (!first) { | 1382 if (!first) { |
1350 result.write("&"); | 1383 result.write("&"); |
1351 } | 1384 } |
1352 first = false; | 1385 first = false; |
1353 result.write(Uri.encodeQueryComponent(key)); | 1386 if (value == null) { |
Søren Gjesse
2015/12/14 07:41:00
Maybe this is OK, but if value is null or the empt
Lasse Reichstein Nielsen
2015/12/14 13:13:12
I think a "foo":[] entry should not introduce a "f
| |
1354 if (value != null && !value.isEmpty) { | 1387 result.write(Uri.encodeQueryComponent(key)); |
1355 result.write("="); | 1388 } else if (value is String) { |
1356 result.write(Uri.encodeQueryComponent(value)); | 1389 result.write(Uri.encodeQueryComponent(key)); |
1390 if (value.isNotEmpty) { | |
1391 result.write("="); | |
1392 result.write(Uri.encodeQueryComponent(value)); | |
1393 } | |
1394 } else { | |
1395 Iterable values = value; | |
1396 for (String value in values) { | |
1397 result.write(Uri.encodeQueryComponent(key)); | |
1398 if (value != null && value.isNotEmpty) { | |
1399 result.write("="); | |
1400 result.write(Uri.encodeQueryComponent(value)); | |
1401 } | |
1402 } | |
1357 } | 1403 } |
1358 }); | 1404 }); |
1359 return result.toString(); | 1405 return result.toString(); |
1360 } | 1406 } |
1361 | 1407 |
1362 static String _makeFragment(String fragment, int start, int end) { | 1408 static String _makeFragment(String fragment, int start, int end) { |
1363 if (fragment == null) return null; | 1409 if (fragment == null) return null; |
1364 return _normalize(fragment, start, end, _queryCharTable); | 1410 return _normalize(fragment, start, end, _queryCharTable); |
1365 } | 1411 } |
1366 | 1412 |
(...skipping 782 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2149 } else if (index != 0) { | 2195 } else if (index != 0) { |
2150 var key = element.substring(0, index); | 2196 var key = element.substring(0, index); |
2151 var value = element.substring(index + 1); | 2197 var value = element.substring(index + 1); |
2152 map[Uri.decodeQueryComponent(key, encoding: encoding)] = | 2198 map[Uri.decodeQueryComponent(key, encoding: encoding)] = |
2153 decodeQueryComponent(value, encoding: encoding); | 2199 decodeQueryComponent(value, encoding: encoding); |
2154 } | 2200 } |
2155 return map; | 2201 return map; |
2156 }); | 2202 }); |
2157 } | 2203 } |
2158 | 2204 |
2205 static List _createList() => []; | |
2206 | |
2207 static Map _splitQueryStringAll( | |
2208 String query, {Encoding encoding: UTF8}) { | |
2209 Map result = {}; | |
2210 int i = 0; | |
2211 int start = 0; | |
2212 int equalsIndex = -1; | |
2213 void parsePair(int start, int equalsIndex, int end) { | |
floitsch
2015/12/11 19:00:58
new line before and after nested functions.
Lasse Reichstein Nielsen
2016/01/12 13:34:11
Done.
| |
2214 String key; | |
2215 String value; | |
2216 if (equalsIndex < 0) { | |
2217 key = _uriDecode(encodedComponent, start, end, encoding, true); | |
2218 value = ""; | |
2219 } else { | |
2220 key = | |
2221 _uriDecode(encodedComponent, start, equalsIndex, encoding, true); | |
floitsch
2015/12/11 19:00:58
nit: it looks like this would fit on the previous
Lasse Reichstein Nielsen
2016/01/12 13:34:11
It does. Incredible!
| |
2222 value = | |
2223 _uriDecode(encodedComponent, equalsIndex + 1, end, encoding, true); | |
2224 } | |
2225 result.putIfAbsent(key, _createList).add(value); | |
2226 } | |
2227 while (i < query.length) { | |
2228 int char = query.codeUnitAt(i); | |
2229 if (char == _equals && equalsIndex < 0) { | |
2230 equalsIndex = i; | |
2231 } else if (i == _ampersand) { | |
2232 parsePair(start, equalsIndex, i); | |
2233 start = i + 1; | |
2234 equalsIndex = -1; | |
2235 } | |
2236 i++; | |
2237 } | |
2238 parsePair(start, equalsIndex, i); | |
2239 return result; | |
2240 } | |
2241 | |
2159 /** | 2242 /** |
2160 * Parse the [host] as an IP version 4 (IPv4) address, returning the address | 2243 * 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). | 2244 * as a list of 4 bytes in network byte order (big endian). |
2162 * | 2245 * |
2163 * Throws a [FormatException] if [host] is not a valid IPv4 address | 2246 * Throws a [FormatException] if [host] is not a valid IPv4 address |
2164 * representation. | 2247 * representation. |
2165 */ | 2248 */ |
2166 static List<int> parseIPv4Address(String host) { | 2249 static List<int> parseIPv4Address(String host) { |
2167 void error(String msg) { | 2250 void error(String msg) { |
2168 throw new FormatException('Illegal IPv4 address, $msg'); | 2251 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. | 3340 // All non-escape RFC-2396 uric characters. |
3258 // | 3341 // |
3259 // uric = reserved | unreserved | escaped | 3342 // uric = reserved | unreserved | escaped |
3260 // reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," | 3343 // reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," |
3261 // unreserved = alphanum | mark | 3344 // unreserved = alphanum | mark |
3262 // mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" | 3345 // mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" |
3263 // | 3346 // |
3264 // This is the same characters as in a URI query (which is URI pchar plus '?') | 3347 // This is the same characters as in a URI query (which is URI pchar plus '?') |
3265 static const _uricTable = Uri._queryCharTable; | 3348 static const _uricTable = Uri._queryCharTable; |
3266 } | 3349 } |
OLD | NEW |