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 |