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

Side by Side Diff: runtime/bin/vmservice/loader.dart

Issue 1232593003: - Implement .packages specification. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Address review comments. Created 5 years, 4 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 | « runtime/bin/main.cc ('k') | runtime/bin/vmservice_dartium.cc » ('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) 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);
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 // Walk up the directory hierarchy to check for the existence of
283 // .packages files in parent directories and for the existense of a
284 // packages/ directory on the first iteration.
285 var dir = new File.fromUri(base).parent;
286 var prev = null;
287 // Keep searching until we reach the root.
288 while ((prev == null) || (prev.path != dir.path)) {
289 // Check for the existence of a .packages file and if it exists try to
290 // load and parse it.
291 var dirUri = dir.uri;
292 var packagesFile = dirUri.resolve(".packages");
293 if (traceLoading) {
294 _log("Checking for $packagesFile file.");
295 }
296 var exists = await new File.fromUri(packagesFile).exists();
297 if (traceLoading) {
298 _log("$packagesFile exists: $exists");
299 }
300 if (exists) {
301 _loadPackagesFile(sp, traceLoading, packagesFile);
302 return;
303 }
304 // On the first loop try whether there is a packages/ directory instead.
305 if (prev == null) {
306 var packageRoot = dirUri.resolve("packages/");
307 if (traceLoading) {
308 _log("Checking for $packageRoot directory.");
309 }
310 exists = await new Directory.fromUri(packageRoot).exists();
311 if (traceLoading) {
312 _log("$packageRoot exists: $exists");
313 }
314 if (exists) {
315 if (traceLoading) {
316 _log("Found a package root at: $packageRoot");
317 }
318 sp.send([packageRoot.toString()]);
319 return;
320 }
321 }
322 // Move up one level.
323 prev = dir;
324 dir = dir.parent;
325 }
326
327 // No .packages file was found.
328 if (traceLoading) {
329 _log("Could not resolve a package location from $base");
330 }
331 sp.send("Could not resolve a package location for base at $base");
332 } catch (e, s) {
333 if (traceLoading) {
334 _log("Error loading packages: $e\n$s");
335 }
336 sp.send("Uncaught error ($e) loading packages file.");
337 }
338 }
339
340 _handlePackagesRequest(SendPort sp,
341 bool traceLoading,
342 int id,
343 Uri resource) async {
344 if (id == -1) {
345 if (resource.scheme == 'file') {
346 _findPackagesFile(sp, traceLoading, resource);
347 } else if ((resource.scheme == 'http') || (resource.scheme == 'https')) {
348 // TODO(iposva): Check for the existence of a .packages file when loading
349 // from http or https.
350 var packageRoot = resource.resolve('packages/');
351 sp.send([packageRoot.toString()]);
352 } else {
353 sp.send("Unsupported base URI to identify .packages file: '$resource'.");
354 }
355 } else if (id == -2) {
356 if (traceLoading) {
357 _log("Handling load of packages map: '$resource'.");
358 }
359 var exists = await new File.fromUri(resource).exists();
360 if (exists) {
361 _loadPackagesFile(sp, traceLoading, resource);
362 } else {
363 sp.send("Packages file $resource not found.");
364 }
365 } else {
366 sp.send("Unknown packages request id: $id for '$resource'.");
367 }
368 }
369
370
371 // External entry point for loader requests.
92 _processLoadRequest(request) { 372 _processLoadRequest(request) {
93 SendPort sp = request[0]; 373 SendPort sp = request[0];
94 int id = request[1]; 374 assert(sp != null);
95 String resource = request[2]; 375 bool traceLoading = request[1];
96 var uri = Uri.parse(request[2]); 376 assert(traceLoading != null);
97 if (uri.scheme == 'file') { 377 int id = request[2];
98 _loadFile(sp, id, uri); 378 assert(id != null);
99 } else if ((uri.scheme == 'http') || (uri.scheme == 'https')) { 379 String resource = request[3];
100 _loadHttp(sp, id, uri); 380 assert(resource != null);
101 } else if ((uri.scheme == 'data')) { 381 var uri = Uri.parse(resource);
102 _loadDataUri(sp, id, uri); 382 if (id >= 0) {
383 _handleResourceRequest(sp, traceLoading, id, uri);
103 } else { 384 } else {
104 sp.send([id, 'Unknown scheme (${uri.scheme}) for $uri']); 385 _handlePackagesRequest(sp, traceLoading, id, uri);
105 } 386 }
106 } 387 }
OLDNEW
« no previous file with comments | « runtime/bin/main.cc ('k') | runtime/bin/vmservice_dartium.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698