mirror of
https://github.com/bower/bower.git
synced 2026-01-22 20:58:08 -05:00
110 lines
3.3 KiB
JavaScript
110 lines
3.3 KiB
JavaScript
var progress = require('request-progress');
|
|
var request = require('request');
|
|
var Q = require('q');
|
|
var mout = require('mout');
|
|
var retry = require('retry');
|
|
var fs = require('graceful-fs');
|
|
var createError = require('./createError');
|
|
|
|
var errorCodes = [
|
|
'EADDRINFO',
|
|
'ETIMEDOUT',
|
|
'ECONNRESET',
|
|
'ESOCKETTIMEDOUT'
|
|
];
|
|
|
|
function download(url, file, options) {
|
|
var operation;
|
|
var response;
|
|
var deferred = Q.defer();
|
|
var progressDelay = 8000;
|
|
|
|
options = mout.object.mixIn({
|
|
retries: 5,
|
|
factor: 2,
|
|
minTimeout: 1000,
|
|
maxTimeout: 35000,
|
|
randomize: true
|
|
}, options || {});
|
|
|
|
// Retry on network errors
|
|
operation = retry.operation(options);
|
|
operation.attempt(function () {
|
|
var req;
|
|
var writeStream;
|
|
var contentLength;
|
|
var bytesDownloaded = 0;
|
|
|
|
req = progress(request(url, options), {
|
|
delay: progressDelay
|
|
})
|
|
.on('response', function (res) {
|
|
var status = res.statusCode;
|
|
|
|
if (status < 200 || status >= 300) {
|
|
return deferred.reject(createError('Status code of ' + status, 'EHTTP'));
|
|
}
|
|
|
|
response = res;
|
|
contentLength = Number(res.headers['content-length']);
|
|
})
|
|
.on('data', function (data) {
|
|
bytesDownloaded += data.length;
|
|
})
|
|
.on('progress', function (state) {
|
|
deferred.notify(state);
|
|
})
|
|
.on('end', function () {
|
|
// Check if the whole file was downloaded
|
|
// In some unstable connections the ACK/FIN packet might be sent in the
|
|
// middle of the download
|
|
// See: https://github.com/joyent/node/issues/6143
|
|
if (contentLength && bytesDownloaded < contentLength) {
|
|
req.emit('error', createError('Transfer closed with ' + (contentLength - bytesDownloaded) + ' bytes remaining to read', 'EINCOMPLETE'));
|
|
}
|
|
})
|
|
.on('error', function (error) {
|
|
var timeout = operation._timeouts[0];
|
|
|
|
// Reject if error is not a network error
|
|
if (errorCodes.indexOf(error.code) === -1) {
|
|
return deferred.reject(error);
|
|
}
|
|
|
|
// Next attempt will start reporting download progress immediately
|
|
progressDelay = 0;
|
|
|
|
// Check if there are more retries
|
|
if (operation.retry(error)) {
|
|
// Ensure that there are no more events from this request
|
|
req.removeAllListeners();
|
|
req.on('error', function () {});
|
|
// Ensure that there are no more events from the write stream
|
|
writeStream.removeAllListeners();
|
|
writeStream.on('error', function () {});
|
|
|
|
return deferred.notify({
|
|
retry: true,
|
|
delay: timeout,
|
|
error: error
|
|
});
|
|
}
|
|
|
|
// No more retries, reject!
|
|
deferred.reject(error);
|
|
});
|
|
|
|
// Pipe read stream to write stream
|
|
writeStream = req
|
|
.pipe(fs.createWriteStream(file))
|
|
.on('error', deferred.reject)
|
|
.on('close', function () {
|
|
deferred.resolve(response);
|
|
});
|
|
});
|
|
|
|
return deferred.promise;
|
|
}
|
|
|
|
module.exports = download;
|