Index: utils/pub/io.dart |
diff --git a/utils/pub/io.dart b/utils/pub/io.dart |
index c622b9505ddc0b81ca2d1a5ff8d0526f0b02d519..005916d8b5cbd4f145ca2b897e6a3784cd66d31a 100644 |
--- a/utils/pub/io.dart |
+++ b/utils/pub/io.dart |
@@ -8,6 +8,7 @@ |
#library('io'); |
#import('dart:io'); |
+#import('dart:isolate'); |
#import('dart:uri'); |
/** Gets the current working directory. */ |
@@ -285,41 +286,27 @@ Future<File> createSymlink(from, to) { |
// TODO(rnystrom): Should this be async? |
String getFullPath(entry) => new File(_getPath(entry)).fullPathSync(); |
+// TODO(nweiz): make this configurable |
/** |
- * Opens an input stream for a HTTP GET request to [uri], which may be a |
- * [String] or [Uri]. |
+ * The amount of time in milliseconds to allow HTTP requests before assuming |
+ * they've failed. |
*/ |
-InputStream httpGet(uri) { |
- var resultStream = new ListInputStream(); |
- var client = new HttpClient(); |
- var connection = client.getUrl(_getUri(uri)); |
- |
- // TODO(nweiz): propagate this error to the return value. See issue 3657. |
- connection.onError = (e) { throw e; }; |
- connection.onResponse = (response) { |
- if (response.statusCode >= 400) { |
- // TODO(nweiz): propagate this error to the return value. See issue 3657. |
- throw new Exception( |
- "HTTP request for $uri failed with status ${response.statusCode}"); |
- } |
- |
- pipeInputToInput(response.inputStream, resultStream, client.shutdown); |
- }; |
- |
- return resultStream; |
-} |
+final HTTP_TIMEOUT = 30 * 1000; |
/** |
* Opens an input stream for a HTTP GET request to [uri], which may be a |
- * [String] or [Uri]. Completes with the result of the request as a String. |
+ * [String] or [Uri]. |
+ * |
+ * Callers should be sure to use [timeout] to make sure that the HTTP request |
+ * doesn't last indefinitely |
*/ |
-Future<String> httpGetString(uri) { |
- // TODO(rnystrom): This code is very similar to httpGet() and |
- // consumeInputStream() (but this one propagates errors). Merge them when |
- // #3657 is fixed. |
+Future<InputStream> httpGet(uri) { |
+ // TODO(nweiz): This could return an InputStream synchronously if issue 3657 |
+ // were fixed and errors could be propagated through it. Then we could also |
+ // automatically attach a timeout to that stream. |
uri = _getUri(uri); |
- var completer = new Completer<String>(); |
+ var completer = new Completer<InputStream>(); |
var client = new HttpClient(); |
var connection = client.getUrl(uri); |
@@ -344,28 +331,23 @@ Future<String> httpGetString(uri) { |
return; |
} |
- var resultStream = new ListInputStream(); |
- var buffer = <int>[]; |
- |
- response.inputStream.onClosed = () { |
- client.shutdown(); |
- completer.complete(new String.fromCharCodes(buffer)); |
- }; |
- |
- response.inputStream.onData = () { |
- buffer.addAll(response.inputStream.read()); |
- }; |
- |
- response.inputStream.onError = (e) { |
- client.shutdown(); |
- completer.completeException(e); |
- }; |
+ completer.complete(response.inputStream); |
}; |
return completer.future; |
} |
/** |
+ * Opens an input stream for a HTTP GET request to [uri], which may be a |
+ * [String] or [Uri]. Completes with the result of the request as a String. |
+ */ |
+Future<String> httpGetString(uri) { |
+ var future = httpGet(uri).chain((stream) => consumeInputStream(stream)) |
+ .transform((bytes) => new String.fromCharCodes(bytes)); |
Bob Nystrom
2012/09/06 22:52:05
Indent +2
nweiz
2012/09/06 23:33:34
Done.
|
+ return timeout(future, HTTP_TIMEOUT, 'Timed out while fetching URL "$uri".'); |
+} |
+ |
+/** |
* Takes all input from [source] and writes it to [sink]. |
* |
* [onClosed] is called when [source] is closed. |
@@ -460,6 +442,32 @@ Future<PubProcessResult> runProcess(String executable, List<String> args, |
} |
/** |
+ * Wraps [input] to provide a timeout. If [input] completes before |
+ * [milliSeconds] have passed, then the return value completes in the same way. |
+ * However, if [milliSeconds] pass before [input] has completed, it completes |
+ * with [exception]. |
+ */ |
+Future timeout(Future input, int milliSeconds, exception) { |
Bob Nystrom
2012/09/06 22:52:05
Can you create a TimeoutException class and then t
nweiz
2012/09/06 23:33:34
Done.
|
+ var completer = new Completer(); |
+ var timer = new Timer(milliSeconds, (_) { |
+ if (completer.future.isComplete) return; |
+ completer.completeException(exception); |
Bob Nystrom
2012/09/06 22:52:05
I like the idea of timeout() being a separate orth
nweiz
2012/09/06 23:33:34
Given that we do explicitly exit, I don't see anyt
Bob Nystrom
2012/09/07 00:15:34
OK. Can you update the doc comment to note that th
|
+ }); |
+ input.handleException((e) { |
+ if (completer.future.isComplete) return false; |
+ timer.cancel(); |
+ completer.completeException(e); |
+ return true; |
+ }); |
+ input.then((value) { |
+ if (completer.future.isComplete) return; |
+ timer.cancel(); |
+ completer.complete(value); |
+ }); |
+ return completer.future; |
+} |
+ |
+/** |
* Tests whether or not the git command-line app is available for use. |
*/ |
Future<bool> get isGitInstalled { |