From 32d2f8cb4ad2673e0aa4fb410144d103ba21dd92 Mon Sep 17 00:00:00 2001 From: Michael Barajas Date: Sun, 20 May 2018 13:55:39 -0400 Subject: [PATCH] File size redirect upgrades (#12) * Added a test server and new test for append vs write (#5) * Added new test express server that serves a random binary file * Added test to not append file * Upgraded to version 2.0.0 (#6) * Integrate circle (#7) * Added test command, added circle.yml * Switched filesize to a number, added handleing of unknown filesizes instead of returning NAN * 1) added redirect tests 2) added infinite redirect tests 3) filesize is now a number instead of a string 4) No longer returns NaN if the server does not send a content length a) If there is no content length, the "filesize" will be set to 10^length(numberofdownloadedbytes) b) this will make progress bars continue to work, but they may jump between 0-99% multiple times 5) Added file size tests * Formatting * Added handling of unspecified content length --- .gitignore | 2 + README.md | 37 ++- circle.yml | 3 + lib/wget.js | 233 ++++++++++-------- package-lock.json | 590 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 11 +- test/helpers.js | 0 test/server.js | 86 +++++++ test/test.js | 178 ++++++++++++-- 9 files changed, 1018 insertions(+), 122 deletions(-) create mode 100644 circle.yml create mode 100644 test/helpers.js create mode 100644 test/server.js diff --git a/.gitignore b/.gitignore index 0aa35d8..db73300 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ .DS_Store node_modules .idea +coverage +.nyc_output \ No newline at end of file diff --git a/README.md b/README.md index 2d9f731..db7bb17 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ wget-improved simplifies retrieving files from any URL Improvements over [wuchengwei/node-wget](https://github.com/wuchengwei/node-wget) -- Handles 302 redirects (including infinite redirect loops) +- Handles 3xx redirects (including infinite redirect loops) - Passes URL parameters - Better error reporting - Does not write using append (uses w+ identical to wget) @@ -18,13 +18,13 @@ npm install wget-improved --save ## download(src, output, options) ```js -var wget = require('wget-improved'); -var src = 'http://nodejs.org/images/logo.svg'; -var output = '/tmp/logo.svg'; -var options = { +const wget = require('wget-improved'); +const src = 'http://nodejs.org/images/logo.svg'; +const output = '/tmp/logo.svg'; +const options = { // see options below }; -var download = wget.download(src, output, options); +let download = wget.download(src, output, options); download.on('error', function(err) { console.log(err); }); @@ -35,6 +35,7 @@ download.on('end', function(output) { console.log(output); }); download.on('progress', function(progress) { + typeof progress === 'number' // code to show progress bar }); ``` @@ -42,16 +43,16 @@ download.on('progress', function(progress) { ## request(options, callback) ```js -var wget = require('wget'); -var options = { +const wget = require('wget'); +const options = { protocol: 'https', host: 'raw.github.com', path: '/Fyrd/caniuse/master/data.json', proxy: 'http://host:port', method: 'GET' }; -var req = wget.request(options, function(res) { - var content = ''; +let req = wget.request(options, function(res) { + let content = ''; if (res.statusCode === 200) { res.on('error', function(err) { console.log(err); @@ -98,6 +99,18 @@ nwget https://raw.github.com/Fyrd/caniuse/master/data.json -O /tmp/data.json ./node_modules/.bin/nwget https://raw.github.com/Fyrd/caniuse/master/data.json -O /tmp/data.json ``` -## Todo +## Changes from 2.0.0 to 3.0.0 +**Progress is now returned as a Number instead of a String** + +**On start filesize can return null when the remote server does not provided content-lenth** + +Exception for not specifying protocol is now: `Your URL must use either HTTP or HTTPS.` + +Supports handling redirects that return a relative URL. + +You can now get events for the **total** number of bytes downloaded `download.on('bytes', function(bytes) {}...)` + +Request headers can be specified by passing an object to options.headers. + +Unit tests have been added for most download functionality and error cases and are a requirement for all PRs going forward! -- Enable gzip when using request method diff --git a/circle.yml b/circle.yml new file mode 100644 index 0000000..3e0a2b3 --- /dev/null +++ b/circle.yml @@ -0,0 +1,3 @@ +machine: + node: + version: 6.10.0 diff --git a/lib/wget.js b/lib/wget.js index b63813c..bc7ebd9 100644 --- a/lib/wget.js +++ b/lib/wget.js @@ -1,12 +1,12 @@ -'use strict' +'use strict'; -var http = require('http'); -var https = require('https'); -var tunnel = require('tunnel'); -var url = require('url'); -var zlib = require('zlib'); -var fs = require('fs'); -var EventEmitter = require('events').EventEmitter; +const http = require('http'); +const https = require('https'); +const tunnel = require('tunnel'); +const url = require('url'); +const zlib = require('zlib'); +const fs = require('fs'); +const EventEmitter = require('events').EventEmitter; /** * Downloads a file using http get and request @@ -18,12 +18,11 @@ var EventEmitter = require('events').EventEmitter; * @returns {*|EventEmitter} */ function download(src, output, options, _parentEvent, redirects) { - if(typeof redirects === "undefined") { + if (typeof redirects === 'undefined') { redirects = 0; } - var downloader = _parentEvent || new EventEmitter(), + let downloader = _parentEvent || new EventEmitter(), srcUrl, - tunnelAgent, req; if (options) { @@ -34,87 +33,121 @@ function download(src, output, options, _parentEvent, redirects) { }; } srcUrl = url.parse(src); - if(!srcUrl.protocol) { - downloader.emit('error', 'Cannot parse url protocol for '+src); - return; + if (!srcUrl.protocol || srcUrl.prototype === null) { + downloader.emit( + 'error', + 'Cannot parse url protocol for ' + + src + + ', please specify either HTTP or HTTPS' + ); + return false; } srcUrl.protocol = cleanProtocol(srcUrl.protocol); - req = request({ - protocol: srcUrl.protocol, - host: srcUrl.hostname, - port: srcUrl.port, - path: srcUrl.pathname + (srcUrl.search || ""), - proxy: options?options.proxy:undefined, - auth: options.auth?options.auth:undefined, - method: 'GET' - }, function(res) { - var fileSize, writeStream, downloadedSize; - var gunzip = zlib.createGunzip(); - - // Handle 302 redirects - if(res.statusCode === 301 || res.statusCode === 302 || res.statusCode === 307) { - - var new_location=res.headers.location; - if(res.headers.location.indexOf('/')===0) { - new_location = srcUrl.protocol+'://'+srcUrl.hostname+(srcUrl.port? ':'+srcUrl.port:'')+res.headers.location; - } - - redirects++; - if(redirects >= 10) { - downloader.emit('error', 'Infinite redirect loop detected'); - } - download(new_location, output, options, downloader, redirects); - } + req = request( + { + protocol: srcUrl.protocol, + host: srcUrl.hostname, + port: srcUrl.port, + path: srcUrl.pathname + (srcUrl.search || ''), + proxy: options ? options.proxy : undefined, + auth: options.auth ? options.auth : undefined, + method: 'GET' + }, + function(res) { + let fileSize, writeStream, downloadedSize; + let gunzip = zlib.createGunzip(); - if (res.statusCode === 200) { - downloadedSize = 0; - fileSize = res.headers['content-length']; - writeStream = fs.createWriteStream(output, { - flags: 'w+', - encoding: 'binary' - }); - - res.on('error', function(err) { - writeStream.end(); - downloader.emit('error', err); - }); - - var encoding = ""; - if(typeof res.headers['content-encoding'] === "string") { - encoding = res.headers['content-encoding']; - } + // Handle 302 redirects + if ( + res.statusCode === 301 || + res.statusCode === 302 || + res.statusCode === 307 + ) { + var newLocation = res.headers.location; + if (res.headers.location.indexOf('/') === 0) { + newLocation = + srcUrl.protocol + + '://' + + srcUrl.hostname + + (srcUrl.port ? ':' + srcUrl.port : '') + + res.headers.location; + } - // If the user has specified to unzip, and the file is gzip encoded, pipe to gunzip - if(options.gunzip === true && encoding === "gzip") { - res.pipe(gunzip); - } else { - res.pipe(writeStream); + redirects++; + if (redirects >= 10) { + downloader.emit('error', 'Infinite redirect loop detected'); + return false; + } + download(newLocation, output, options, downloader, redirects); } - //emit a start event so the user knows the file-size he's gonna receive - downloader.emit('start', fileSize); - - // Data handlers - res.on('data', function(chunk) { - downloadedSize += chunk.length; - downloader.emit('progress', downloadedSize/fileSize); - }); - gunzip.on('data', function(chunk) { - writeStream.write(chunk); - }); - - writeStream.on('finish', function() { - writeStream.end(); - downloader.emit('end', "Finished writing to disk"); - req.end('finished'); - }); - } else if(res.statusCode !== 200 && res.statusCode !== 301 && res.statusCode !== 302) { - downloader.emit('error', 'Server responded with unhandled status: ' + res.statusCode); + if (res.statusCode === 200) { + downloadedSize = 0; + fileSize = Number(res.headers['content-length']); + + // If content length header is not sent, there is no way to determine file size. + if (isNaN(fileSize) === true) { + fileSize = null; + } + writeStream = fs.createWriteStream(output, { + flags: 'w+', + encoding: 'binary' + }); + + res.on('error', function(err) { + writeStream.end(); + downloader.emit('error', err); + }); + + var encoding = ''; + if (typeof res.headers['content-encoding'] === 'string') { + encoding = res.headers['content-encoding']; + } + + // If the user has specified to unzip, and the file is gzip encoded, pipe to gunzip + if (options.gunzip === true && encoding === 'gzip') { + res.pipe(gunzip); + } else { + res.pipe(writeStream); + } + + //emit a start event so the user knows the file-size he's gonna receive + downloader.emit('start', fileSize); + + // Data handlers + res.on('data', function(chunk) { + downloadedSize += chunk.length; + downloader.emit( + 'progress', + calculateProgress(fileSize, downloadedSize) + ); + downloader.emit('bytes', downloadedSize); + }); + gunzip.on('data', function(chunk) { + writeStream.write(chunk); + }); + + writeStream.on('finish', function() { + writeStream.end(); + downloader.emit('progress', 1); + downloader.emit('end', 'Finished writing to disk'); + req.end('finished'); + }); + } else if ( + res.statusCode !== 200 && + res.statusCode !== 301 && + res.statusCode !== 302 + ) { + downloader.emit( + 'error', + 'Server responded with unhandled status: ' + res.statusCode + ); + } } - }); + ); - req.end('done'); + req.flushHeaders(); req.on('error', function(err) { downloader.emit('error', err); }); @@ -126,7 +159,9 @@ function download(src, output, options, _parentEvent, redirects) { } function request(options, callback) { - var newOptions = {}, newProxy = {}, key; + var newOptions = {}, + newProxy = {}, + key; options = parseOptions('request', options); if (options.protocol === 'http') { if (options.proxy) { @@ -136,9 +171,9 @@ function request(options, callback) { } } if (options.proxy.protocol === 'http') { - options.agent = tunnel.httpOverHttp({proxy: newProxy}); + options.agent = tunnel.httpOverHttp({ proxy: newProxy }); } else if (options.proxy.protocol === 'https') { - options.agent = tunnel.httpOverHttps({proxy: newProxy}); + options.agent = tunnel.httpOverHttps({ proxy: newProxy }); } else { throw options.proxy.protocol + ' proxy is not supported!'; } @@ -158,9 +193,9 @@ function request(options, callback) { } } if (options.proxy.protocol === 'http') { - options.agent = tunnel.httpsOverHttp({proxy: newProxy}); + options.agent = tunnel.httpsOverHttp({ proxy: newProxy }); } else if (options.proxy.protocol === 'https') { - options.agent = tunnel.httpsOverHttps({proxy: newProxy}); + options.agent = tunnel.httpsOverHttps({ proxy: newProxy }); } else { throw options.proxy.protocol + ' proxy is not supported!'; } @@ -172,7 +207,7 @@ function request(options, callback) { } return https.request(newOptions, callback); } - throw 'only allow http or https request!'; + throw 'Your URL must use either HTTP or HTTPS.'; } function parseOptions(type, options) { @@ -186,7 +221,7 @@ function parseOptions(type, options) { options.proxy.host = proxy.hostname; options.proxy.port = proxy.port; options.proxy.proxyAuth = proxy.auth; - options.proxy.headers = {'User-Agent': 'Node'}; + options.proxy.headers = { 'User-Agent': 'Node-Wget' }; } } return options; @@ -205,7 +240,7 @@ function parseOptions(type, options) { options.proxy.host = proxy.hostname; options.proxy.port = proxy.port; options.proxy.proxyAuth = proxy.auth; - options.proxy.headers = {'User-Agent': 'Node'}; + options.proxy.headers = { 'User-Agent': 'Node-Wget' }; } } @@ -215,7 +250,21 @@ function parseOptions(type, options) { } function cleanProtocol(str) { - return str.trim().toLowerCase().replace(/:$/, ''); + return str + .trim() + .toLowerCase() + .replace(/:$/, ''); +} + +function calculateProgress(fileSize, totalDownloaded) { + // If we don't know the size of the download, we Microsoft(tm) the progress bar into a moving target... + if (fileSize === null) { + var length = String(totalDownloaded).length; + // This guarantees that totalDownloaded is never greater than or equal to fileSize. + fileSize = Math.pow(10, length) + 1; + } + + return totalDownloaded / fileSize; } exports.download = download; diff --git a/package-lock.json b/package-lock.json index ac2669d..48dce95 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,20 +1,81 @@ { "name": "wget-improved", - "version": "1.4.0", + "version": "2.0.0", "lockfileVersion": 1, "dependencies": { + "accepts": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", + "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=", + "dev": true + }, + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "dev": true + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", + "dev": true + }, + "assert-plus": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", + "dev": true + }, "assertion-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.2.tgz", "integrity": "sha1-E8pRXYYgbaC6xm6DTdOX2HWBCUw=", "dev": true }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "aws-sign2": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", + "dev": true + }, + "aws4": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", + "dev": true + }, "balanced-match": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", "dev": true }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "dev": true, + "optional": true + }, + "boom": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "dev": true + }, "brace-expansion": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz", @@ -27,6 +88,12 @@ "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", "dev": true }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, "chai": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/chai/-/chai-4.0.2.tgz", @@ -39,6 +106,18 @@ "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", "dev": true }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "combined-stream": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", + "dev": true + }, "commander": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", @@ -51,6 +130,50 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", + "dev": true + }, + "content-type": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz", + "integrity": "sha1-t9ETrueo3Se9IRM8TcJSnfFyHu0=", + "dev": true + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "cryptiles": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "dev": true + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, "debug": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.0.tgz", @@ -71,18 +194,143 @@ } } }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "depd": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz", + "integrity": "sha1-4b2Cxqq2ztlluXuIsX7T5SjKGMM=", + "dev": true + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, "diff": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz", "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=", "dev": true }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "dev": true, + "optional": true + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "encodeurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", + "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=", + "dev": true + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, + "etag": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.0.tgz", + "integrity": "sha1-b2Ma7zNtbEY2K1F2QETOIWvjwFE=", + "dev": true + }, + "express": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.15.3.tgz", + "integrity": "sha1-urZdDwOqgMNYQIly/HAPkWlEtmI=", + "dev": true, + "dependencies": { + "debug": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.7.tgz", + "integrity": "sha1-krrR9tBbu2u6Isyoi80OyJTChh4=", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", + "dev": true + }, + "extsprintf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz", + "integrity": "sha1-4QgOBljjALBilJkMxw4VAiNf1VA=", + "dev": true + }, + "finalhandler": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.3.tgz", + "integrity": "sha1-70fneVDpmXgOhgIqVg4yF+DQzIk=", + "dev": true, + "dependencies": { + "debug": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.7.tgz", + "integrity": "sha1-krrR9tBbu2u6Isyoi80OyJTChh4=", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "dev": true + }, + "forwarded": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.0.tgz", + "integrity": "sha1-Ge+YdMSuHCl7zweP3mOgm2aoQ2M=", + "dev": true + }, + "fresh": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.0.tgz", + "integrity": "sha1-9HTKXmqSRtb9jglTz6m5yAWvp44=", + "dev": true + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -95,6 +343,20 @@ "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", "dev": true }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, "glob": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", @@ -113,12 +375,48 @@ "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=", "dev": true }, + "har-schema": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", + "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=", + "dev": true + }, + "har-validator": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", + "dev": true + }, "has-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", "dev": true }, + "hawk": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "dev": true + }, + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", + "dev": true + }, + "http-errors": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.1.tgz", + "integrity": "sha1-X4uO2YrKVFZWv1cplzh/kEpyIlc=", + "dev": true + }, + "http-signature": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -131,12 +429,75 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, + "ipaddr.js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.3.0.tgz", + "integrity": "sha1-HgOlL9rYOou7KyXL9JmLTP/NPew=", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true, + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, "json3": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", "dev": true }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, + "jsprim": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", + "integrity": "sha1-o7h+QCmNjDgFUtjMdiigu5WiKRg=", + "dev": true, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, "lodash._baseassign": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", @@ -191,6 +552,42 @@ "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", "dev": true }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "mime": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz", + "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=", + "dev": true + }, + "mime-db": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz", + "integrity": "sha1-gg9XIpa70g7CXtVeW13oaeVDbrE=", + "dev": true + }, + "mime-types": { + "version": "2.1.15", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", + "integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=", + "dev": true + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -228,41 +625,232 @@ "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", "dev": true }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", + "dev": true + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true }, + "parseurl": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz", + "integrity": "sha1-yKuMkiO6NIiKpkopeyiFO+wY2lY=", + "dev": true + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, "pathval": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", "dev": true }, + "performance-now": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", + "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=", + "dev": true + }, + "proxy-addr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.1.4.tgz", + "integrity": "sha1-J+VF9pYKRKYn2bREZ+NcG2tM4vM=", + "dev": true + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "qs": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", + "dev": true + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", + "dev": true + }, + "request": { + "version": "2.81.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", + "dev": true + }, + "safe-buffer": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.0.tgz", + "integrity": "sha512-aSLEDudu6OoRr/2rU609gRmnYboRLxgDG1z9o2Q0os7236FwvcqIOO8r8U5JUEwivZOhDaKlFO4SbPTJYyBEyQ==", + "dev": true + }, + "send": { + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/send/-/send-0.15.3.tgz", + "integrity": "sha1-UBP5+ZAj31DRvZiSwZ4979HVMwk=", + "dev": true, + "dependencies": { + "debug": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.7.tgz", + "integrity": "sha1-krrR9tBbu2u6Isyoi80OyJTChh4=", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "serve-static": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.12.3.tgz", + "integrity": "sha1-n0uhni8wMMVH+K+ZEHg47DjVseI=", + "dev": true + }, + "setprototypeof": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=", + "dev": true + }, + "sntp": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "dev": true + }, + "sshpk": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "dev": true, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "statuses": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", + "dev": true + }, + "stringstream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", + "dev": true + }, "supports-color": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=", "dev": true }, + "tough-cookie": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", + "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=", + "dev": true + }, "tunnel": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.2.tgz", "integrity": "sha1-8jvNi3p7ioZCYbIIT2b5MZM5YzQ=" }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true, + "optional": true + }, "type-detect": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.3.tgz", "integrity": "sha1-Dj8mcLRAmbC0bChNE2p+9Jx0wuo=", "dev": true }, + "type-is": { + "version": "1.6.15", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", + "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, + "utils-merge": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz", + "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg=", + "dev": true + }, + "uuid": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz", + "integrity": "sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE=", + "dev": true + }, + "vary": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.1.tgz", + "integrity": "sha1-Z1Neu2lMHVIldFeYRmUyP1h+jTc=", + "dev": true + }, + "verror": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", + "integrity": "sha1-z/XfEpRtKX0rqu+qJoniW+AcAFw=", + "dev": true + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/package.json b/package.json index 1e1830e..064e7f8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wget-improved", - "version": "1.5.0", + "version": "3.0.0", "description": "wget in nodejs, forked from wuchengwei/node-wget to add improvements and help maintain the project", "keywords": [ "download", @@ -10,7 +10,7 @@ "proxy", "wget" ], - "author": "Michael Barajas ", + "author": "Bearjaws ", "repository": { "type": "git", "url": "git://github.com/bearjaws/node-wget.git" @@ -34,6 +34,11 @@ "license": "MIT", "devDependencies": { "chai": "^4.0.2", - "mocha": "^3.4.2" + "express": "^4.15.3", + "mocha": "^3.4.2", + "request": "^2.81.0" + }, + "scripts": { + "test": "mocha test/test.js" } } diff --git a/test/helpers.js b/test/helpers.js new file mode 100644 index 0000000..e69de29 diff --git a/test/server.js b/test/server.js new file mode 100644 index 0000000..8c14f6a --- /dev/null +++ b/test/server.js @@ -0,0 +1,86 @@ +const crypto = require('crypto'); +const express = require('express'); +const http = require('http'); + +const app = express(); +let size = 1024 * 1024; +let file = new Buffer(size); + +let redirectFile = new Buffer(size); +crypto.randomBytes(size, function(err, buffer) { + file = buffer; +}); +app.get('/file', function(req, res) { + res.send(file); +}); + +app.get('/file/metadata', function(req, res) { + let hash = crypto + .createHash('sha256') + .update(file) + .digest('hex'); + res.json({ + size: size, + hash: hash + }); +}); + +app.get('/file/redirect', function(req, res) { + let count = 1; + if (typeof req.query.count === 'string') { + count = Number(req.query.count); + } + if (count > 3) { + res.send(redirectFile); + } else { + res.redirect( + 'http://localhost:8884/file/redirect?count=' + String(count + 1) + ); + } +}); + +app.get('/file/redirect/infinite', function(req, res) { + let count = 1; + if (typeof req.query.count === 'string') { + count = Number(req.query.count); + } + res.redirect( + 'http://localhost:8884/file/redirect/infinite?count=' + + String(count + 1) + ); +}); + +app.get('/file/redirect/relative', function(req, res) { + res.set('Location', '/file/redirect?count=4'); + res.status(301).end(); +}); + +app.get('/file/redirect/metadata', function(req, res) { + let hash = crypto + .createHash('sha256') + .update(redirectFile) + .digest('hex'); + res.json({ + size: size, + hash: hash + }); +}); + +module.exports = function() { + return new Promise(function(resolve, reject) { + app.listen(8884, function() { + // This server is used due to the inability to not send content-length headers in express. + http + .createServer(function(request, response) { + response.writeHead(200, { + 'Content-Type': 'binary' + }); + response.write(new Buffer(858478).toString('hex')); + response.end(); + }) + .listen(9933); + console.log('listening'); + resolve(); + }); + }); +}; diff --git a/test/test.js b/test/test.js index 13136da..44905c5 100644 --- a/test/test.js +++ b/test/test.js @@ -1,30 +1,180 @@ -let wget = require('../lib/wget'); +let crypto = require('crypto'); +let fs = require('fs'); let expect = require('chai').expect; +let request = require('request'); -describe("Download Tests", function() { - // with a proxy: - it("Should be able to download the NPM logo", function(done) { - let download = wget.download('https://www.npmjs.com/static/images/npm-logo.svg', '/tmp/npm-logo.svg'); - // @todo upgrade these tests to use a more consistent environment, with its own http server and files. +let wget = require('../lib/wget'); +let baseHTTP = 'http://localhost:8884'; +let metadata = {}; +before(function() { + let server = require('./server'); + return server().then(function() { + request(baseHTTP + '/file/metadata', function(err, res, body) { + metadata = JSON.parse(body); + }); + }); +}); + +describe('Download Tests', function() { + it('Should be able to download a file', function(done) { + let download = wget.download( + 'http://localhost:8884/file', + '/tmp/wget-test-file.bin' + ); + let bytes = 0; download.on('error', function(err) { - console.log(err); - expect(err).to.be.null; + done(err); + }); + download.on('start', function(fileSize) { + expect(fileSize).to.be.a('number'); + expect(fileSize).to.equal(metadata.size); + }); + download.on('end', function(output) { + let file = fs.readFileSync('/tmp/wget-test-file.bin'); + let hash = crypto + .createHash('sha256') + .update(file) + .digest('hex'); + expect(output).to.equal('Finished writing to disk'); + expect(hash).to.equal(metadata.hash); + expect(bytes).to.equal(1024 * 1024); done(); }); + download.on('bytes', function(input) { + expect(input).to.be.above(0); + bytes = input; + }); + download.on('progress', function(progress) { + expect(progress).to.be.above(0); + expect(progress).to.be.below(1.00000000000001); + }); + }); + + it('Should handle a server that does not have content-length header', function(done) { + let download = wget.download( + 'http://localhost:9933/', + '/tmp/wget-bs-test.bin' + ); + download.on('error', function(err) { + done(err); + }); download.on('start', function(fileSize) { - expect(fileSize).to.be.a('string'); - fileSize = Number(fileSize); - expect(fileSize).to.be.above(200); - expect(fileSize).to.be.below(500); + expect(fileSize).to.be.null; + }); + download.on('progress', function(progress) { + expect(progress).to.be.above(0); + expect(progress).to.be.below(1.00000000000001); + }); + + download.on('end', function() { + done(); + }); + }); + + it('Should not append to the previous file.', function(done) { + let download = wget.download( + 'http://localhost:8884/file', + '/tmp/wget-test-file.bin' + ); + download.on('error', function(err) { + console.log(err); + expect(err).to.be.null; + done(); }); download.on('end', function(output) { + let file = fs.readFileSync('/tmp/wget-test-file.bin'); + let hash = crypto + .createHash('sha256') + .update(file) + .digest('hex'); expect(output).to.equal('Finished writing to disk'); + expect(hash).to.equal(metadata.hash); done(); - process.exit(); }); + }); + + it('Should handle 302 redirects that end with file download.', function(done) { + let download = wget.download( + 'http://localhost:8884/file/redirect', + '/tmp/wget-test-file2.bin' + ); + download.on('end', function(output) { + request(baseHTTP + '/file/redirect/metadata', function( + err, + res, + body + ) { + let meta = JSON.parse(body); + let file = fs.readFileSync('/tmp/wget-test-file2.bin'); + let hash = crypto + .createHash('sha256') + .update(file) + .digest('hex'); + expect(output).to.equal('Finished writing to disk'); + expect(hash).to.equal(meta.hash); + done(); + }); + }); + }); + + it('Should handle infinite redirects', function(done) { + let download = wget.download( + 'http://localhost:8884/file/redirect/infinite', + '/tmp/wget-test-file2.bin' + ); + + download.on('error', function(err) { + expect(err).to.equal('Infinite redirect loop detected'); + done(); + }); + }); + + it('Should handle relative path redirect', function(done) { + let download = wget.download( + 'http://localhost:8884/file/redirect/relative', + '/tmp/wget-test-file3.bin' + ); + download.on('error', function(err) { + done(err); + }); + download.on('start', function(fileSize) { + expect(fileSize).to.be.a('number'); + expect(fileSize).to.equal(metadata.size); + }); + download.on('end', function(output) { + request(baseHTTP + '/file/redirect/metadata', function( + err, + res, + body + ) { + let meta = JSON.parse(body); + let file = fs.readFileSync('/tmp/wget-test-file2.bin'); + let hash = crypto + .createHash('sha256') + .update(file) + .digest('hex'); + expect(output).to.equal('Finished writing to disk'); + expect(hash).to.equal(meta.hash); + done(); + }); + }); + download.on('progress', function(progress) { expect(progress).to.be.above(0); + expect(progress).to.be.below(1.00000000000001); }); }); -}); \ No newline at end of file + + it('Should handle invalid protocol (no http/https)', function(done) { + try { + let download = wget.download( + 'localhost:8884/file/redirect/infinite', + '/tmp/wget-test-file2.bin' + ); + } catch (err) { + expect(err).to.equal('Your URL must use either HTTP or HTTPS.'); + done(); + } + }); +});