diff --git a/index.js b/index.js index 352f587..75c53ee 100644 --- a/index.js +++ b/index.js @@ -2,6 +2,7 @@ var http = require('http'); var https = require('https'); var urlLib = require('url'); +var util = require('util'); var zlib = require('zlib'); var objectAssign = require('object-assign'); var agent = require('infinity-agent'); @@ -12,6 +13,15 @@ var timeout = require('timed-out'); var prependHttp = require('prepend-http'); var lowercaseKeys = require('lowercase-keys'); var status = require('statuses'); +var NestedError = require('nested-error-stacks'); + +function GotError(message, nested) { + NestedError.call(this, message, nested); + objectAssign(this, nested); +} + +util.inherits(GotError, NestedError); +GotError.prototype.name = 'GotError'; function got(url, opts, cb) { if (typeof opts === 'function') { @@ -74,7 +84,7 @@ function got(url, opts, cb) { res.resume(); // Discard response if (++redirectCount > 10) { - cb(new Error('Redirected 10 times. Aborting.'), undefined, res); + cb(new GotError('Redirected 10 times. Aborting.'), undefined, res); return; } @@ -89,8 +99,8 @@ function got(url, opts, cb) { } if (statusCode < 200 || statusCode > 299) { - read(res, encoding, function (error, data) { - var err = error || new Error(url + ' response code is ' + statusCode + ' (' + status[statusCode] + ')'); + read(res, encoding, function (err, data) { + err = new GotError(url + ' response code is ' + statusCode + ' (' + status[statusCode] + ')', err); err.code = statusCode; cb(err, data, response); }); @@ -104,9 +114,15 @@ function got(url, opts, cb) { } read(res, encoding, function (err, data) { + if (err) { + err = new GotError('Reading ' + url + ' response failed', err); + } + cb(err, data, response); }); - }).once('error', cb); + }).once('error', function (err) { + cb(new GotError('Request to ' + url + ' failed', err)); + }); if (opts.timeout) { timeout(req, opts.timeout); diff --git a/package.json b/package.json index 7945527..c4b2b13 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "infinity-agent": "^1.0.0", "is-stream": "^1.0.0", "lowercase-keys": "^1.0.0", + "nested-error-stacks": "^1.0.0", "object-assign": "^2.0.0", "prepend-http": "^1.0.0", "read-all-stream": "^2.0.0", diff --git a/test/test-error.js b/test/test-error.js index 2a62620..d0442be 100644 --- a/test/test-error.js +++ b/test/test-error.js @@ -23,6 +23,14 @@ tape('error message', function (t) { }); }); +tape('dns error message', function (t) { + got('.com', function (err) { + t.ok(err); + t.equal(err.message, 'Request to .com failed'); + t.end(); + }); +}); + tape('cleanup', function (t) { s.close(); t.end(); diff --git a/test/test-gzip.js b/test/test-gzip.js index 9b5e3d7..9bbe7d0 100644 --- a/test/test-gzip.js +++ b/test/test-gzip.js @@ -9,15 +9,17 @@ var testContent = 'Compressible response content.\n'; s.on('/', function (req, res) { res.statusCode = 200; res.setHeader('Content-Type', 'text/plain'); + res.setHeader('Content-Encoding', 'gzip'); + zlib.gzip(testContent, function (err, data) { + res.end(data); + }); +}); - if (/\bgzip\b/i.test(req.headers['accept-encoding'])) { - res.setHeader('Content-Encoding', 'gzip'); - zlib.gzip(testContent, function (err, data) { - res.end(data); - }); - } else { - res.end(testContent); - } +s.on('/corrupted', function (req, res) { + res.statusCode = 200; + res.setHeader('Content-Type', 'text/plain'); + res.setHeader('Content-Encoding', 'gzip'); + res.end('Not gzipped content'); }); tape('setup', function (t) { @@ -34,6 +36,14 @@ tape('ungzip content', function (t) { }); }); +tape('ungzip error', function (t) { + got(s.url + '/corrupted', function (err) { + t.ok(err); + t.equal(err.message, 'Reading ' + s.url + '/corrupted response failed'); + t.end(); + }); +}); + tape('cleanup', function (t) { s.close(); t.end();