| Index: pkg/dev_compiler/web/web_command.dart
|
| diff --git a/pkg/dev_compiler/web/web_command.dart b/pkg/dev_compiler/web/web_command.dart
|
| index 1df3255ec61bf74ce53e0bccdf0b22b16f40c8b0..d5eef15cc48a182485e73890387a55d5daf54080 100644
|
| --- a/pkg/dev_compiler/web/web_command.dart
|
| +++ b/pkg/dev_compiler/web/web_command.dart
|
| @@ -7,6 +7,7 @@ library dev_compiler.web.web_command;
|
| import 'dart:async';
|
| import 'dart:convert';
|
| import 'dart:html' show HttpRequest;
|
| +import 'dart:typed_data';
|
|
|
| import 'package:analyzer/dart/element/element.dart'
|
| show
|
| @@ -37,6 +38,21 @@ typedef void MessageHandler(Object message);
|
|
|
| @JS()
|
| @anonymous
|
| +class JSIterator<V> {}
|
| +
|
| +@JS('Map')
|
| +class JSMap<K, V> {
|
| + external V get(K v);
|
| + external set(K k, V v);
|
| + external JSIterator<K> keys();
|
| + external JSIterator<V> values();
|
| +}
|
| +
|
| +@JS('Array.from')
|
| +external List<V> iteratorToList<V>(JSIterator<V> iterator);
|
| +
|
| +@JS()
|
| +@anonymous
|
| class CompileResult {
|
| external factory CompileResult(
|
| {String code, List<String> errors, bool isValid});
|
| @@ -45,6 +61,51 @@ class CompileResult {
|
| typedef CompileModule(String imports, String body, String libraryName,
|
| String existingLibrary, String fileName);
|
|
|
| +/// Version of HttpRequest.request that retries on 502 errors.
|
| +Future<HttpRequest> httpRequestRetry502Error(String url,
|
| + {String responseType, String mimeType}) {
|
| + var completer = new Completer<HttpRequest>();
|
| +
|
| + var xhr = new HttpRequest();
|
| + xhr.open('GET', url, async: true);
|
| +
|
| + if (responseType != null) {
|
| + xhr.responseType = responseType;
|
| + }
|
| +
|
| + if (mimeType != null) {
|
| + xhr.overrideMimeType(mimeType);
|
| + }
|
| +
|
| + xhr.onLoad.listen((e) {
|
| + var accepted = xhr.status >= 200 && xhr.status < 300;
|
| + var fileUri = xhr.status == 0; // file:// URIs have status of 0.
|
| + var notModified = xhr.status == 304;
|
| + // Redirect status is specified up to 307, but others have been used in
|
| + // practice. Notably Google Drive uses 308 Resume Incomplete for
|
| + // resumable uploads, and it's also been used as a redirect. The
|
| + // redirect case will be handled by the browser before it gets to us,
|
| + // so if we see it we should pass it through to the user.
|
| + var unknownRedirect = xhr.status > 307 && xhr.status < 400;
|
| +
|
| + if (accepted || fileUri || notModified || unknownRedirect) {
|
| + completer.complete(xhr);
|
| + } else {
|
| + if (xhr.status == 502) {
|
| + print("Retrying due to 502 error for $url");
|
| + return HttpRequest.request(url,
|
| + responseType: responseType, mimeType: mimeType);
|
| + }
|
| + completer.completeError(e);
|
| + }
|
| + });
|
| +
|
| + xhr.onError.listen(completer.completeError);
|
| +
|
| + xhr.send();
|
| + return completer.future;
|
| +}
|
| +
|
| /// The command for invoking the modular compiler.
|
| class WebCompileCommand extends Command {
|
| get name => 'compile';
|
| @@ -63,35 +124,43 @@ class WebCompileCommand extends Command {
|
| return requestSummaries;
|
| }
|
|
|
| - void requestSummaries(String summaryRoot, String sdkUrl,
|
| - List<String> summaryUrls, Function onCompileReady, Function onError) {
|
| - HttpRequest
|
| - .request(sdkUrl,
|
| - responseType: "arraybuffer", mimeType: "application/octet-stream")
|
| - .then((sdkRequest) {
|
| - var sdkBytes = sdkRequest.response.asUint8List();
|
| -
|
| - // Map summary URLs to HttpRequests.
|
| - var summaryRequests = summaryUrls.map((summary) => new Future(() =>
|
| - HttpRequest.request(summaryRoot + summary,
|
| - responseType: "arraybuffer",
|
| - mimeType: "application/octet-stream")));
|
| -
|
| - Future.wait(summaryRequests).then((summaryResponses) {
|
| - // Map summary responses to summary bytes.
|
| - var summaryBytes = <List<int>>[];
|
| - for (var response in summaryResponses) {
|
| - summaryBytes.add(response.response.asUint8List());
|
| - }
|
| + Future<Null> requestSummaries(String sdkUrl, JSMap<String, String> summaryMap,
|
| + Function onCompileReady, Function onError) async {
|
| + var sdkRequest;
|
| + try {
|
| + sdkRequest = await httpRequestRetry502Error(sdkUrl,
|
| + responseType: "arraybuffer", mimeType: "application/octet-stream");
|
| + } catch (error) {
|
| + onError('Dart sdk summaries failed to load: $error. url: $sdkUrl');
|
| + return null;
|
| + }
|
|
|
| - onCompileReady(setUpCompile(sdkBytes, summaryBytes, summaryUrls));
|
| - }).catchError((error) => onError('Summaries failed to load: $error'));
|
| - }).catchError((error) =>
|
| - onError('Dart sdk summaries failed to load: $error. url: $sdkUrl'));
|
| + var sdkBytes = (sdkRequest.response as ByteBuffer).asUint8List();
|
| +
|
| + // Map summary URLs to HttpRequests.
|
| + var summaryRequests = iteratorToList(summaryMap.values())
|
| + .map((String summaryUrl) => httpRequestRetry502Error(summaryUrl,
|
| + responseType: "arraybuffer", mimeType: "application/octet-stream"))
|
| + .toList();
|
| + var summaryResponses;
|
| + try {
|
| + summaryResponses = await Future.wait(summaryRequests);
|
| + } catch (error) {
|
| + print('Summaries failed to load: $error');
|
| + onError('Summaries failed to load: $error');
|
| + return null;
|
| + }
|
| + // Map summary responses to summary bytes.
|
| + List<List<int>> summaryBytes = summaryResponses
|
| + .map((response) => (response.response as ByteBuffer).asUint8List())
|
| + .toList();
|
| + print("Summaries loaded");
|
| + onCompileReady(setUpCompile(
|
| + sdkBytes, summaryBytes, iteratorToList(summaryMap.keys())));
|
| }
|
|
|
| List<Function> setUpCompile(List<int> sdkBytes, List<List<int>> summaryBytes,
|
| - List<String> summaryUrls) {
|
| + List<String> moduleIds) {
|
| var dartSdkSummaryPath = '/dart-sdk/lib/_internal/web_sdk.sum';
|
|
|
| var resourceProvider = new MemoryResourceProvider()
|
| @@ -106,9 +175,17 @@ class WebCompileCommand extends Command {
|
| resourceProvider: resourceProvider, recordDependencyInfo: true);
|
| for (var i = 0; i < summaryBytes.length; i++) {
|
| var bytes = summaryBytes[i];
|
| - var url = '/' + summaryUrls[i];
|
| - var summaryBundle = new PackageBundle.fromBuffer(bytes);
|
| - summaryDataStore.addBundle(url, summaryBundle);
|
| + // Packages with no dart source files will have empty summaries which is fine.
|
| + if (bytes.length == 0) continue;
|
| + var url = '/${moduleIds[i]}.api.ds';
|
| + try {
|
| + var summaryBundle = new PackageBundle.fromBuffer(bytes);
|
| + summaryDataStore.addBundle(url, summaryBundle);
|
| + } catch (e) {
|
| + print(
|
| + "XXX failed to load summary for $i, ${moduleIds[i]}, len = ${summaryBytes[i].length}");
|
| + continue;
|
| + }
|
| }
|
| var summaryResolver =
|
| new InSummaryUriResolver(resourceProvider, summaryDataStore);
|
|
|