|
@@ -49,6 +49,9 @@ function setupHttp(uri, options, dst) {
|
|
|
var tries = 10;
|
|
var tries = 10;
|
|
|
var fetch = probe ? request.head : request.get;
|
|
var fetch = probe ? request.head : request.get;
|
|
|
|
|
|
|
|
|
|
+ // attach empty 'error' listener to keep dst from ever throwing
|
|
|
|
|
+ dst.on('error', noop);
|
|
|
|
|
+
|
|
|
function fetchHttp(start) {
|
|
function fetchHttp(start) {
|
|
|
if (start > 0)
|
|
if (start > 0)
|
|
|
headers['range'] = 'bytes=' + start + '-';
|
|
headers['range'] = 'bytes=' + start + '-';
|
|
@@ -61,12 +64,22 @@ function setupHttp(uri, options, dst) {
|
|
|
req.on('error', noop);
|
|
req.on('error', noop);
|
|
|
req.on('response', onresponse);
|
|
req.on('response', onresponse);
|
|
|
|
|
|
|
|
|
|
+ var failed = false;
|
|
|
function failOrRetry(err, temporary) {
|
|
function failOrRetry(err, temporary) {
|
|
|
|
|
+ if (failed) return;
|
|
|
|
|
+ failed = true;
|
|
|
|
|
+
|
|
|
req.abort();
|
|
req.abort();
|
|
|
- // remap error to partial error if we have received any data
|
|
|
|
|
- if (start + accum !== 0)
|
|
|
|
|
- err = new PartialError(err, start - offset + accum, (size !== -1) ? start - offset + size : size);
|
|
|
|
|
- return dst.emit('error', err);
|
|
|
|
|
|
|
+ if (--tries <= 0) {
|
|
|
|
|
+ // remap error to partial error if we have received any data
|
|
|
|
|
+ if (start + accum !== 0)
|
|
|
|
|
+ err = new PartialError(err, start - offset + accum, (size !== -1) ? start - offset + size : size);
|
|
|
|
|
+ return dst.emit('error', err);
|
|
|
|
|
+ }
|
|
|
|
|
+ debug('retrying at ' + (start + accum));
|
|
|
|
|
+
|
|
|
|
|
+ // TODO: delay retry?
|
|
|
|
|
+ fetchHttp(start + accum);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function reqcleanup() {
|
|
function reqcleanup() {
|
|
@@ -104,14 +117,18 @@ function setupHttp(uri, options, dst) {
|
|
|
oncemore(stream).once('end', 'error', function(err) {
|
|
oncemore(stream).once('end', 'error', function(err) {
|
|
|
dst._read = noop;
|
|
dst._read = noop;
|
|
|
if (err) return failOrRetry(err);
|
|
if (err) return failOrRetry(err);
|
|
|
- dst.push(null);
|
|
|
|
|
|
|
+ if (!failed)
|
|
|
|
|
+ dst.push(null);
|
|
|
});
|
|
});
|
|
|
dst._read = function(n) {
|
|
dst._read = function(n) {
|
|
|
stream.resume();
|
|
stream.resume();
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
// allow aborting the request
|
|
// allow aborting the request
|
|
|
- dst.abort = req.abort.bind(req);
|
|
|
|
|
|
|
+ dst.abort = function() {
|
|
|
|
|
+ tries = 0;
|
|
|
|
|
+ req.abort();
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
// forward all future errors to response stream
|
|
// forward all future errors to response stream
|
|
|
req.on('error', function(err) {
|
|
req.on('error', function(err) {
|
|
@@ -133,9 +150,6 @@ function setupHttp(uri, options, dst) {
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // attach empty 'error' listener to keep it from ever throwing
|
|
|
|
|
- dst.on('error', noop);
|
|
|
|
|
-
|
|
|
|
|
if (!dst.meta) {
|
|
if (!dst.meta) {
|
|
|
// extract meta information from header
|
|
// extract meta information from header
|
|
|
var typeparts = /^(.+?\/.+?)(?:;\w*.*)?$/.exec(res.headers['content-type']) || [null, 'application/octet-stream'],
|
|
var typeparts = /^(.+?\/.+?)(?:;\w*.*)?$/.exec(res.headers['content-type']) || [null, 'application/octet-stream'],
|