Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 vmservice_io; | 5 part of vmservice_io; |
| 6 | 6 |
| 7 _log(msg) { | |
| 8 print("% $msg"); | |
| 9 } | |
| 10 | |
| 7 var _httpClient; | 11 var _httpClient; |
| 8 | 12 |
| 9 // Send a response to the requesting isolate. | 13 // Send a response to the requesting isolate. |
| 10 void _sendResponse(SendPort sp, int id, dynamic data) { | 14 void _sendResourceResponse(SendPort sp, int id, dynamic data) { |
| 11 assert((data is List<int>) || (data is String)); | 15 assert((data is List<int>) || (data is String)); |
| 12 var msg = new List(2); | 16 var msg = new List(2); |
| 13 msg[0] = id; | 17 msg[0] = id; |
| 14 msg[1] = data; | 18 msg[1] = data; |
| 15 sp.send(msg); | 19 sp.send(msg); |
| 16 } | 20 } |
| 17 | 21 |
| 18 void _loadHttp(SendPort sp, int id, Uri uri) { | 22 void _loadHttp(SendPort sp, int id, Uri uri) { |
| 19 if (_httpClient == null) { | 23 if (_httpClient == null) { |
| 20 _httpClient = new HttpClient()..maxConnectionsPerHost = 6; | 24 _httpClient = new HttpClient()..maxConnectionsPerHost = 6; |
| 21 } | 25 } |
| 22 _httpClient.getUrl(uri) | 26 _httpClient.getUrl(uri) |
| 23 .then((HttpClientRequest request) => request.close()) | 27 .then((HttpClientRequest request) => request.close()) |
| 24 .then((HttpClientResponse response) { | 28 .then((HttpClientResponse response) { |
| 25 var builder = new BytesBuilder(copy: false); | 29 var builder = new BytesBuilder(copy: false); |
| 26 response.listen( | 30 response.listen( |
| 27 builder.add, | 31 builder.add, |
| 28 onDone: () { | 32 onDone: () { |
| 29 if (response.statusCode != 200) { | 33 if (response.statusCode != 200) { |
| 30 var msg = "Failure getting $uri:\n" | 34 var msg = "Failure getting $uri:\n" |
| 31 " ${response.statusCode} ${response.reasonPhrase}"; | 35 " ${response.statusCode} ${response.reasonPhrase}"; |
| 32 _sendResponse(sp, id, msg); | 36 _sendResourceResponse(sp, id, msg); |
| 33 } else { | 37 } else { |
| 34 _sendResponse(sp, id, builder.takeBytes()); | 38 _sendResourceResponse(sp, id, builder.takeBytes()); |
| 35 } | 39 } |
| 36 }, | 40 }, |
| 37 onError: (e) { | 41 onError: (e) { |
| 38 _sendResponse(sp, d, e.toString()); | 42 _sendResourceResponse(sp, id, e.toString()); |
| 39 }); | 43 }); |
| 40 }) | 44 }) |
| 41 .catchError((e) { | 45 .catchError((e) { |
| 42 _sendResponse(sp, id, e.toString()); | 46 _sendResourceResponse(sp, id, e.toString()); |
| 43 }); | 47 }); |
| 44 // It's just here to push an event on the event loop so that we invoke the | 48 // It's just here to push an event on the event loop so that we invoke the |
| 45 // scheduled microtasks. | 49 // scheduled microtasks. |
| 46 Timer.run(() {}); | 50 Timer.run(() {}); |
| 47 } | 51 } |
| 48 | 52 |
| 49 void _loadFile(SendPort sp, int id, Uri uri) { | 53 void _loadFile(SendPort sp, int id, Uri uri) { |
| 50 var path = uri.toFilePath(); | 54 var path = uri.toFilePath(); |
| 51 var sourceFile = new File(path); | 55 var sourceFile = new File(path); |
| 52 sourceFile.readAsBytes().then((data) { | 56 sourceFile.readAsBytes().then((data) { |
| 53 _sendResponse(sp, id, data); | 57 _sendResourceResponse(sp, id, data); |
| 54 }, | 58 }, |
| 55 onError: (e) { | 59 onError: (e) { |
| 56 var err = "Error loading $uri:\n $e"; | 60 var err = "Error loading $uri:\n $e"; |
| 57 _sendResponse(sp, id, err); | 61 _sendResourceResponse(sp, id, err); |
| 58 }); | 62 }); |
| 59 } | 63 } |
| 60 | 64 |
| 61 var dataUriRegex = new RegExp( | 65 var dataUriRegex = new RegExp( |
| 62 r"data:([\w-]+/[\w-]+)?(;charset=([\w-]+))?(;base64)?,(.*)"); | 66 r"data:([\w-]+/[\w-]+)?(;charset=([\w-]+))?(;base64)?,(.*)"); |
| 63 | 67 |
| 64 void _loadDataUri(SendPort sp, int id, Uri uri) { | 68 void _loadDataUri(SendPort sp, int id, Uri uri) { |
| 65 try { | 69 try { |
| 66 var match = dataUriRegex.firstMatch(uri.toString()); | 70 var match = dataUriRegex.firstMatch(uri.toString()); |
| 67 if (match == null) throw "Malformed data uri"; | 71 if (match == null) throw "Malformed data uri"; |
| 68 | 72 |
| 69 var mimeType = match.group(1); | 73 var mimeType = match.group(1); |
| 70 var encoding = match.group(3); | 74 var encoding = match.group(3); |
| 71 var maybeBase64 = match.group(4); | 75 var maybeBase64 = match.group(4); |
| 72 var encodedData = match.group(5); | 76 var encodedData = match.group(5); |
| 73 | 77 |
| 74 if (mimeType != "application/dart") { | 78 if (mimeType != "application/dart") { |
| 75 throw "MIME-type must be application/dart"; | 79 throw "MIME-type must be application/dart"; |
| 76 } | 80 } |
| 77 if (encoding != "utf-8") { | 81 if (encoding != "utf-8") { |
| 78 // Default is ASCII. The C++ portion of the embedder assumes UTF-8. | 82 // Default is ASCII. The C++ portion of the embedder assumes UTF-8. |
| 79 throw "Only utf-8 encoding is supported"; | 83 throw "Only utf-8 encoding is supported"; |
| 80 } | 84 } |
| 81 if (maybeBase64 != null) { | 85 if (maybeBase64 != null) { |
| 82 throw "Only percent encoding is supported"; | 86 throw "Only percent encoding is supported"; |
| 83 } | 87 } |
| 84 | 88 |
| 85 var data = UTF8.encode(Uri.decodeComponent(encodedData)); | 89 var data = UTF8.encode(Uri.decodeComponent(encodedData)); |
| 86 _sendResponse(sp, id, data); | 90 _sendResourceResponse(sp, id, data); |
| 87 } catch (e) { | 91 } catch (e) { |
| 88 _sendResponse(sp, id, "Invalid data uri ($uri):\n $e"); | 92 _sendResourceResponse(sp, id, "Invalid data uri ($uri):\n $e"); |
| 89 } | 93 } |
| 90 } | 94 } |
| 91 | 95 |
| 96 _handleResourceRequest(SendPort sp, bool traceLoading, int id, Uri resource) { | |
| 97 if (resource.scheme == 'file') { | |
| 98 _loadFile(sp, id, resource); | |
| 99 } else if ((resource.scheme == 'http') || (resource.scheme == 'https')) { | |
| 100 _loadHttp(sp, id, resource); | |
| 101 } else if ((resource.scheme == 'data')) { | |
| 102 _loadDataUri(sp, id, resource); | |
| 103 } else { | |
| 104 _sendResourceResponse(sp, id, | |
| 105 'Unknown scheme (${resource.scheme}) for $resource'); | |
| 106 } | |
| 107 } | |
| 108 | |
| 109 | |
| 110 // Handling of packages requests. Finding and parsing of .packages file or | |
| 111 // packages/ directories. | |
| 112 const _LF = 0x0A; | |
| 113 const _CR = 0x0D; | |
| 114 const _SPACE = 0x20; | |
| 115 const _HASH = 0x23; | |
| 116 const _DOT = 0x2E; | |
| 117 const _COLON = 0x3A; | |
| 118 const _DEL = 0x7F; | |
| 119 | |
| 120 const _invalidPackageNameChars = const [ | |
| 121 // space ! " # $ % & ' | |
| 122 true , false, true , true , false, true , false, false, | |
| 123 // ( ) * + , - . / | |
| 124 false, false, false, false, false, false, false, true , | |
| 125 // 0 1 2 3 4 5 6 7 | |
| 126 false, false, false, false, false, false, false, false, | |
| 127 // 8 9 : ; < = > ? | |
| 128 false, false, true , false, true , false, true , true , | |
| 129 // @ A B C D E F G | |
| 130 false, false, false, false, false, false, false, false, | |
| 131 // H I J K L M N O | |
| 132 false, false, false, false, false, false, false, false, | |
| 133 // P Q R S T U V W | |
| 134 false, false, false, false, false, false, false, false, | |
| 135 // X Y Z [ \ ] ^ _ | |
| 136 false, false, false, true , true , true , true , false, | |
| 137 // ` a b c d e f g | |
| 138 true , false, false, false, false, false, false, false, | |
| 139 // h i j k l m n o | |
| 140 false, false, false, false, false, false, false, false, | |
| 141 // p q r s t u v w | |
| 142 false, false, false, false, false, false, false, false, | |
| 143 // x y z { | } ~ DEL | |
| 144 false, false, false, true , true , true , false, true | |
| 145 ]; | |
| 146 | |
| 147 _parsePackagesFile(SendPort sp, | |
| 148 bool traceLoading, | |
| 149 Uri packagesFile, | |
| 150 List<int> data) { | |
| 151 var result = []; | |
| 152 var index = 0; | |
| 153 var len = data.length; | |
| 154 while (index < len) { | |
| 155 var start = index; | |
| 156 var char = data[index]; | |
| 157 if ((char == _CR) || (char == _LF)) { | |
| 158 // Skipping empty lines. | |
| 159 index++; | |
| 160 continue; | |
| 161 } | |
| 162 | |
| 163 // Identify split within the line and end of the line. | |
| 164 var separator = -1; | |
| 165 var end = len; | |
| 166 // Verifying validity of package name while scanning the line. | |
| 167 var nonDot = false; | |
| 168 var invalidPackageName = false; | |
| 169 | |
| 170 // Scan to the end of the line or data. | |
| 171 while (index < len) { | |
| 172 char = data[index++]; | |
| 173 if (separator == -1) { | |
| 174 if ((char == _COLON)) { | |
| 175 // The first colon on a line is the separator between package name and | |
| 176 // related URI. | |
| 177 separator = index - 1; | |
| 178 } else { | |
| 179 // Still scanning the package name part. Check for the validity of | |
| 180 // the characters. | |
| 181 nonDot = nonDot || (char != _DOT); | |
| 182 invalidPackageName = invalidPackageName || | |
| 183 (char < _SPACE) || (char > _DEL) || | |
| 184 _invalidPackageNameChars[char - _SPACE]; | |
| 185 } | |
| 186 } else if ((char == _CR) || (char == _LF)) { | |
| 187 // Identify end of line. | |
| 188 end = index - 1; | |
| 189 break; | |
| 190 } | |
| 191 } | |
| 192 | |
| 193 // No further handling needed for comment lines. | |
| 194 if (data[start] == _HASH) { | |
| 195 if (traceLoading) { | |
| 196 _log("Skipping comment in $packagesFile:\n" | |
| 197 "${new String.fromCharCodes(data, start, end)}"); | |
| 198 } | |
| 199 continue; | |
| 200 } | |
| 201 | |
| 202 // Check for a badly formatted line, starting with a ':'. | |
| 203 if (separator == start) { | |
| 204 var line = new String.fromCharCodes(data, start, end); | |
| 205 if (traceLoading) { | |
| 206 _log("Line starts with ':' in $packagesFile:\n" | |
| 207 "$line"); | |
| 208 } | |
| 209 sp.send("Missing package name in $packagesFile:\n" | |
| 210 "$line"); | |
| 211 return; | |
| 212 } | |
| 213 | |
| 214 // Ensure there is a separator on the line. | |
| 215 if (separator == -1) { | |
| 216 var line = new String.fromCharCodes(data, start, end); | |
| 217 if (traceLoading) { | |
| 218 _log("Line has no ':' in $packagesFile:\n" | |
| 219 "$line"); | |
| 220 } | |
| 221 sp.send("Missing ':' separator in $packagesFile:\n" | |
| 222 "$line"); | |
| 223 return; | |
| 224 } | |
| 225 | |
| 226 var packageName = new String.fromCharCodes(data, start, separator); | |
|
siva
2015/07/28 23:50:09
packageName duplicate check (needs to be a warning
Ivan Posva
2015/07/29 18:22:18
Added TODO in builtin.dart where we are adding the
| |
| 227 | |
| 228 // Check for valid package name. | |
| 229 if (invalidPackageName || !nonDot) { | |
| 230 var line = new String.fromCharCodes(data, start, end); | |
| 231 if (traceLoading) { | |
| 232 _log("Invalid package name $packageName in $packagesFile"); | |
| 233 } | |
| 234 sp.send("Invalid package name '$packageName' in $packagesFile:\n" | |
| 235 "$line"); | |
| 236 return; | |
| 237 } | |
| 238 | |
| 239 if (traceLoading) { | |
| 240 _log("packageName: $packageName"); | |
| 241 } | |
| 242 var packageUri = new String.fromCharCodes(data, separator + 1, end); | |
| 243 if (traceLoading) { | |
| 244 _log("original packageUri: $packageUri"); | |
| 245 } | |
| 246 // Ensure the package uri ends with a /. | |
| 247 if (!packageUri.endsWith("/")) { | |
| 248 packageUri = "$packageUri/"; | |
| 249 } | |
| 250 packageUri = packagesFile.resolve(packageUri).toString(); | |
| 251 if (traceLoading) { | |
| 252 _log("mapping: $packageName -> $packageUri"); | |
| 253 } | |
| 254 result.add(packageName); | |
| 255 result.add(packageUri); | |
| 256 } | |
| 257 | |
| 258 if (traceLoading) { | |
| 259 _log("Parsed packages file at $packagesFile. Sending:\n$result"); | |
| 260 } | |
| 261 sp.send(result); | |
| 262 } | |
| 263 | |
| 264 _loadPackagesFile(SendPort sp, bool traceLoading, Uri packagesFile) async { | |
| 265 try { | |
| 266 var data = await new File.fromUri(packagesFile).readAsBytes(); | |
| 267 if (traceLoading) { | |
| 268 _log("Loaded packages file from $packagesFile:\n" | |
| 269 "${new String.fromCharCodes(data)}"); | |
| 270 } | |
| 271 _parsePackagesFile(sp, traceLoading, packagesFile, data); | |
| 272 } catch (e, s) { | |
| 273 if (traceLoading) { | |
| 274 _log("Error loading packages: $e\n$s"); | |
| 275 } | |
| 276 sp.send("Uncaught error ($e) loading packags file."); | |
| 277 } | |
| 278 } | |
| 279 | |
| 280 _findPackagesFile(SendPort sp, bool traceLoading, Uri base) async { | |
| 281 try { | |
| 282 // Check for the existence of a .packages file and if it exists try to load | |
| 283 // and parse it. | |
| 284 var packagesFile = base.resolve(".packages"); | |
| 285 if (traceLoading) { | |
| 286 _log("Checking for $packagesFile file."); | |
| 287 } | |
| 288 var exists = await new File.fromUri(packagesFile).exists(); | |
| 289 if (traceLoading) { | |
| 290 _log("$packagesFile exists: $exists"); | |
| 291 } | |
| 292 if (exists) { | |
| 293 _loadPackagesFile(sp, traceLoading, packagesFile); | |
| 294 return; | |
| 295 } | |
| 296 | |
| 297 // Try whether there is a packages/ directory instead. | |
| 298 var packageRoot = base.resolve("packages/"); | |
| 299 if (traceLoading) { | |
| 300 _log("Checking for $packageRoot directory."); | |
| 301 } | |
| 302 exists = await new Directory.fromUri(packageRoot).exists(); | |
| 303 if (traceLoading) { | |
| 304 _log("$packageRoot exists: $exists"); | |
| 305 } | |
| 306 if (exists) { | |
| 307 if (traceLoading) { | |
| 308 _log("Found a package root at: $packageRoot"); | |
| 309 } | |
| 310 sp.send([packageRoot.toString()]); | |
| 311 return; | |
| 312 } | |
| 313 | |
| 314 // Walk up the directory hierarchy to check for the existence of | |
| 315 // .packages files in parent directories. | |
| 316 var dir = new File.fromUri(base).parent; | |
| 317 var parent = dir.parent; | |
| 318 // Keep searching until we reach the root. | |
| 319 while (parent.path != dir.path) { | |
| 320 // Already looked in dir: Move up one level. | |
| 321 dir = parent; | |
| 322 parent = dir.parent; | |
| 323 packagesFile = dir.uri.resolve(".packages"); | |
| 324 if (traceLoading) { | |
| 325 _log("Checking for $packagesFile file."); | |
| 326 } | |
| 327 exists = await new File.fromUri(packagesFile).exists(); | |
| 328 if (traceLoading) { | |
| 329 _log("$packagesFile exists: $exists"); | |
| 330 } | |
| 331 if (exists) { | |
| 332 _loadPackagesFile(sp, traceLoading, packagesFile); | |
| 333 return; | |
| 334 } | |
|
siva
2015/07/28 23:50:09
This chunk of code for finiding ".packages" file c
Ivan Posva
2015/07/29 18:22:18
Refactored into a single loop. It probably now mak
| |
| 335 } | |
| 336 | |
| 337 // No .packages file was found. | |
| 338 if (traceLoading) { | |
| 339 _log("Could not resolve a package location from $base"); | |
| 340 } | |
| 341 sp.send("Could not resolve a package location for base at $base"); | |
| 342 } catch (e, s) { | |
| 343 if (traceLoading) { | |
| 344 _log("Error loading packages: $e\n$s"); | |
| 345 } | |
| 346 sp.send("Uncaught error ($e) loading packages file."); | |
| 347 } | |
| 348 } | |
| 349 | |
| 350 _handlePackagesRequest(SendPort sp, bool traceLoading, int id, Uri resource) { | |
| 351 if (id == -1) { | |
|
siva
2015/07/28 23:50:09
-1 and -2 seem like magic numbers maybe we can hav
| |
| 352 if (resource.scheme == 'file') { | |
| 353 _findPackagesFile(sp, traceLoading, resource); | |
| 354 } else if ((resource.scheme == 'http') || (resource.scheme == 'https')) { | |
| 355 // TODO(iposva): Check for the existence of a .packages file when loading | |
| 356 // from http or https. | |
| 357 var packageRoot = resource.resolve('packages/'); | |
| 358 sp.send([packageRoot.toString()]); | |
| 359 } else { | |
| 360 sp.send("Unsupported base URI to identify .packages file: '$resource'."); | |
| 361 } | |
| 362 } else if (id == -2) { | |
| 363 if (traceLoading) { | |
| 364 _log("Handling load of packages map: '$resource'."); | |
| 365 } | |
| 366 _loadPackagesFile(sp, traceLoading, resource); | |
| 367 } else { | |
| 368 sp.send("Unknown packages request id: $id for '$resource'."); | |
| 369 } | |
| 370 } | |
| 371 | |
| 372 | |
| 373 // External entry point for loader requests. | |
| 92 _processLoadRequest(request) { | 374 _processLoadRequest(request) { |
| 93 SendPort sp = request[0]; | 375 SendPort sp = request[0]; |
| 94 int id = request[1]; | 376 assert(sp != null); |
| 95 String resource = request[2]; | 377 bool traceLoading = request[1]; |
| 96 var uri = Uri.parse(request[2]); | 378 assert(traceLoading != null); |
| 97 if (uri.scheme == 'file') { | 379 int id = request[2]; |
| 98 _loadFile(sp, id, uri); | 380 assert(id != null); |
| 99 } else if ((uri.scheme == 'http') || (uri.scheme == 'https')) { | 381 String resource = request[3]; |
| 100 _loadHttp(sp, id, uri); | 382 assert(resource != null); |
| 101 } else if ((uri.scheme == 'data')) { | 383 var uri = Uri.parse(resource); |
| 102 _loadDataUri(sp, id, uri); | 384 if (id >= 0) { |
| 385 _handleResourceRequest(sp, traceLoading, id, uri); | |
| 103 } else { | 386 } else { |
| 104 sp.send([id, 'Unknown scheme (${uri.scheme}) for $uri']); | 387 _handlePackagesRequest(sp, traceLoading, id, uri); |
| 105 } | 388 } |
| 106 } | 389 } |
| OLD | NEW |