| 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 library utils; | |
| 6 | |
| 7 import 'dart:async'; | |
| 8 | |
| 9 /// Adds additional query parameters to [url], overwriting the original | |
| 10 /// parameters if a name conflict occurs. | |
| 11 Uri addQueryParameters(Uri url, Map<String, String> parameters) { | |
| 12 var queryMap = queryToMap(url.query); | |
| 13 queryMap.addAll(parameters); | |
| 14 return url.resolve("?${mapToQuery(queryMap)}"); | |
| 15 } | |
| 16 | |
| 17 /// Convert a URL query string (or `application/x-www-form-urlencoded` body) | |
| 18 /// into a [Map] from parameter names to values. | |
| 19 Map<String, String> queryToMap(String queryList) { | |
| 20 var map = {}; | |
| 21 for (var pair in queryList.split("&")) { | |
| 22 var split = split1(pair, "="); | |
| 23 if (split.isEmpty) continue; | |
| 24 var key = urlDecode(split[0]); | |
| 25 var value = split.length > 1 ? urlDecode(split[1]) : ""; | |
| 26 map[key] = value; | |
| 27 } | |
| 28 return map; | |
| 29 } | |
| 30 | |
| 31 /// Convert a [Map] from parameter names to values to a URL query string. | |
| 32 String mapToQuery(Map<String, String> map) { | |
| 33 var pairs = <List<String>>[]; | |
| 34 map.forEach((key, value) { | |
| 35 key = Uri.encodeQueryComponent(key); | |
| 36 value = (value == null || value.isEmpty) | |
| 37 ? null | |
| 38 : Uri.encodeQueryComponent(value); | |
| 39 pairs.add([key, value]); | |
| 40 }); | |
| 41 return pairs.map((pair) { | |
| 42 if (pair[1] == null) return pair[0]; | |
| 43 return "${pair[0]}=${pair[1]}"; | |
| 44 }).join("&"); | |
| 45 } | |
| 46 | |
| 47 /// Decode a URL-encoded string. Unlike [Uri.decodeComponent], this includes | |
| 48 /// replacing `+` with ` `. | |
| 49 String urlDecode(String encoded) => | |
| 50 Uri.decodeComponent(encoded.replaceAll("+", " ")); | |
| 51 | |
| 52 /// Like [String.split], but only splits on the first occurrence of the pattern. | |
| 53 /// This will always return a list of two elements or fewer. | |
| 54 List<String> split1(String toSplit, String pattern) { | |
| 55 if (toSplit.isEmpty) return <String>[]; | |
| 56 | |
| 57 var index = toSplit.indexOf(pattern); | |
| 58 if (index == -1) return [toSplit]; | |
| 59 return [toSplit.substring(0, index), | |
| 60 toSplit.substring(index + pattern.length)]; | |
| 61 } | |
| 62 | |
| 63 /// A WWW-Authenticate header value, parsed as per [RFC 2617][]. | |
| 64 /// | |
| 65 /// [RFC 2617]: http://tools.ietf.org/html/rfc2617 | |
| 66 class AuthenticateHeader { | |
| 67 final String scheme; | |
| 68 final Map<String, String> parameters; | |
| 69 | |
| 70 AuthenticateHeader(this.scheme, this.parameters); | |
| 71 | |
| 72 /// Parses a header string. Throws a [FormatException] if the header is | |
| 73 /// invalid. | |
| 74 factory AuthenticateHeader.parse(String header) { | |
| 75 var split = split1(header, ' '); | |
| 76 if (split.length == 0) { | |
| 77 throw new FormatException('Invalid WWW-Authenticate header: "$header"'); | |
| 78 } else if (split.length == 1 || split[1].trim().isEmpty) { | |
| 79 return new AuthenticateHeader(split[0].toLowerCase(), {}); | |
| 80 } | |
| 81 var scheme = split[0].toLowerCase(); | |
| 82 var paramString = split[1]; | |
| 83 | |
| 84 // From http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html. | |
| 85 var tokenChar = r'[^\0-\x1F()<>@,;:\\"/\[\]?={} \t\x7F]'; | |
| 86 var quotedStringChar = r'(?:[^\0-\x1F\x7F"]|\\.)'; | |
| 87 var regexp = new RegExp('^ *($tokenChar+)="($quotedStringChar*)" *(, *)?'); | |
| 88 | |
| 89 var parameters = {}; | |
| 90 var match; | |
| 91 do { | |
| 92 match = regexp.firstMatch(paramString); | |
| 93 if (match == null) { | |
| 94 throw new FormatException('Invalid WWW-Authenticate header: "$header"'); | |
| 95 } | |
| 96 | |
| 97 paramString = paramString.substring(match.end); | |
| 98 parameters[match.group(1).toLowerCase()] = match.group(2); | |
| 99 } while (match.group(3) != null); | |
| 100 | |
| 101 if (!paramString.trim().isEmpty) { | |
| 102 throw new FormatException('Invalid WWW-Authenticate header: "$header"'); | |
| 103 } | |
| 104 | |
| 105 return new AuthenticateHeader(scheme, parameters); | |
| 106 } | |
| 107 } | |
| 108 | |
| 109 /// Returns a [Future] that asynchronously completes to `null`. | |
| 110 Future get async => new Future.delayed(const Duration(milliseconds: 0), | |
| 111 () => null); | |
| OLD | NEW |