Skip to content
Snippets Groups Projects
Commit 446f05b0 authored by nweiz@google.com's avatar nweiz@google.com
Browse files

Add a 30s timeout for all HTTP requests in Pub.

Review URL: https://chromiumcodereview.appspot.com//10928041

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge@12151 260f80e4-7a28-3924-810f-c04153c831b5
parent 295a6b3e
No related branches found
No related tags found
No related merge requests found
......@@ -56,9 +56,8 @@ class HostedSource extends Source {
var parsed = _parseDescription(id.description);
var fullUrl = "${parsed.last}/packages/${parsed.first}/versions/"
"${id.version}.yaml";
return consumeInputStream(httpGet(fullUrl)).transform((data) {
return new Pubspec.parse(
new String.fromCharCodes(data), systemCache.sources);
return httpGetString(fullUrl).transform((yaml) {
return new Pubspec.parse(yaml, systemCache.sources);
});
}
......@@ -70,9 +69,10 @@ class HostedSource extends Source {
var name = parsedDescription.first;
var url = parsedDescription.last;
return ensureDir(destPath).chain((destDir) {
var fullUrl = "$url/packages/$name/versions/${id.version}.tar.gz";
return extractTarGz(httpGet(fullUrl), destDir);
var fullUrl = "$url/packages/$name/versions/${id.version}.tar.gz";
return Futures.wait([httpGet(fullUrl), ensureDir(destPath)]).chain((args) {
return timeout(extractTarGz(args[0], args[1]), HTTP_TIMEOUT,
'Timed out while fetching URL "$fullUrl".');
});
}
......
......@@ -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,27 +331,25 @@ 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);
};
// TODO(nweiz): remove this extra pipe when issue 4974 is fixed.
var sink = new ListInputStream();
pipeInputToInput(response.inputStream, sink);
completer.complete(sink);
};
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));
return timeout(future, HTTP_TIMEOUT, 'Timed out while fetching URL "$uri".');
}
/**
* Takes all input from [source] and writes it to [sink].
*
......@@ -459,6 +444,35 @@ Future<PubProcessResult> runProcess(String executable, List<String> args,
return completer.future;
}
/**
* 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 a [TimeoutException] with [message].
*
* Note that timing out will not cancel the asynchronous operation behind
* [input].
*/
Future timeout(Future input, int milliSeconds, String message) {
var completer = new Completer();
var timer = new Timer(milliSeconds, (_) {
if (completer.future.isComplete) return;
completer.completeException(new TimeoutException(message));
});
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.
*/
......@@ -517,6 +531,15 @@ class HttpException implements Exception {
const HttpException(this.statusCode, this.reason);
}
/**
* Exception thrown when an operation times out.
*/
class TimeoutException implements Exception {
final String message;
const TimeoutException(this.message);
}
/**
* Contains the results of invoking a [Process] and waiting for it to complete.
*/
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment