good-guy-http
Advanced tools
Comparing version 1.6.0 to 1.7.0
@@ -20,3 +20,3 @@ var _ = require('underscore'); | ||
method = req.method, | ||
accept = req.headers['accept'], | ||
accept = req.headers && req.headers['accept'], | ||
qs = req.qs; | ||
@@ -23,0 +23,0 @@ |
@@ -1,2 +0,1 @@ | ||
var requestLib = require('request'); | ||
var _ = require('underscore'); | ||
@@ -25,2 +24,3 @@ | ||
cacheResponseTimeout: 500, // if a cache fails to respond in 500ms, it will be ignored | ||
maxResponseSize: 1024*1024, // responses are limited to a maximum of 1MB by default | ||
errorLogger: console.error, // error logs will go to stderr | ||
@@ -48,2 +48,5 @@ | ||
// if needed, you can override the request lib used - just pass it in | ||
requestLib: require('request'), | ||
// settings for test purposes | ||
@@ -59,2 +62,3 @@ mockTimer: false, | ||
'maxRetries', | ||
'maxResponseSize', | ||
'collapseIdenticalRequests', | ||
@@ -71,2 +75,3 @@ 'allowServingStale', | ||
'usePromise', | ||
'requestLib', | ||
@@ -109,3 +114,3 @@ 'mockTimer' | ||
// set all the components up | ||
var request = requestLib.defaults(reqConfig); | ||
var request = config.requestLib.defaults(reqConfig); | ||
@@ -120,3 +125,3 @@ // cache retrieval should be gated with a timeout to prevent broken caches from braking the whole request | ||
// we obviously aren't allowed to retry POST's and other non-idempotent requests | ||
var fetch = require('./promised-request')(request, Promise); | ||
var fetch = require('./promised-request')(request, Promise, _.pick(config, ['maxResponseSize'])); | ||
var fetchIdempotent = fetch, fetchNonIdempotent = fetch; | ||
@@ -123,0 +128,0 @@ if (config.maxRetries) |
@@ -0,7 +1,13 @@ | ||
var util = require('util'); | ||
var Response = require('./response'); | ||
var HttpError = require('./http-error'); | ||
var requestKey = require('./caching/request-key'); | ||
var canonicalizeRequest = require('./canonicalize-request'); | ||
module.exports = promisedRequests; | ||
function promisedRequests(request, Promise) { | ||
function promisedRequests(request, Promise, config) { | ||
config = config || {}; | ||
var maxResponseSize = config.maxResponseSize || false; | ||
return makePromisedRequest; | ||
@@ -19,6 +25,9 @@ | ||
return new Promise(function(resolve, reject) { | ||
request(requestOptions, function(err, response) { | ||
var httpReq = request(requestOptions, function(err, response) { | ||
// hard errors cause a promise rejection | ||
if (err) { | ||
err.request = requestOptions; // augment with request for easy debugging | ||
if (typeof err == 'object') { | ||
// add request-specific information to the error for easier debugging | ||
err = augmentError(err, requestOptions); | ||
} | ||
return reject(err); | ||
@@ -37,4 +46,58 @@ } | ||
}); | ||
// we want to bail early if a response goes over a chosen size | ||
if (maxResponseSize) { | ||
httpReq.on('data', abortIfResponseAbove(requestOptions, httpReq, maxResponseSize, reject)); | ||
} | ||
}); | ||
} | ||
/** | ||
* Creates a 'data' event handler that will abort the request whenever | ||
* the server's response goes over a specified size limit. | ||
*/ | ||
function abortIfResponseAbove(requestOptions, httpRequest, maximumSize, rejectionCallback) { | ||
var sizeSoFar = 0; | ||
return function(chunk) { | ||
sizeSoFar += chunk.length; | ||
if (sizeSoFar > maximumSize) { | ||
// abort the actual Node request | ||
httpRequest.abort(); | ||
// reject the promise with an appropriate error | ||
var err = new ResponseSizeExceededError("Response exceeded the maximum size of " + maximumSize + " bytes."); | ||
err = augmentError(err, requestOptions); | ||
rejectionCallback(err); | ||
} | ||
}; | ||
} | ||
/** | ||
* Adds request-specific information to the error to make it easier to debug problems. | ||
*/ | ||
function augmentError(err, request) { | ||
request = canonicalizeRequest(request); | ||
// store for later reference by clients | ||
err.request = request; | ||
// add basic information about method/URL to the message | ||
var requestDescription = requestKey(request); | ||
var requestPrefix = "[While requesting " + requestDescription + "]: "; | ||
if (err.message) { | ||
err.message = requestPrefix + err.message; | ||
} | ||
if (err.stack) { | ||
err.stack = requestPrefix + err.stack; | ||
} | ||
return err; | ||
} | ||
} | ||
var ResponseSizeExceededError = module.exports.CircuitBrokenError = function(message) { | ||
Error.call(this); | ||
this.message = message; | ||
this.code = 'ERESPONSETOOBIG'; | ||
}; | ||
util.inherits(ResponseSizeExceededError, Error); |
{ | ||
"name": "good-guy-http", | ||
"version": "1.6.0", | ||
"version": "1.7.0", | ||
"description": "The opinionated sane HTTP client with a good guy approach.", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
@@ -47,2 +47,4 @@ # good-guy-http | ||
// useful for remote caches (e.g. Redis) | ||
maximumResponseSize: 1024*1024 // any responses above this size will be rejected to prevent memory trouble, | ||
// the default is 1MB | ||
errorLogger: console.error, // error logging function - a failing cache doesn't break requests, but logs here | ||
@@ -49,0 +51,0 @@ // instead |
@@ -77,2 +77,9 @@ var Promise = require('bluebird'); | ||
// responds with a body of the chosen size | ||
app.get('/response-size/:bytes', function(req, res) { | ||
var size = parseInt(req.params.bytes); | ||
res.set({'content-type': 'application/octet-stream'}); | ||
res.status(200).send(new Buffer(size)).end(); | ||
}); | ||
// for each ID - fails 2 times with 500, then returns "Ok!" | ||
@@ -79,0 +86,0 @@ var fttsCounts = {}; |
@@ -73,6 +73,22 @@ var request = require('request'); | ||
expectRejection(req('http://127.0.0.1:1')).then(function(err) { | ||
assert(err.request); | ||
assert.equal(err.request, 'http://127.0.0.1:1'); | ||
assert.ok(err.request); | ||
assert.equal(err.request.url, 'http://127.0.0.1:1'); | ||
}).then(done).catch(done); | ||
}); | ||
it("should add request info to error messages", function(done) { | ||
expectRejection(req('http://127.0.0.1:1?hi=there')).then(function(err) { | ||
assert.ok(err.message.indexOf('[While requesting GET|http://127.0.0.1:1?hi=there]') >= 0); | ||
assert.ok(err.stack.indexOf('[While requesting GET|http://127.0.0.1:1?hi=there]') >= 0); | ||
assert.ok(err.toString().indexOf('[While requesting GET|http://127.0.0.1:1?hi=there]') >= 0); | ||
}).then(done).catch(done); | ||
}); | ||
it("should respect the maximum response size", function(done) { | ||
var req = require('../../lib/promised-request')(request.defaults({timeout: 500}), Promise, {maxResponseSize: 2048}); | ||
expectRejection(req(app.url('/response-size/4096'))).then(function(err) { | ||
assert.equal(err.code, 'ERESPONSETOOBIG'); | ||
}).then(done).catch(done); | ||
}); | ||
}); | ||
@@ -79,0 +95,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
88984
47
2084
149