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

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

Issue 1520943002: Support the same parameter key more than once in Uri query parameters. (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Address some comments Created 4 years, 11 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
« no previous file with comments | « CHANGELOG.md ('k') | tests/corelib/uri_parameters_all_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 part of dart.core; 5 part of dart.core;
6 6
7 /** 7 /**
8 * A parsed URI, such as a URL. 8 * A parsed URI, such as a URL.
9 * 9 *
10 * **See also:** 10 * **See also:**
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
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;
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
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 `+`.
floitsch 2016/01/12 16:51:33 Consider breaking it into paragraphs. Use http://
Lasse Reichstein Nielsen 2016/01/13 12:30:14 More paragraphs added.
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.
floitsch 2016/01/12 16:51:33 has no
Lasse Reichstein Nielsen 2016/01/13 12:30:14 Changed here, and in other places in the file.
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
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 [queryParametersAll] getter can provide a
floitsch 2016/01/12 16:51:33 which one? Either explicitly state a "random" one,
Lasse Reichstein Nielsen 2016/01/13 12:30:14 Now say "arbitrary".
1117 * map that maps to all the values.
1118 *
1119 * The returned map is unmodifiable.
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").
floitsch 2016/01/12 16:51:33 make sure that this encodes correctly with dartdoc
Lasse Reichstein Nielsen 2016/01/13 12:30:14 Checks out correctly in the commonmark checker lin
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.
1115 */ 1141 */
1116 Map<String, String> get queryParameters { 1142 Map<String, List<String>> get queryParametersAll {
1117 if (_queryParameters == null) { 1143 if (_queryParameterLists == null) {
1118 _queryParameters = new UnmodifiableMapView(splitQueryString(query)); 1144 Map queryParameterLists = _splitQueryStringAll(query);
1145 for (var key in queryParameterLists.keys) {
1146 queryParameterLists[key] =
1147 new List<String>.unmodifiable(queryParameterLists[key]);
1148 }
1149 _queryParameterLists =
1150 new Map<String, List<String>>.unmodifiable(queryParameterLists);
1119 } 1151 }
1120 return _queryParameters; 1152 return _queryParameterLists;
1121 } 1153 }
1122 1154
1123 /** 1155 /**
1124 * Returns a URI where the path has been normalized. 1156 * Returns a URI where the path has been normalized.
1125 * 1157 *
1126 * A normalized path does not contain `.` segments or non-leading `..` 1158 * A normalized path does not contain `.` segments or non-leading `..`
1127 * segments. 1159 * segments.
1128 * Only a relative path with no scheme or authority may contain 1160 * Only a relative path with no scheme or authority may contain
1129 * leading `..` segments, 1161 * leading `..` segments,
1130 * a path that starts with `/` will also drop any leading `..` segments. 1162 * a path that starts with `/` will also drop any leading `..` segments.
(...skipping 206 matching lines...) Expand 10 before | Expand all | Expand 10 after
1337 1369
1338 static String _makeQuery(String query, int start, int end, 1370 static String _makeQuery(String query, int start, int end,
1339 Map<String, String> queryParameters) { 1371 Map<String, String> queryParameters) {
1340 if (query == null && queryParameters == null) return null; 1372 if (query == null && queryParameters == null) return null;
1341 if (query != null && queryParameters != null) { 1373 if (query != null && queryParameters != null) {
1342 throw new ArgumentError('Both query and queryParameters specified'); 1374 throw new ArgumentError('Both query and queryParameters specified');
1343 } 1375 }
1344 if (query != null) return _normalize(query, start, end, _queryCharTable); 1376 if (query != null) return _normalize(query, start, end, _queryCharTable);
1345 1377
1346 var result = new StringBuffer(); 1378 var result = new StringBuffer();
1347 var first = true; 1379 var separator = "";
1348 queryParameters.forEach((key, value) { 1380
1349 if (!first) { 1381 void writeParameter(String key, String value) {
1350 result.write("&"); 1382 result.write(separator);
1351 } 1383 separator = "&";
1352 first = false;
1353 result.write(Uri.encodeQueryComponent(key)); 1384 result.write(Uri.encodeQueryComponent(key));
1354 if (value != null && !value.isEmpty) { 1385 if (value != null && value.isNotEmpty) {
1355 result.write("="); 1386 result.write("=");
1356 result.write(Uri.encodeQueryComponent(value)); 1387 result.write(Uri.encodeQueryComponent(value));
1357 } 1388 }
1389 }
1390
1391 queryParameters.forEach((key, value) {
1392 if (value == null || value is String) {
1393 writeParameter(key, value);
1394 } else {
1395 Iterable values = value;
1396 for (String value in values) {
1397 writeParameter(key, value);
1398 }
1399 }
1358 }); 1400 });
1359 return result.toString(); 1401 return result.toString();
1360 } 1402 }
1361 1403
1362 static String _makeFragment(String fragment, int start, int end) { 1404 static String _makeFragment(String fragment, int start, int end) {
1363 if (fragment == null) return null; 1405 if (fragment == null) return null;
1364 return _normalize(fragment, start, end, _queryCharTable); 1406 return _normalize(fragment, start, end, _queryCharTable);
1365 } 1407 }
1366 1408
1367 static int _stringOrNullLength(String s) => (s == null) ? 0 : s.length; 1409 static int _stringOrNullLength(String s) => (s == null) ? 0 : s.length;
(...skipping 781 matching lines...) Expand 10 before | Expand all | Expand 10 after
2149 } else if (index != 0) { 2191 } else if (index != 0) {
2150 var key = element.substring(0, index); 2192 var key = element.substring(0, index);
2151 var value = element.substring(index + 1); 2193 var value = element.substring(index + 1);
2152 map[Uri.decodeQueryComponent(key, encoding: encoding)] = 2194 map[Uri.decodeQueryComponent(key, encoding: encoding)] =
2153 decodeQueryComponent(value, encoding: encoding); 2195 decodeQueryComponent(value, encoding: encoding);
2154 } 2196 }
2155 return map; 2197 return map;
2156 }); 2198 });
2157 } 2199 }
2158 2200
2201 static List _createList() => [];
2202
2203 static Map _splitQueryStringAll(
2204 String query, {Encoding encoding: UTF8}) {
2205 Map result = {};
2206 int i = 0;
2207 int start = 0;
2208 int equalsIndex = -1;
2209
2210 void parsePair(int start, int equalsIndex, int end) {
2211 String key;
2212 String value;
2213 if (start == end) return;
2214 if (equalsIndex < 0) {
2215 key = _uriDecode(query, start, end, encoding, true);
2216 value = "";
2217 } else {
2218 key = _uriDecode(query, start, equalsIndex, encoding, true);
2219 value = _uriDecode(query, equalsIndex + 1, end, encoding, true);
2220 }
2221 result.putIfAbsent(key, _createList).add(value);
2222 }
2223
2224 const int _equals = 0x3d;
2225 const int _ampersand = 0x26;
2226 while (i < query.length) {
2227 int char = query.codeUnitAt(i);
2228 if (char == _equals) {
2229 if (equalsIndex < 0) equalsIndex = i;
2230 } else if (char == _ampersand) {
2231 parsePair(start, equalsIndex, i);
2232 start = i + 1;
2233 equalsIndex = -1;
2234 }
2235 i++;
2236 }
2237 parsePair(start, equalsIndex, i);
2238 return result;
2239 }
2240
2159 /** 2241 /**
2160 * Parse the [host] as an IP version 4 (IPv4) address, returning the address 2242 * 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). 2243 * as a list of 4 bytes in network byte order (big endian).
2162 * 2244 *
2163 * Throws a [FormatException] if [host] is not a valid IPv4 address 2245 * Throws a [FormatException] if [host] is not a valid IPv4 address
2164 * representation. 2246 * representation.
2165 */ 2247 */
2166 static List<int> parseIPv4Address(String host) { 2248 static List<int> parseIPv4Address(String host) {
2167 void error(String msg) { 2249 void error(String msg) {
2168 throw new FormatException('Illegal IPv4 address, $msg'); 2250 throw new FormatException('Illegal IPv4 address, $msg');
(...skipping 1088 matching lines...) Expand 10 before | Expand all | Expand 10 after
3257 // All non-escape RFC-2396 uric characters. 3339 // All non-escape RFC-2396 uric characters.
3258 // 3340 //
3259 // uric = reserved | unreserved | escaped 3341 // uric = reserved | unreserved | escaped
3260 // reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," 3342 // reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ","
3261 // unreserved = alphanum | mark 3343 // unreserved = alphanum | mark
3262 // mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" 3344 // mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
3263 // 3345 //
3264 // This is the same characters as in a URI query (which is URI pchar plus '?') 3346 // This is the same characters as in a URI query (which is URI pchar plus '?')
3265 static const _uricTable = Uri._queryCharTable; 3347 static const _uricTable = Uri._queryCharTable;
3266 } 3348 }
OLDNEW
« no previous file with comments | « CHANGELOG.md ('k') | tests/corelib/uri_parameters_all_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698