Skip to content

Instantly share code, notes, and snippets.

@garthk
Last active August 29, 2015 13:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save garthk/9634037 to your computer and use it in GitHub Desktop.
Save garthk/9634037 to your computer and use it in GitHub Desktop.
node_modules
*.zip
{
"node": true,
"browser": true,
"esnext": true,
"bitwise": true,
"camelcase": true,
"curly": true,
"eqeqeq": true,
"immed": true,
"indent": 4,
"latedef": true,
"newcap": true,
"noarg": true,
"quotmark": "single",
"regexp": true,
"undef": true,
"unused": true,
"strict": true,
"trailing": true,
"smarttabs": true,
"globals": {
"angular": false
}
}

pipe-to-file fail

Forgetting to call stream.pause and releasing the tick lets data spill out between your request callback and the next function.

To try it out:

npm install
rm v3.1.1.zip
node .
unzip -t v3.1.1.zip

Follow the sorry saga in the commits.

'use strict';
var assert = require('assert'),
path = require('path'),
http = require('http'),
https = require('https'),
fs = require('fs'),
url = require('url');
function fetch(uri, callback) {
'fetch a file';
console.error('requesting', uri);
var modules = {
'http:': http,
'https:': https
},
module = modules[url.parse(uri).protocol];
module.get(uri, function (res) {
console.error('headers:', res.headers);
switch (res.statusCode) {
case 200:
return callback(null, res);
case 301:
case 302:
res.socket.end();
return fetch(res.headers.location, callback);
default:
return callback(new Error(res.statusCode));
}
})
.on('error', callback);
}
function writeResults(destFile, res, callback) {
'write response text to a file';
assert(!res.buffered);
console.error('streaming to', destFile);
var stream = fs.createWriteStream(destFile),
size = 0;
// you can tell I'm getting annoyed
function onError(err) {
console.error('ERROR', err);
callback(err);
}
res.on('error', onError);
res.on('data', function(chunk) {
size += chunk.length;
});
stream.on('error', onError);
stream.on('close', function() { // also fails for finish
callback(null, size);
});
res.pipe(stream);
res.resume();
}
function download(details, callback) {
'download a file if it is absent or empty, making its parent directory.';
function afterWriting(err, size) {
if (err) {
return callback(err);
} else if (size === details.size || !details.size) {
return callback(null);
} else {
return callback(new Error('unexpected size: ' + size));
}
}
function afterRequesting(err, res) {
if (err) {
return callback(err);
} else {
res.pause(); // we don't want to miss writes
return setImmediate(writeResults, details.as, res, afterWriting);
}
}
fs.existsSync(details.as) && fs.truncateSync(details.as, 0);
fetch(details.url, afterRequesting);
}
module.exports = download;
if (!module.parent) {
download({
url: 'https://github.com/twbs/bootstrap/archive/v3.1.1.zip',
as: path.join(__dirname, 'v3.1.1.zip'),
size: 2357836
}, console.error);
}
{
"dependencies": {},
"version": "0.4.0"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment